您能举一个使用Ramda Lift的例子吗?

时间:2019-01-14 08:53:17

标签: javascript functional-programming ramda.js

我正在阅读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]

它看起来像是一个非常有用的功能。我看不出有什么用例。

谢谢

4 个答案:

答案 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 monadscathegory 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感到迷惑。让我们看看他看到了什么...

...

What did you see? (xkcd comic)

他看到了梦幻之地

  

在幻想世界中,对象具有Apply规范   定义的ap方法(该对象还必须实现   通过定义map方法Functor规范。

  • Fantasy-land是功能编程规范中的一个花哨名称 javascript。 Ramda紧随其后。
  • 应用是我们的应用程序, Functor ,它也实现了 ap 方法。
  • Functor 是具有 map 方法的东西。

所以,等等... 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]