javascript - 具有扩展参数的 Pointfree 组合函数

标签 javascript ramda.js pointfree

当参数应该散布在柯里化(Currying)组合函数中时,我试图弄清楚是否存在编写无点组合函数的模式
即(使用 Ramda):

add_1_and_multiply = (add, mul) => R.compose(R.multiply(mul), R.add(1))(add)
add_1_and_multiply(3, 5)  // 20

如何以 pointfree 风格编写 add_1_and_multiply

最佳答案

我不确定您是否可以轻松地将 pointfree 样式和非一元元数结合起来。 首先考虑结果函数和组合函数的类型:

// Compose:         (B  ->  C) -> (A  ->  B) ->  A  ->  C
const compose = f => g => x => f(g(x))
// Add:              A  ->  A  ->  A
const add = x => y => x + y
// Mul:              A  ->  A  ->  A
const mul = x => y => x * y

// Add1:             A  ->  A
const add1 = add(1)

// Add1AndMul:       A  ->         A  ->  A
//   because:
//     Add1:         A  ->  A
//     Mul:                 A  ->  A  ->  A
const add_1_and_mul = compose(mul)(add1)

// Mul4:             A  ->  A
const mul_4 = add_1_and_mul(3)
const result = mul_4(5) //> 20

Ramda 有 uncurryN,因此您可以将它包裹在 compose 周围,并摆脱结果函数的柯里化(Currying)。

const add_1_and_multiply = R.uncurryN(2, R.compose(R.multiply, R.add(1)))
let result2 = add_1_and_multiply(3, 5) //> 20

要将另一个函数添加到“链”中,您需要将其与之前的函数组合。

// Add1AndMul:          A -> A -> A
const add1_mul = compose(mul)(add1)

这是我们想要的签名。

//                      1         2         3
// Add1AndMulAndAdd:    A ->      A ->      A -> A
//  which is:           |         |         |
//      Add1:           A -> A    |         |
//      Mul:                 A -> A -> A    |
//      Add:                           A -> A -> A

所以我们必须以某种方式通过那些 A2 和 A3 而没有任何“分数”。 让我们尝试简单的组合并分析它:

let add1_mul_add = compose(add)(add1_mul)

记住撰写的签名:(E -> F) -> (D -> E) -> D -> F! 分步分析:

  1. 我们提供 add 函数签名而不是 (E -> F)

     (E -> F     )
     (A -> A -> A)
    

    我们的结论是

     E = A
     F = A -> A
    
  2. 我们对 (D -> E)add1_mul

    做同样的事情
     (D -> E     )
     (A -> A -> A)
    

    我们的结论是

     D = A
     E = A -> A
    

但我们已经可以看出其中的矛盾之处! 步骤 2 中的结论与步骤 1 中的结论相矛盾: E 不能同时是 AA -> A

因此我们不能组合 addadd1_mul 并且我们的 add1_mul_add 会抛出错误。

让我们尝试解决这个问题并修复它,打破我们对 pointfree 风格的 promise 。

const add1_mul_add = x => compose(add)(add1_mul(x))

我将打破一些规则并将签名与代码混合来说明我的观点:

x -> (A -> A -> A) -> (x -> A -> A) -> A -> A -> A
                       ||
                       \/
x -> (A -> A -> A) -> (A -> A) -> A -> A -> A
     (E -> F     ) -> (D -> E) -> D -> F

所以我们得到了正确的撰写签名!如何摆脱 x 变量以返回到 pointfree? 我们可以尝试寻找明显的模式,例如......我们的古老函数组合!

f(g(x)) => compose(f)(g)

我们在新的 add1_mul_add 中找到了这种模式 -

f = compose(add)
g = add1_mul
f(g(x)) = compose(add)(add1_mul(x))

然后我们将它简化为 pointfree,我们得到了新的 add1_mul_add 函数:

const add1_mul_add = compose(compose(add))(add1_mul)

但是,嘿 - 我们可以进一步减少它!

const add1_mul_add = compose(compose)(compose)(add)(add1_mul)

然后我们发现 haskell 中已经存在的名称为 The Owl 的东西.

我们可以在 Javascript 中简单地定义它:

const owl = compose(compose)(compose)

但是现在,对于链中的每个新函数,您都必须创建更高阶的猫头鹰运算符。

const owl2 = compose(compose)(owl)
const add1_mul_add_mul = owl2(mul)(add1_mul_add)

const owl3 = compose(compose)(owl2)
const add1_mul_add_mul_add = owl3(add)(add1_mul_add_mul)

所以我真的建议让你的函数以 pointfree 风格一元化。或者使用其他构造,如列表:

const actions = [ add, mul, add, mul ]
const values  = [ 1,   2,   3,   4   ]
const add_mul_add_mul = (...values) => zip(actions, values).reduce((acc, [action, value]) => action(acc, value), 0)

关于javascript - 具有扩展参数的 Pointfree 组合函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46663923/

相关文章:

javascript - 当我开始输入时缩小搜索结果

javascript - 我可以让这个函数定义更短吗?

javascript - 这里有更好的方法来使用 applySpec

javascript - 如何在JS中演示简单的无点风格

javascript - 使用 PHP 和 Javascript 的多次拍卖倒计时

javascript - 如何将 iframe 面板注入(inject) Google Chrome 扩展并在 iframe 内添加点击事件?

javascript - Ionic 从系统中读取文件并将其放入 Canvas 中

javascript - 使用 ramda.js 获取嵌套对象数组中某个键的所有值

f# - 创建元组 pointfree

performance - Pointfree 版本使性能恶化