如何调和Javascript与咖喱和功能组成 [英] How to reconcile Javascript with currying and function composition
问题描述
我喜欢currying,但是有一些原因促使一大堆JavaScript开发者拒绝这种技术:
- 审美关注典型curry pattern:
f(x)(y)(z)
- 关注由于函数调用次数增加导致的性能损失
- 由于存在多个嵌套的匿名函数而担心调试问题。
- 关注无点式样的可读性(与构图有关的曲调) >
有没有一种方法可以缓解这些担忧,以便我的同事不讨厌我?
注意: @ftor回答了他/她自己的问题。这是该答案的直接伴侣。
您已经是天才了
我想你可能已经重新想象了部分函数 - 至少部分是!
const partial =(f,... xs)=> (... ys)=> f(... xs,... ys);
,它是配对的, partialRight
const partialRight =(f,... xs)=> (... ys)=> f(... ys,... xs);
partial
需要一个函数,一些args xs
),并且 always 返回一个需要更多参数( ys
)的函数,然后将 f
应用于(... xs,... ys)
最初的评论
这个问题的背景是咖啡和咖啡的组合可以在编码员的大量用户基础上发挥出色。我的评论将会出现在同一个上下文中
-
仅仅因为一个函数可能返回一个函数并不意味着它是 curry - 在
_
上添加以表示一个函数正在等待更多的参数是令人困惑的。回想一下,currying(或部分函数应用程序)抽象arity,所以我们永远不知道函数调用何时会导致计算值或其他函数的值等待调用。 -
咖喱应不翻转参数;如果我们要为 reduce
创建一个封装器,那么这会对您的编码器造成一些严重的wtf时刻。 code> reduceRight wrapper应该是一致的 - 例如,你的
foldl
使用 f(acc,x,i)
但您的 foldr
使用 f(x,acc,i)
- 这会在不熟悉这些选择的同事之间造成很大的痛苦 下一节,我将用 partial
替换 composable
,删除 _
-suffixes,并修复
foldr
wrapper
< data-lang =jsdata-hide =false数据控制台可组合函数 =truedata-babel =false>
const partial =( f,... xs)=> (... ys)=> f(... xs,... ys); const partialRight =(f,... xs)=> (... ys)=> f(... ys,... xs); const comp =(f,g)=> x => f(g(x)); const foldl =(f,acc,xs)=> xs.reduce(f,acc); const drop =(xs,n)=> xs.slice(n); const add =(x,y)=> x + y; const sum = partial(foldl,add,0); const dropAndSum = comp(sum,partialRight(drop,1)); console.log(dropAndSum([1,2,3,4])// 9 );
编程解决方案
const partial =(f, ... xs)=> (... ys)=> f(... xs,... ys); //恢复一致的interfaceconst foldr =(f,acc,xs)=> xs.reduceRight(f,acc); const comp =(f,g)=> x => f(g(x)); //将其添加为laterconst flip = f => (x,y)=> f(y,x); const I = x => x; const inc = x => x + 1; const compn = partial(foldr,flip(comp),I); const inc3 = compn([inc,inc,inc]); console.log(inc3(0)// 3); $ c
更严重任务
const partial =(f,... xs )=> (... ys)=> f(... xs,... ys); const filter =(f,xs)=> xs.filter(f); const comp2 =(f,g,x,y)=> f(g(x,y)); const len = xs => xs.length; const odd = x => x%2 === 1; const countWhere = f =>部分(comp2,len,filter,f); const countWhereOdd = countWhere(odd); console.log(countWhereOdd([1,2,3,4,5])// 3);
部分
实际上可以根据需要多次应用
< pre class =snippet-code-js lang-js prettyprint-override> const partial =(f,... xs)=> (... ys)=> f(... xs,... ys)const p =(a,b,c,d,e,f)=> a + b + c + d + e + flet f = partial(p,1,2)let g = partial(f,3,4)let h = partial(g,5,6)console.log(p ,2,3,4,5,6))// 21console.log(f(3,4,5,6))// 21console.log(g(5,6))// 21console.log(h( ))// 21
这使它成为工作中不可或缺的工具使用可变参数函数也是如此数据库控制台数据库控制台数据库控制台数据库数据库数据库数据库数据库数据库数据库数据库babel =false>
const partial =(f,... xs )=> (... ys)=> f(... xs,... ys)const add =(x,y)=> x + yconst p =(... xs)=> xs.reduce(add,0)让f = partial(p,1,1,1,1)let g = partial(f,2,2,2,2)let h = partial(g,3,3,3 ,3)console.log(h(4,4,4,4))// 1 + 1 + 1 + 1 + // 2 + 2 + 2 + 2 + // 3 + 3 + 3 + 3 + // 4 + 4 + 4 + 4 => 40
最后,演示 partialRight
const partial =(f,.. .xs)=> (... ys)=> f(... xs,... ys); const partialRight =(f,... xs)=> (... ys)=> f(... ys,... xs); const p =(... xs)=> console.log(... xs)const f = partialRight(p,7,8,9); const g = partial(f,1,2,3); const h = partial(g,4,5,6) ; p(1,2,3,4,5,6,7,8,9)// 1 2 3 4 5 6 7 8 9f(1,2,3,4,5,6)// 1 2 3 4 5 6 7 8 9g(4,5,6)// 1 2 3 4 5 6 7 8 9h()// 1 2 3 4 5 6 7 8 9
b 好的,所以 我同意你的看法没有完全咖喱功能的替代品。我个人发现,一旦我停止判断语法的丑陋,就很容易采用这种新风格 - 它只是不同而人们不喜欢不同。 I love currying but there are a couple of reasons why a lof of Javascript devs reject this technique: Is there an approach that can mitigate these concerns so that my coworkers don't hate me? Note: @ftor answered his/her own question. This is a direct companion to that answer. You're already a genius I think you might've re-imagined the and it's counter-part, Initial remarks The context of this question is set in how currying and composition can play nice with a large user base of coders. My remarks will be in the same context just because a function may return a function does not mean that it is curried – tacking on a if we're going to create a wrapper for For the next section, I'm going to replace your Composable functions
Programmatic solution
A more serious task
Partial power !
This makes it an indispensable tool for working with variadic functions, too
Lastly, a demonstration of
Summary OK, so I agree with you that there's no replacement for fully curried functions. I personally found it easy to adopt the new style once I stopped being judgmental about the "ugliness" of the syntax – it's just different and people don't like different. 这篇关于如何调和Javascript与咖喱和功能组成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
partial 几乎是替代
可组合的的下降,一些额外的角落案件。让我们来看看这是如何与你最初的名单冲突的。
f(x)(y)( z)
partial
创建新函数
部分
足够灵活,可以在很多情况下删除点数
f(x) (y) (z)
partial
function – at least, in part!const partial = (f, ...xs) => (...ys) => f(...xs, ...ys);
partialRight
const partialRight = (f, ...xs) => (...ys) => f(...ys, ...xs);
partial
takes a function, some args (xs
), and always returns a function that takes some more args (ys
), then applies f
to (...xs, ...ys)
_
to signify that a function is waiting for more args is confusing. Recall that currying (or partial function application) abstracts arity, so we never know when a function call will result in the value of a computation or another function waiting to be called.curry
should not flip arguments; that is going to cause some serious wtf moments for your fellow coderreduce
, the reduceRight
wrapper should be consistent – eg, your foldl
uses f(acc, x, i)
but your foldr
uses f(x, acc, i)
– this will cause a lot of pain amongst coworkers that aren't familiar with these choicescomposable
with partial
, remove _
-suffixes, and fix the foldr
wrapper
const partial = (f, ...xs) => (...ys) => f(...xs, ...ys);
const partialRight = (f, ...xs) => (...ys) => f(...ys, ...xs);
const comp = (f, g) => x => f(g(x));
const foldl = (f, acc, xs) => xs.reduce(f, acc);
const drop = (xs, n) => xs.slice(n);
const add = (x, y) => x + y;
const sum = partial(foldl, add, 0);
const dropAndSum = comp(sum, partialRight(drop, 1));
console.log(
dropAndSum([1,2,3,4]) // 9
);
const partial = (f, ...xs) => (...ys) => f(...xs, ...ys);
// restore consistent interface
const foldr = (f, acc, xs) => xs.reduceRight(f, acc);
const comp = (f,g) => x => f(g(x));
// added this for later
const flip = f => (x,y) => f(y,x);
const I = x => x;
const inc = x => x + 1;
const compn = partial(foldr, flip(comp), I);
const inc3 = compn([inc, inc, inc]);
console.log(
inc3(0) // 3
);
const partial = (f, ...xs) => (...ys) => f(...xs, ...ys);
const filter = (f, xs) => xs.filter(f);
const comp2 = (f, g, x, y) => f(g(x, y));
const len = xs => xs.length;
const odd = x => x % 2 === 1;
const countWhere = f => partial(comp2, len, filter, f);
const countWhereOdd = countWhere(odd);
console.log(
countWhereOdd([1,2,3,4,5]) // 3
);
partial
can actually be applied as many times as neededconst partial = (f, ...xs) => (...ys) => f(...xs, ...ys)
const p = (a,b,c,d,e,f) => a + b + c + d + e + f
let f = partial(p,1,2)
let g = partial(f,3,4)
let h = partial(g,5,6)
console.log(p(1,2,3,4,5,6)) // 21
console.log(f(3,4,5,6)) // 21
console.log(g(5,6)) // 21
console.log(h()) // 21
const partial = (f, ...xs) => (...ys) => f(...xs, ...ys)
const add = (x,y) => x + y
const p = (...xs) => xs.reduce(add, 0)
let f = partial(p,1,1,1,1)
let g = partial(f,2,2,2,2)
let h = partial(g,3,3,3,3)
console.log(h(4,4,4,4))
// 1 + 1 + 1 + 1 +
// 2 + 2 + 2 + 2 +
// 3 + 3 + 3 + 3 +
// 4 + 4 + 4 + 4 => 40
partialRight
const partial = (f, ...xs) => (...ys) => f(...xs, ...ys);
const partialRight = (f, ...xs) => (...ys) => f(...ys, ...xs);
const p = (...xs) => console.log(...xs)
const f = partialRight(p, 7, 8, 9);
const g = partial(f, 1, 2, 3);
const h = partial(g, 4, 5, 6);
p(1, 2, 3, 4, 5, 6, 7, 8, 9) // 1 2 3 4 5 6 7 8 9
f(1, 2, 3, 4, 5, 6) // 1 2 3 4 5 6 7 8 9
g(4, 5, 6) // 1 2 3 4 5 6 7 8 9
h() // 1 2 3 4 5 6 7 8 9
partial
is pretty much a drop in replacement for composable
but also tackles some additional corner cases. Let's see how this bangs up against your initial list
f (x) (y) (z)
partial
creates new functionspartial
is flexible enough to remove points in many cases