我知道这是很有可能的,因为我的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 ? 6 :
(getScore(iteration - 1, factor) * factor);
在这种情况下,假设iteration
应该是整数,所以我们想将round()
应用于该参数。并设想factor
必须至少为1.3
,所以我们想将clamp()
应用于该参数。
如果我们将getScore
分为两个函数,则看起来更容易编写:
const getScore = iteration => factor =>
iteration < 2 ? 1 :
iteration === 2 ? 6 :
(getScore(iteration - 1)(factor) * factor);
执行此操作的代码可能类似于:
const getRoundedClampedScore = compose(round, clamp, getScore);
但是compose函数是什么样的?以及getRoundedClampedScore
是如何调用的?还是这太可怕了?
答案 0 :(得分:3)
compose
函数可能应该先将核心函数组成为 first ,使用rest参数将其他函数放入数组中,然后返回调用{{数组中具有第i
个参数的第1}}个函数:
i
答案 1 :(得分:3)
我认为您遇到的部分麻烦是compose
实际上不是您要查找的功能,而是其他东西。 compose
通过一系列函数提供值,而您希望对一系列参数进行预处理,然后将处理后的参数馈送到最终函数中。
Ramda具有一个非常完善的实用程序功能,称为converge
。 converge
的作用是产生一个函数,该函数以一对一的对应关系将一系列函数应用于一系列参数,然后将所有这些转换后的参数馈送到另一个函数中。在您的情况下,使用它看起来像这样:
var saferGetScore = R.converge(getScore, [round, clamp]);
如果您不想仅使用此converge
函数就参与整个第三方库,则只需一行代码即可轻松定义您的代码。看起来很像CaptainPerformance在答案中使用的内容,但少了...
(而且您绝对不应该将其命名为compose
,因为这是一个完全不同的概念):
const converge = (f, fs) => (...args) => f(...args.map((a, i) => fs[i](a)));
const saferGetScore = converge(getScore, [round, clamp]);
const score = saferGetScore(2.5, 0.3);
答案 2 :(得分:3)
Haskell程序员通常可以简化表达式,类似于简化数学表达式的方式。我将在此答案中向您展示如何执行此操作。首先,让我们看看您的表情的基本组成部分:
round :: Number -> Number
clamp :: Number -> Number
getScore :: Number -> Number -> Number
通过组成这三个函数,我们想要创建以下函数:
getRoundedClampedScore :: Number -> Number -> Number
getRoundedClampedScore iteration factor = getScore (round iteration) (clamp factor)
我们可以简化此表达式,如下所示:
getRoundedClampedScore iteration factor = getScore (round iteration) (clamp factor)
getRoundedClampedScore iteration = getScore (round iteration) . clamp
getRoundedClampedScore iteration = (getScore . round) iteration . clamp
getRoundedClampedScore iteration = (. clamp) ((getScore . round) iteration)
getRoundedClampedScore = (. clamp) . (getScore . round)
getRoundedClampedScore = (. clamp) . getScore . round
如果要将其直接转换为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, clamp);
Pointfree style通常有用,但有时毫无意义。