[英]JavaScript function composition by chaining
我检查了重复问题的可能性,但找不到确切的解决方案。
我在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
有些事情是非常错误的。 你能修好它吗?
谢谢。
当我们有
// 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!
到目前为止,顺利,现在我们尝试使用绑定
world = (log(1))
(bind((async), (log(x))));
//should be
//1
//async process Done!
//3
//in fact
//ReferenceError: x is not defined
你可以修改一下吗?
retrun x, y;
多重价值 我不明白
// seq :: IO a -> IO b -> IO b
var seq = function(x, y)
{
return function()
{
return x(), y();
};
};
正如图书馆作者所提到的那样
请注意,这在Haskell中是不可能的,因为一个函数不能返回两个结果。 而且,在我的拙见中,它看起来很难看。
我同意,不知道这是什么
return x(), y();
多重回报值。
我在谷歌搜索并搜索,但找不到答案。
这是什么??
(以防万一,我会选择这个hack语法)
谢谢!
因此,如果我正确理解了这个问题,您希望在JavaScript中链接IO操作。 为此,首先需要定义IO操作的内容。 想到IO动作的一种方法是它只是一个不带参数的函数。 例如:
// log :: a -> IO b
function log(x) {
return function () { // IO action
return console.log(x);
};
}
将IO操作表示为不带参数的函数的一个优点是它与thunk ( 未评估的表达式 )的表示相同。 Thunk是能够在像Haskell这样的语言中进行延迟评估的东西。 因此,你可以免费得到懒惰。
现在组成。 你如何在JavaScript中组成两个IO动作? 在Haskell中,使用>>
运算符对IO操作进行排序,IO操作通常按照>>=
(aka bind
)定义,如下所示:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
(>>) :: Monad m => m a -> m b -> m b
x >> y = x >>= \_ -> y
在JavaScript中为IO动作编写等效的bind
函数很容易:
// bind :: IO a -> (a -> IO b) -> IO b
function bind(x, y) {
return function () {
return y(x())();
};
}
假设你有一个IO动作x :: IO a
。 因为它只是一个没有参数的函数,所以当你调用它时它等同于评估IO动作。 因此x() :: a
。 将此结果提供给函数y :: a -> IO b
导致IO操作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
函数,它将值提升为monadic上下文。 由于return
是JavaScript中的关键字,我们将其称为unit
:
// unit :: a -> IO a
function unit(x) {
return function () {
return x;
};
}
事实证明,Haskell中还有一个sequence
运算符,它对列表中的monadic值进行排序。 它可以在JavaScript中实现,用于IO操作,如下所示:
// 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操作。 但是,我们需要重新定义IO操作的内容:
function action(x) {
return function (y) {
return y ? action(seq(x, y)) : x();
};
}
让我们通过一个例子来理解action
函数的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
。 但是,这就是你想要的。
让我们来看看这里发生了什么:
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
,我们刚刚提供了一个值,所以我进入函数并用给定的值( log('2')
的结果)替换每次出现的f1
,就像使用log
参数一样。
如果你仍然不遵循,请再次查看此处发生的事情,但我的建议是自己动手:将代码段复制到您喜欢的代码编辑器中并自行进行扩展。
现在您可能会看到为什么调用log('1')
。 我们编译器需要做的下一件事就是处理下一个函数调用。 而whadya知道,该函数的第一行是console.log
! 做得更好!
我不知道Haskell或IO Monad,但按照你目前的计划,我认为你不能用基本功能做你想做的事情,而不是那样。 如果你能用这个...呃......模式说出你想解决什么问题,也许我们可以提供帮助!
这是因为你只是回来并归还一切......
输出中打印了三件事:
1
function ()
{
f0();
return f1;
}
2
1)第一输出:1
这是因为: console.log(args)
只在你的链接中执行一次,因为f0只在最后找到args
为1时执行一次(因为返回每个嵌套函数,你在最后返回的值是函数f1,当args的值为1时执行f0(),然后将1打印到控制台。
2)第二次输出函数f1
return f1;
(在你将args传递为1时返回给函数)在最后一次返回时执行
function ()
{
f0();
return f1;
}
回到变量世界,因此只有内部嵌套函数被打印到控制台。
3)第三输出:2
然后当你执行函数world()
,
函数f1再次直接执行(请参阅world
和world()
之间的一点点差异)但这次返回的函数是将args
传递给2
时的函数。
原因:世界将仅输出函数, world()
将执行该函数。
当你编写world()
,在返回function f1
的最后一次,args的值是2,直接执行。
我知道我的答案非常措辞..但希望这会有所帮助(希望你理解)
执行时
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打印到屏幕上),我们返回指向log('2')返回的函数的f1,这将记录log('4');
这可以通过执行以下操作来验证: world()()()
这个输出:
2
4
undefined
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.