JavaScript中的函数组合
我知道这是很有可能的,因为我的Haskell朋友们似乎能够在他们的睡眠中做这类事情,但我不能用JS来处理更复杂的函数组合 比如说,您有以下三个功能:JavaScript中的函数组合,javascript,functional-programming,composition,Javascript,Functional Programming,Composition,我知道这是很有可能的,因为我的Haskell朋友们似乎能够在他们的睡眠中做这类事情,但我不能用JS来处理更复杂的函数组合 比如说,您有以下三个功能: const round = v => Math.round(v); const clamp = v => v < 1.3 ? 1.3 : v; const getScore = (iteration, factor) => iteration < 2 ? 1 : iteration === 2 ?
const round = v => Math.round(v);
const clamp = v => v < 1.3 ? 1.3 : v;
const getScore = (iteration, factor) =>
iteration < 2 ? 1 :
iteration === 2 ? 6 :
(getScore(iteration - 1, factor) * factor);
执行此操作的代码可能如下所示:
const getRoundedClampedScore = compose(round, clamp, getScore);
var saferGetScore = R.converge(getScore, [round, clamp]);
但是compose函数是什么样子的呢?如何调用
getRoundedClampedScore
?或者这是一个可怕的错误?函数应该首先使用要合成的核心函数,使用rest参数将其他函数放入一个数组,然后返回一个函数,该函数使用i
th参数调用数组中的i
th函数:
constround=v=>Math.round(v);
常数钳=v=>v<1.3?1.3:v;
const getScore=迭代=>因子=>
迭代<2?1 :
迭代===2?6 :
(得分(迭代-1)(因子)*因子);
const compose=(fn,…transformArgsFns)=>(…args)=>{
const newArgs=transformArgsFns.map((tranformArgFn,i)=>tranformArgFn(args[i]);
返回fn(…新参数);
}
const getRoundedClampedScore=组合(getScore,round,clampedscore);
控制台日志(getRoundedClampedScore(1)(5))
控制台日志(getRoundedClampedScore(3.3)(5))
log(getRoundedClampedScore(3.3)(1))
我认为您遇到的部分问题是,compose
实际上不是您要寻找的函数,而是其他函数compose
通过一系列函数提供值,而您希望预处理一系列参数,然后将处理后的参数提供给最终函数
有一个非常适合此功能的实用程序函数,称为converge
。converge所做的是生成一个函数,该函数将一系列函数应用于一对一对应关系上的一系列参数,然后将所有这些转换后的参数馈送到另一个函数中。在您的情况下,使用它将如下所示:
const getRoundedClampedScore = compose(round, clamp, getScore);
var saferGetScore = R.converge(getScore, [round, clamp]);
如果您不想仅仅为了使用这个converge
函数而卷入整个第三方库,您可以用一行代码轻松地定义您的。这看起来很像队长在回答中使用的,但是少了一个。
(你绝对不应该把它命名为compose
,因为这是一个完全不同的概念):
Haskell程序员通常可以简化表达式,就像简化数学表达式一样。我将在这个答案中向您展示如何做到这一点。首先,让我们看看表达式的构建块:
round::Number->Number
夹具::编号->编号
getScore::编号->编号->编号
通过组合这三个函数,我们希望创建以下函数:
getRoundedClampedScore::Number->Number->Number
getRoundedClampedScore迭代因子=getScore(圆形迭代)(钳制因子)
我们可以将此表达式简化如下:
getRoundedClampedScore迭代因子=getScore(圆形迭代)(钳制因子)
getRoundedClampedScore迭代=getScore(圆形迭代)。夹紧
getRoundedClampedScore迭代=(getScore.round)迭代。夹紧
getRoundedClampedScore迭代=(.clamp)((getScore.round)迭代)
getRoundedClampedScore=(.clamp)。(getScore.round)
getRoundedClampedScore=(.clamp)。得分。圆形的
如果要将其直接转换为JavaScript,则可以使用反向函数组合:
const pipe = f => g => x => g(f(x));
const compose2 = (f, g, h) => pipe(g)(pipe(f)(pipe(h)));
const getRoundedClampedScore = compose2(getScore, round, clamp);
// You'd call it as follows:
getRoundedClampedScore(iteration)(factor);
也就是说,最好的解决方案是简单地以有意义的形式定义它:
const compose2=(f,g,h)=>x=>y=>f(g(x))(h(y));
const getRoundedClampedScore=compose2(getScore,round,climp);
通常是有用的,但有时是毫无意义的。通常,一个
compose
函数通过提供的函数按与提供的顺序相反的顺序提供数据。因此,在compose(getScore,round,clamp)
的情况下,人们希望执行clamp
,然后round
,然后getScore
。通过函数从左到右传递数据的实用函数通常称为pipe
。现在我更仔细地看一下您的代码,这里的实现与compose
完全不同。它将所有函数(第一个函数除外)应用于每个参数,然后将这些结果输入到第一个函数中。这不是compose
函数所做的。更简单的是:const compose=(f,…fs)=>(…xs)=>f(…fs.map((f,i)=>f(xs[i])这是我第一次觉得没有资格接受任何答案。我的感觉是,我的问题可能偏离了目标:我没有完全理解compose()
的精确定义,而是更笼统地询问了有关composition的问题。那太草率了。我喜欢这里的函数签名及其简单性(尤其是@AaditMShah的速记),因此这可能是我最终将使用的代码。我也非常尊重@JLRishe对上述异议的评论。我真的很感谢你们花了这么多时间在这件事上,我真的想把答案奖励给你们三个人。我认为“撰写函数看起来像什么”是一个错误的问题。只有一个compose
函数,人们不应该发明新的compose
函数来适应特定的情况。@JLRishe我不同意。正常的函数组合(即(b->c)->(a->b)->a->c)就是函数组合的最基本形式。还有更复杂的函数组合形式,如(c->d)->(a->b->c)->a->b->d
。在这个问题中,函数组合的形式是(a->b->c)->(d->a)->(e->b)->d->e->c
。现在,更复杂的形式是o