你能给我一个如何使用 Ramda lift 的例子吗?
Can you give me an example of how to use Ramda lift?
我正在阅读 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]
看起来是个很有用的功能。我看不出它有什么用例。
谢谢
此函数只能接受数字:
const add3 = (a, b, c) => a + b + c;
add3(1, 2, 3); //=> 6
但是,如果这些数字每个都包含在一个函子中呢? (即包含值的事物;下例中的数组)
add3([1], [2], [3]); //=> "123"
这显然不是我们想要的。
您可以 "lift" 函数,以便它可以 "extract" 每个 parameter/functor:
的值
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]);
基本上它采用笛卡尔积并将函数应用于每个数组。
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]
当前答案中没有提到的是,像 R.lift
这样的函数不仅适用于数组,而且适用于任何表现良好的 Apply1数据类型。
例如,我们可以重用 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
命名空间方法时仍然受支持。
函数式编程是一个很长的数学主题,尤其是 the part dealing with monads and cathegory theory in general. But it is worth to take a look at it, here is a funny introduction with pictures。
简而言之,lift 是一个函数,它将采用 n 个参数 函数并将产生一个采用 n wrapped-values 并产生另一个结果 wrapped-value。采用单参数函数的提升由以下类型签名定义
// name :: f is a wrp-value => function -> wrp-value -> wrp-value
liftA :: Applicative f => (a -> b) -> f a -> f b
等等...包装值?
我简单介绍一下Haskell,只是为了说明这一点。在haskell中,wrapped-value的一个简单例子是Maybe,Maybe可以是wrapped-value或者nothing,这也是wrapped-value。以下示例将函数应用于包含值的 Maybe 和空的 Maybe。
> liftA (+ 8) (Just 8)
Just 16
> liftA (+ 8) Nothing
Nothing
列表也是一个包装值,我们可以对其应用函数。在第二种情况下,liftA2 将双参数函数应用于两个列表。
> liftA (+ 8) [1,2,3]
[9,10,11]
> liftA2 (*) [1..3] [1..3]
[1,2,3,2,4,6,3,6,9]
这个 wrapped-value 是一个 Applicative Functor,所以从现在开始我将把它称为 Applicative。
也许也许你从这点开始失去兴趣...
但是我们之前有人在这个话题上迷失了方向,。
让我们看看他看到了什么...
...
他看到了幻境
In fantasy-land, an object implements Apply
spec when it has
an ap
method defined (that object also has to implement
Functor
spec by defining a map
method).
- Fantasy-land 是函数式编程规范的奇特名称
javascript。 Ramda 紧随其后。
- Apply是我们的Applicative,一个
Functor 也实现了 ap 方法。
- A Functor,是具有 map 方法的东西。
所以,等等...javascript 中的数组有一个映射...
[1,2,3].map((a)=>a+1) \=> [ 2, 3, 4 ]
那么 Array 是一个 Functor,map 将一个函数应用于它的所有值,返回另一个具有相同数量的值的 Functor。
但是 ap 有什么作用?
ap applies a list of functions to a list of values.
Dispatches to the ap method of the second argument, if present. Also
treats curried functions as applicatives.
让我们试着用它做点什么。
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>
这些 wrappers(Applicatives、Functors、Monads)很有趣,因为它们可以是实现这些方法的任何东西。在haskell中,this用于包装不安全的操作,如input/output。它也可以是错误包装器或树,甚至是任何数据结构。
我正在阅读 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]
看起来是个很有用的功能。我看不出它有什么用例。
谢谢
此函数只能接受数字:
const add3 = (a, b, c) => a + b + c;
add3(1, 2, 3); //=> 6
但是,如果这些数字每个都包含在一个函子中呢? (即包含值的事物;下例中的数组)
add3([1], [2], [3]); //=> "123"
这显然不是我们想要的。 您可以 "lift" 函数,以便它可以 "extract" 每个 parameter/functor:
的值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]);
基本上它采用笛卡尔积并将函数应用于每个数组。
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]
当前答案中没有提到的是,像 R.lift
这样的函数不仅适用于数组,而且适用于任何表现良好的 Apply1数据类型。
例如,我们可以重用 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
命名空间方法时仍然受支持。
函数式编程是一个很长的数学主题,尤其是 the part dealing with monads and cathegory theory in general. But it is worth to take a look at it, here is a funny introduction with pictures。
简而言之,lift 是一个函数,它将采用 n 个参数 函数并将产生一个采用 n wrapped-values 并产生另一个结果 wrapped-value。采用单参数函数的提升由以下类型签名定义
// name :: f is a wrp-value => function -> wrp-value -> wrp-value
liftA :: Applicative f => (a -> b) -> f a -> f b
等等...包装值?
我简单介绍一下Haskell,只是为了说明这一点。在haskell中,wrapped-value的一个简单例子是Maybe,Maybe可以是wrapped-value或者nothing,这也是wrapped-value。以下示例将函数应用于包含值的 Maybe 和空的 Maybe。
> liftA (+ 8) (Just 8)
Just 16
> liftA (+ 8) Nothing
Nothing
列表也是一个包装值,我们可以对其应用函数。在第二种情况下,liftA2 将双参数函数应用于两个列表。
> liftA (+ 8) [1,2,3]
[9,10,11]
> liftA2 (*) [1..3] [1..3]
[1,2,3,2,4,6,3,6,9]
这个 wrapped-value 是一个 Applicative Functor,所以从现在开始我将把它称为 Applicative。
也许也许你从这点开始失去兴趣...
但是我们之前有人在这个话题上迷失了方向,
让我们看看他看到了什么...
...
他看到了幻境
In fantasy-land, an object implements
Apply
spec when it has anap
method defined (that object also has to implementFunctor
spec by defining amap
method).
- Fantasy-land 是函数式编程规范的奇特名称 javascript。 Ramda 紧随其后。
- Apply是我们的Applicative,一个 Functor 也实现了 ap 方法。
- A Functor,是具有 map 方法的东西。
所以,等等...javascript 中的数组有一个映射...
[1,2,3].map((a)=>a+1) \=> [ 2, 3, 4 ]
那么 Array 是一个 Functor,map 将一个函数应用于它的所有值,返回另一个具有相同数量的值的 Functor。
但是 ap 有什么作用?
ap applies a list of functions to a list of values.
Dispatches to the ap method of the second argument, if present. Also treats curried functions as applicatives.
让我们试着用它做点什么。
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>
这些 wrappers(Applicatives、Functors、Monads)很有趣,因为它们可以是实现这些方法的任何东西。在haskell中,this用于包装不安全的操作,如input/output。它也可以是错误包装器或树,甚至是任何数据结构。