如何撰写像zip-apply-ish

时间:2018-03-07 09:43:18

标签: functional-programming ramda.js function-composition

假设我有一堆arity 2的函数:f: a b -> xg: c d -> y, 等到一元函数u: a -> a。我想做的是以这种方式链接它们:

f(_, g(_, .... z(_, u(_))...)

其中内部_占位符将注入来自给定输入数组的连续值。我试图使用Ramda库解决此问题。

我遇到的另一个非常类似的问题是以相同的方式链接函数,但_占位符填充了与此合成执行相同的值。

更具体一点:

// 1st problem
someComposition( f(v[0], g(v[1], ..... z(v[n-1], u(v[n]))...) )(v);

// 2nd problem
someComposition2( f(v, g(v, ..... z(v, u(v))...) )(v);

我能想出的第二个问题是,假设所有函数都是可以修改的,遵循一段代码(因为(v)重复而讨厌它):

compose(
    z(v),
    ...
    g(v),
    f(v),
    u
)(v);

我尝试使用composecomposeKpipeap解决问题,但似乎没有一个适用于这种情况,或者我只是无法看到解决方案。任何帮助都更受欢迎。

2 个答案:

答案 0 :(得分:1)

可能有一些我不知道的方便的Ramda功能,或者一些超级功能的组合 - 我不明白这使得这很容易,但无论如何:

您可以创建自己的合成函数来组合二进制函数列表并注入值。此函数采用函数列表和参数列表。它部分地应用它获得的第一个函数到第一个参数并继续这样做,直到它超出参数,在该参数下它返回一个最终的组合(一元)函数:

// Utils
const { add, subtract, multiply, identity, compose, isNil } = R;
const square = x => x * x;

// Our own compose method
const comp2_1 = ([f2, ...f2s ], [x, ...xs], f = identity) =>
  isNil(x)
    ? compose(f2, f)
    : comp2_1(f2s, xs, compose(f2(x), f));


// An example
const myFormula = comp2_1(
  [add, subtract, multiply, square], 
  [10, 5, 2]);

// 3 + 10 = 13
// 13 - 5 = 8
// 8 * 2 = 16
// 16 * 16 = 256
console.log(myFormula(3));
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>

此示例仅适用于xs.length === fs.length + 1。例如,即使我们没有参数,你也可能希望它更灵活一些:

/* ... */
isNil(x)
  ? isNil(f2) 
    ? f
    : comp2_1(f2s, [], compose(f2, f)) 
  : /* ... */

答案 1 :(得分:1)

Ramda中没有任何内容可以直接用于其中任何一个。 (免责声明:我是Ramda的作者之一。)如果你愿意,你可以创建这样的组合函数:

const {tail, compose, reduce, identity, reverse} = R;

const f = (x, y) => `f(${x}, ${y})`;
const g = (x, y) => `g(${x}, ${y})`;
const h = (x, y) => `h(${x}, ${y})`;
const i = (x, y) => `i(${x}, ${y})`;
const j = (x) => `j(${x})`;

const multApply = (fns) => (...vals) => fns.length < 2 
      ? fns[0].apply(null, vals) 
      : fns[0](vals[0], multApply(tail(fns))(...tail(vals)));

console.log(multApply([f, g, h, i, j])('a', 'b', 'c', 'd', 'e'));
                               //=> f(a, g(b, h(c, i(d, j(e)))))

const nest = compose(reduce((g, f) => (v) => f(v, g(v)), identity), reverse);

console.log(nest([f, g, h, i, j])('v')) //=> f(v, g(v, h(v, i(v, j(v)))));
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>

这些都没有对空列表进行任何错误检查,或者对于比功能列表短的参数列表(在第一种情况下)。但除此之外,它们似乎符合要求。

肯定会有第二个的递归版本与第一个相匹配,但这个实现已经非常简单了。我没有看到第一个版本和第二个版本一样简单,但它可能存在。