Состав функции JavaScript путем цепочки
Я проверил возможность дублирования вопроса,
и не может найти точное решение.
Я написал код функциональной цепочки в JavaScript, как показано ниже, и отлично работает.
var log = function(args)
{
console.log(args)
return function(f)
{
return f;
};
};
(log('1'))(log('2'))(log('3'))(log('4'));
//1
//2
//3
//4
Я хочу сделать эту ленивую оценку.
Или для создания функции.
var log = function(args)
{
var f0 = function()
{
return console.log(args);
};
return function(f1)
{
return function()
{
f0();
return f1;
};
};
};
var world = (log('1'))(log('2'))(log('3'))(log('4'));
console.log(world);
//should be just a function,
// but in fact
//1
//[function]
world();
//should be
//1
//2
//3
//4
// but in fact
// 2
Что-то очень не так.
Вы можете исправить это?
Спасибо.
Этот вопрос разрешен, но есть еще
проблема async, как показано в обсуждении комментариев
Когда мы имеем
// unit :: a -> IO a
var unit = function(x)
{
return function()
{
return x;
};
};
// bind :: IO a -> (a -> IO b) -> IO b
var bind = function(x, y)
{
return function()
{
return y(x())();
};
};
// seq :: IO a -> IO b -> IO b
var seq = function(x, y)
{
return function()
{
return x(), y();
};
};
var action = function(x)
{
return function(y)
{
return y ? action(seq(x, y)) : x();
};
};
var wrap = function(f)
{
return function(x)
{
return action(function()
{
return f(x);
});
};
};
var log = wrap(console.log);
// -- runtime --
// HACK: when `world` is modified by passing a function,
// the function will be executed.
Object.defineProperties(window,
{
world:
{
set: function(w)
{
return w();
}
}
});
Мы также часто нуждаемся в асинхронных цепных реакциях.
var asyncF = function(callback)
{
setTimeout(function()
{
for (var i = 0; i < 1000000000; i++)
{
};
callback("async process Done!");
}, 0);
};
var async = wrap(asyncF(function(msg)
{
world = log(msg);
return msg;
}));
Теперь,
world = (log(1))(async)(log(3));
//1
//3
//async process Done!
До сих пор приятно и плавно, теперь мы пытаемся использовать bind
world = (log(1))
(bind((async), (log(x))));
//should be
//1
//async process Done!
//3
//in fact
//ReferenceError: x is not defined
Не могли бы вы изменить эту работу, пожалуйста?
еще один < <26 > несколько значений
Я не понимаю
// seq :: IO a -> IO b -> IO b
var seq = function(x, y)
{
return function()
{
return x(), y();
};
};
как автор библиотеки упоминает
Обратите внимание, что это невозможно в Haskell, потому что одна функция не может вернуть два результата. Кроме того, по моему скромному мнению, это выглядит уродливо.
Я согласен и не знаю, что это за
return x(), y();
многократное возвращаемое значение.
Я искал Google и искал здесь, но не смог найти ответ.
Что это такое?
(на всякий случай, я выберу этот хак для синтаксиса)
Спасибо!
Ответы
Ответ 1
Итак, если я правильно понял вопрос, вы хотите связать действия IO в JavaScript. Для этого вам сначала нужно определить, что такое операция ввода-вывода. Один из способов думать о IO-действии состоит в том, что это просто функция, которая не принимает аргументов. Например:
// log :: a -> IO b
function log(x) {
return function () { // IO action
return console.log(x);
};
}
Одним из преимуществ представления IO-действия как функции без аргументов является то, что это то же представление для thunks (неоценимые выражения). Thanks - это то, что позволяет ленивую оценку на таких языках, как Haskell. Следовательно, вы получаете ленту бесплатно.
Теперь состав. Как вы создаете два действия ввода-вывода в JavaScript? В Haskell вы используете оператор >>
для последовательности операций ввода-вывода, который обычно определяется в терминах >>=
(a.k.a. bind
) следующим образом:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
(>>) :: Monad m => m a -> m b -> m b
x >> y = x >>= \_ -> y
Легко написать эквивалентную функцию bind
для наших действий IO в JavaScript:
// bind :: IO a -> (a -> IO b) -> IO b
function bind(x, y) {
return function () {
return y(x())();
};
}
Предположим, что у вас есть действие IO x :: IO a
. Поскольку это просто функция без аргументов, когда вы ее называете, она эквивалентна оценке действия ввода-вывода. Следовательно, x() :: a
. Подача этого результата на функцию y :: a -> IO b
приводит к действию ввода-вывода y(x()) :: IO b
. Обратите внимание, что вся операция завершена в излишнюю функцию для лень.
Аналогично, так же просто реализовать оператор >>
. Позвольте называть его seq
как в "последовательности".
// seq :: IO a -> IO b -> IO b
function seq(x, y) {
return function () {
return x(), y();
};
}
Здесь мы оцениваем выражение IO x
, не заботимся о его результате, а затем возвращаем выражение IO y
. Это именно то, что делает оператор >>
в Haskell. Обратите внимание, что вся операция завершена в излишнюю функцию для лень.
Haskell также имеет функцию return
, которая возвращает значение в монадический контекст. Поскольку return
является ключевым словом в JavaScript, мы будем называть его unit
вместо:
// unit :: a -> IO a
function unit(x) {
return function () {
return x;
};
}
Как оказалось, в Haskell также есть оператор sequence
, который последовательно выполняет монадические значения в списке. Он может быть реализован в JavaScript для операций ввода-вывода следующим образом:
// sequence :: [IO a] -> IO [a]
function sequence(array) {
return function () {
var list = array;
var length = list.length;
var result = new Array(length);
var index = 0;
while (index < length)
result[index] = list[index++]();
return result;
};
}
Это все, что нам нужно. Теперь мы можем написать:
var world = sequence([log("1"), log("2"), log("3"), log("4")]);
world();
// 1
// 2
// 3
// 4
Надеюсь, что это поможет.
Да, действительно возможно связать действия IO с помощью вашего синтаксиса. Однако нам нужно переопределить, что такое действие ввода-вывода:
function action(x) {
return function (y) {
return y ? action(seq(x, y)) : x();
};
}
Понятно, что делает функция action
с помощью примера:
// log :: a -> IO b
// log :: a -> IO r -> IO r
function log(x) {
return action(function () {
return console.log(x);
});
}
Теперь вы можете сделать:
log("1")(); // :: b
log("1")(log("2")); // :: IO r
В первом случае мы оценили действие IO log("1")
. Во втором случае мы упорядочили действия IO log("1")
и log("2")
.
Это позволяет:
var world = (log("1"))(log("2"))(log("3"))(log("4"));
world();
// 1
// 2
// 3
// 4
Кроме того, вы также можете:
var newWorld = (world)(log("5"));
newWorld();
// 1
// 2
// 3
// 4
// 5
И так далее....
Все остальное остается прежним. Обратите внимание, что это невозможно в Haskell, потому что одна функция не может вернуть два результата. Кроме того, по моему скромному мнению, это выглядит уродливо. Вместо этого я предпочитаю использовать sequence
. Однако это то, что вы хотели.
Ответ 2
Посмотрим, что здесь происходит:
var log = function(args)
{
var f0 = function()
{
return console.log(args);
};
return function(f1)
{
return function()
{
f0();
return f1;
};
};
};
И в строке немного:
var log = function(args) {
return function(f1) {
return function() {
console.log(args);
return f1;
};
};
};
Итак, мы возвращаем функцию f
, которая принимает функцию f1
, и возвращает функцию g
, которая выполняет логику и возвращает f1
. Довольно глоток! Ваш вопрос в том, почему
(log('1'))(log('2'))(log('3'));
Журнал 1
. Я покончил с log('4')
, так как для перехода к 3 достаточно показать ваш описанный случай. Ответьте, что разрешите играть в компилятор и сделайте игровую игру!
(log('1'))(log('2'))(log('3'))
// =>
(
function (f1) {
return function () {
console.log('1');
return f1;
}
}
)(
function (f1) {
return function () {
console.log('2');
return f1;
}
}
)(
function (f1) {
return function () {
console.log('3');
return f1;
}
}
)
Простая подстановка. Я взял каждый экземпляр log(something)
, заменил его содержимым функции, заменил аргумент на переданное значение. Позвольте сделать это снова!
(
function () {
console.log('1');
return function (f1) {
return function () {
console.log('2');
return f1;
}
};
}
)(
function (f1) {
return function () {
console.log('3');
return f1;
}
}
)
Это немного сложнее: я расширил первый вызов функции. Самая верхняя функция получила аргумент f1
, на который мы только что указали значение, поэтому я вошел в функцию и заменил каждое вхождение f1
на заданное значение (результат log('2')
), точно так же, как с log
.
Посмотрите, что здесь произошло, если вы все еще не следуете, но мое предложение делает это самостоятельно: скопируйте фрагмент в свой любимый редактор кода и сделайте расширение самостоятельно.
Теперь вы можете понять, почему был вызван log('1')
. Следующее, что нам, компилятор, нужно сделать, это позаботиться о следующем вызове функции. И, как известно, первая строка в этой функции - это console.log
! Лучше сделайте это!
Что мы можем сделать??
Я не знаю Haskell или IO Monad, но, как вы сейчас планировали, я не думаю, что вы можете делать то, что хотите, с помощью основных функций, а не так. Если вы можете сказать, какую проблему вы хотите решить, используя этот... erm... шаблон, возможно, мы сможем помочь!
Ответ 3
Это потому, что вы просто возвращаетесь и возвращаете все...
На выходе выводятся три вещи:
1
function ()
{
f0();
return f1;
}
2
1) первый вывод: 1
это потому, что: console.log(args)
выполняется только один раз в вашей цепочке, так как f0 выполняется только один раз в последний раз, когда он находит args
равным 1 (из-за возврата каждой вложенной функции какое значение вы возвращаете на last - это функция f1, которая выполняет f0(), когда значение args равно 1.
затем он печатает 1 на консоли.
2) второй вывод функции f1
the return f1;
(который возвращается функции, когда вы передавали args как 1), выполненный при последних возвратах
function ()
{
f0();
return f1;
}
вернуться в мир переменных, поэтому на консоль печатается только внутренняя вложенная функция.
3) Третий выход: 2
то при выполнении функции world()
,
Опять функция f1 выполняется непосредственно (см. только небольшое различие между world
и world()
), но на этот раз возвращенная функция, когда вы передали args
как 2
.
Причина: мир выведет только функцию, world()
выполнит эту функцию.
Когда вы пишете world()
, в последний раз, когда возвращается function f1
, значение для args равно 2. Выполняется непосредственно.
Я знаю, что я ужасно сформулировал ответ... но надеюсь, что это поможет (надеюсь, вы поймете)
Ответ 4
При выполнении
var world = (log('1'))(log('2'))(log('3'))(log('4'));
(log ('1')) выполняется первым, который возвращает функцию, которая принимает (log ('2')).
Эта анонимная функция запускается, но не принимает никаких аргументов.
log ('3') пренебрегают. Это можно проверить с помощью
if(typeof(arguments[0]) == 'function'){
console.log("Got a neglected argument");
console.log(arguments[0]());
}
После выполнения f0();
(который печатает 1 на экран), мы возвращаем f1, который указывает на функцию, возвращаемую log ('2'), это принимает log ('4');
Это можно проверить, выполнив:
world()()()
эти выходы:
2
4
undefined