我正在学习javascript.我遇到了这个问题并尝试使用javacript中的currying来解决它但是无法正确解决它.
给定一个函数pipe()
,它将几个函数作为参数并返回一个将其参数传递给第一个函数的新函数,然后将结果传递给第二个函数,然后传递给第三个函数,依此类推,返回最后一个函数的输出.所以给出:pipe(foo, bar, baz)(1, 2, 3)
例如,相当于baz(bar(foo(1,2,3)))
.
我将如何在javascript中解决这个问题?
功能性编程很有趣,所以我会在这个问题上采取行动.通常,这个概念称为函数组合,它最好用于一元函数(只接受一个参数的函数).
在最基本的层面上,功能组合看起来像这样
const comp = f => g => x => f (g (x))
并组成一个列表的功能,是一个折叠使用该名单的补偿开始与ID功能.
const id = x => x; const foldl = f => y => xs => xs.length === 0 ? y : foldl (f) (f (y) (xs[0])) (xs.slice(1)); const compN = fs => foldl (comp) (id) (fs);
我们可以使用这个工作
let foo = x => x - 1; let bar = x => x * 20; let baz = x => x + 3; compN ([foo, bar, baz]) (2); //=> 99
那怎么工作?
compN ([foo, bar, baz]) // returns: x => foo(bar(baz(x)))
因此,当我们调用(2)
返回值时,2
将传入x
,然后整个链执行
foo(bar(baz(2))) foo(bar(5)) foo(100) //=> 99
由于这种实现的结果,你compN
有3等可重复使用的功能,id
,comp
,和foldl
但是,你的问题写得有些神奇.您的标准表明函数组合应该在条目中使用多个参数,因此通过引入丑陋的异常将整个概念混为一谈.无论如何,这是一种你可以写它的方式
const id = x => x; const foldl = f => y => xs => xs.length === 0 ? y : foldl (f) (f (y) (xs[0])) (xs.slice(1)); const badComp = f => g => (...xs) => f(g(...xs)); const pipe = fs => foldl (badComp) (id) (fs); pipe ([x => x * x, (x,y) => x + y]) (2,3); //=> 25 //=> (2+3) * (2+3) //=> 5 * 5 //=> 25
如您所见,这里的实用性甚至没有大大改善.如果要向函数发送多个参数,则不会将它们作为数组发送.所以不要foo(1,2)
使用foo([1,2])
.这样你可以使用我的答案的第一部分中描述的更受欢迎的一元函数组合.然后你不必依赖牙痛糖,如休息参数(...xs) =>
和传播操作员f(g(...xs))
.
此外,ES6为您提供了解构分配,因此您可以轻松使用一元函数组合方法,并仍然可以编写看似多个参数的方法.该pipe
例子可以与重写compN
使用
compN ([x => x * x, ([x,y]) => x + y]) ([2,3]);
所以,是的,我的意见显然支持经典的一元功能组合,但你可以使用任何一个让你开心或完成你的家庭作业.
无论如何,这是我自己的2美分.如果您有任何疑问,我很乐意为您提供帮助.
我不想用最初的实现压倒你,compN
所以我保持foldl
功能简单.如果我要把它拿出来,我实际上会更进一步.
const id = x => x; const comp = f => g => x => f (g (x)); const eq = x => y => y === x; const prop = x => y => y[x]; const len = prop ('length'); const isEmpty = comp (eq (0)) (len); const first = xs => xs[0]; const rest = xs => (xs) .slice (1); const foldl = f => y => xs => isEmpty (xs) ? y : foldl (f) (f (y) (first (xs))) (rest (xs)); const compN = fs => foldl (comp) (id) (fs);
当然它也是一样的
compN ([x => x - 1, x => x * 20, x => x + 3]) (2) //=> 99
作为这一结果compN
的实现,我们得到9个可重复使用的完全功能免费.这是巨大的,imo.这意味着下次你必须定义另一个函数时,你很可能已经完成了这些其他函数所定义的大部分工作.
此外,我选择实现foldl
而不是依赖于本机,Array.prototype.reduce
因为它不仅向您展示如何通过递归实现迭代循环,还因为它期望一个curried的过程.
我相当于foldl
用原生Array.prototype.reduce
会
var uncurry = f => (x,y) => f (x) (y); var foldl2 = f => y => xs => xs.reduce(uncurry(f), y);
要么没事.再次,选择你喜欢的任何一个.如果你最终选择了一个Array.prototype.reduce
解决方案,至少你知道如何制作自己的解决方案^,^
相当激进的东西.好运,祝你好运.