JavaScript中的功能组合

时间:2018-07-19 04:34:50

标签: 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 ? 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是如何调用的?还是这太可怕了?

3 个答案:

答案 0 :(得分:3)

compose函数可能应该先将核心函数组成为 first ,使用rest参数将其他函数放入数组中,然后返回调用{{数组中具有第i个参数的第1}}个函数:

i

答案 1 :(得分:3)

我认为您遇到的部分麻烦是compose实际上不是您要查找的功能,而是其他东西。 compose通过一系列函数提供值,而您希望对一系列参数进行预处理,然后将处理后的参数馈送到最终函数中。

Ramda具有一个非常完善的实用程序功能,称为convergeconverge的作用是产生一个函数,该函数以一对一的对应关系将一系列函数应用于一系列参数,然后将所有这些转换后的参数馈送到另一个函数中。在您的情况下,使用它看起来像这样:

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通常有用,但有时毫无意义。