我正在阅读ramda文档
const madd3 = R.lift((a, b, c) => a + b + c);
madd3([1,2,3], [1,2,3], [1]); //=> [3, 4, 5, 4, 5, 6, 5, 6, 7]
它看起来像是一个非常有用的功能。我看不出有什么用例。
谢谢
答案 0 :(得分:2)
当前答案中未提到的是,像R.lift
这样的函数不仅适用于数组,还适用于任何表现良好的Apply 1 数据类型。
例如,我们可以重用R.lift
产生的相同功能:
const lifted = lift((a, b, c) => a + b - c)
以函数作为“应用”类型:
lifted(a => a * a,
b => b + 5,
c => c * 3)(4) //=> 13
可选类型(调度到.ap
):
const Just = val => ({
map: f => Just(f(val)),
ap: other => other.map(otherVal => val(otherVal)),
getOr: _ => val
})
const Nothing = {
map: f => Nothing,
ap: other => Nothing,
getOr: x => x
}
lifted(Just(4), Just(6), Just(8)).getOr(NaN) //=> 2
lifted(Just(4), Nothing, Just(8)).getOr(NaN) //=> NaN
异步类型(调度到.ap
)
const Asynchronous = fn => ({
run: fn,
map: f => Asynchronous(g => fn(a => g(f(a)))),
ap: other => Asynchronous(fb => fn(f => other.run(a => fb(f(a)))))
})
const delay = (n, x) => Asynchronous(then => void(setTimeout(then, n, x)))
lifted(delay(2000, 4), delay(1000, 6), delay(500, 8)).run(console.log)
...还有更多。这里的要点是,任何可以支持任何Apply类型的界面和法律的事物都可以利用诸如R.lift
之类的通用函数。
1。幻想世界规范中列出的ap
的参数顺序与Ramda中名称分配支持的顺序相反,尽管在使用fantasy-land/ap
命名空间方法时仍受支持。 >
答案 1 :(得分:2)
函数式编程是一个漫长的数学主题,尤其是the part dealing with monads和cathegory theory in general。但是值得一看,here is a funny introduction with pictures。
简而言之,提升是一个带有 n个参数函数并产生一个带有 n 包装值并产生的函数另一个结果包装值。
此包装值是可应用的Functor,因此从现在开始,我将其称为可应用。
从这一点开始,也许您开始失去兴趣了,但是摆在我们面前的某个人对这个话题finally he survived and published it as an answer to this question感到迷惑。让我们看看他看到了什么...
...
他看到了梦幻之地
所以,等等... javascript中的数组有一个映射...
[1,2,3].map((a)=>a+1) \\=> [ 2, 3, 4 ]
然后数组是一个函子,并且映射将函数应用于其所有值,并返回另一个具有相同数量值的函子。
但是ap会做什么?
ap将函数列表应用于值列表。
调度第二个参数的ap方法(如果存在)。也 将咖喱函数视为可应用的函数。
让我们尝试做些什么。
const res = R.ap(
[
(a)=>(-1*a),
(a)=>((a>1?'greater than 1':'a low value'))
],
[1,2,3]); //=> [ -1, -2, -3, "a low value", "greater than 1", "greater than 1" ]
console.log(res);
<script src="https://cdn.jsdelivr.net/npm/ramda@0.26.1/dist/ramda.min.js"></script>
ap方法采用一个数组(或其他一些Applicative)函数,并将其应用于值的Applicative,以生成另一个扁平化的Applicative。
方法的签名对此进行了解释
[a → b] → [a] → [b]
Apply f => f (a → b) → f a → f b
最后,电梯起什么作用?
Lift接受带有 n 自变量的函数,并生成另一个接受 n Aplicatives 的函数,并生成扁平化的 Aplicative 结果>
在这种情况下,我们的Applicative是数组。
const add2 = (a, b) => a + b;
const madd2 = R.lift(add2);
const res = madd2([1,2,3], [2,3,4]);
//=> [3, 4, 5, 4, 5, 6, 5, 6, 7]
console.log(res);
// Equivalent to lift using ap
const result2 = R.ap(R.ap(
[R.curry(add2)], [1, 2, 3]),
[2, 3, 4]
);
//=> [3, 4, 5, 4, 5, 6, 5, 6, 7]
console.log(result2);
<script src="https://cdn.jsdelivr.net/npm/ramda@0.26.1/dist/ramda.min.js"></script>
这些包装器(应用程序,函子和Monads)很有趣,因为您可以是实现这些方法的任何东西。在haskell中,这用于包装不安全的操作,例如输入/输出。它也可以是错误包装器或树,甚至可以是任何数据结构。
答案 2 :(得分:1)
此功能只能接受数字:
const add3 = (a, b, c) => a + b + c;
add3(1, 2, 3); //=> 6
但是这些数字都包含在函子中怎么办? (即包含值的事物;在下面的示例中为数组)
add3([1], [2], [3]); //=> "123"
这显然不是我们想要的。 您可以“提升”该函数,以便它可以“提取”每个参数/函数的值:
const add3Lifted = lift(add3);
add3Lifted([1], [2], [3]); //=> [6]
数组显然可以保存多个值,并与一个提升函数结合在一起,该函数知道如何提取每个函子的值,您现在可以执行以下操作:
add3Lifted([1, 10], [2, 20], [3, 30]);
//=> [6, 33, 24, 51, 15, 42, 33, 60]
如果这样做,基本上可以得到什么:
[
add3(1, 2, 3), // 6
add3(1, 2, 30), // 33
add3(1, 20, 3), // 24
add3(1, 20, 30), // 51
add3(10, 2, 3), // 15
add3(10, 2, 30), // 42
add3(10, 20, 3), // 33
add3(10, 20, 30) // 60
]
请注意,每个数组的长度不必相同:
add3Lifted([1, 10], [2], [3]);
//=> [6, 15]
因此回答您的问题:如果您打算运行具有不同值集的函数,则考虑该函数可能是一个有用的考虑:
const results = [add3(1, 2, 3), add3(10, 2, 3)];
与以下相同:
const results = add3Lifted([1, 10], [2], [3]);
答案 3 :(得分:1)
基本上,它采用笛卡尔乘积并将一个函数应用于每个数组。
const
cartesian = (a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), []),
fn = ([a, b, c]) => a + b + c,
result = [[1, 2, 3], [1, 2, 3], [1]]
.reduce(cartesian)
.map(fn);
console.log(result); // [3, 4, 5, 4, 5, 6, 5, 6, 7]