如何编写像 zip-apply-ish 这样的函数
How to compose functions like zip-apply-ish
假设我有一堆 arity 2 的函数:f: a b -> x
, g: 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);
我尝试用 compose
、composeK
、pipe
、ap
解决它,但其中 none 似乎适用于这种情况,或者我只是根本看不到解决方案。欢迎任何帮助。
可能有一些我不知道的方便的 Ramda 函数,或者一些超级函数 combinator-thingy 我不明白这让这很容易,但无论如何:
您可以创建自己的组合函数来组合二进制函数列表并注入值。这个函数接受一个函数列表和一个参数列表。它部分地将它获得的第一个函数应用到第一个参数并继续这样做直到它没有参数,此时它 returns 一个最终的组合(一元)函数:
// 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))
: /* ... */
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>
这些都不会对空列表或比函数列表短的参数列表(在第一种情况下)进行任何错误检查。但除此之外,它们似乎符合要求。
肯定会有第二个的递归版本来匹配第一个,但是这个实现已经相当简单了。我没有轻易地看到第一个版本像第二个一样简单,但它很可能存在。
假设我有一堆 arity 2 的函数:f: a b -> x
, g: 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);
我尝试用 compose
、composeK
、pipe
、ap
解决它,但其中 none 似乎适用于这种情况,或者我只是根本看不到解决方案。欢迎任何帮助。
可能有一些我不知道的方便的 Ramda 函数,或者一些超级函数 combinator-thingy 我不明白这让这很容易,但无论如何:
您可以创建自己的组合函数来组合二进制函数列表并注入值。这个函数接受一个函数列表和一个参数列表。它部分地将它获得的第一个函数应用到第一个参数并继续这样做直到它没有参数,此时它 returns 一个最终的组合(一元)函数:
// 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))
: /* ... */
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>
这些都不会对空列表或比函数列表短的参数列表(在第一种情况下)进行任何错误检查。但除此之外,它们似乎符合要求。
肯定会有第二个的递归版本来匹配第一个,但是这个实现已经相当简单了。我没有轻易地看到第一个版本像第二个一样简单,但它很可能存在。