具有扩展参数的 Pointfree 组合函数
Pointfree composed function with spreaded arguments
当参数应该散布在柯里化组合函数中时,我正在尝试找出是否存在用于编写 pointfree 组合函数的模式
即(使用 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
周围并摆脱结果函数的柯里化。
const add_1_and_multiply = R.uncurryN(2, R.compose(R.multiply, R.add(1)))
let result2 = add_1_and_multiply(3, 5) //> 20
要向 "chain" 添加另一个函数,您需要将其与之前的函数组合。
// 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 而没有任何 "points"。
让我们尝试简单的组合并分析它:
let add1_mul_add = compose(add)(add1_mul)
记住撰写签名:(E -> F) -> (D -> E) -> D -> F
!
分步分析:
我们提供 add
函数签名而不是 (E -> F)
(E -> F )
(A -> A -> A)
我们得出结论
E = A
F = A -> A
我们对(D -> E)
和add1_mul
做同样的事情
(D -> E )
(A -> A -> A)
我们得出结论
D = A
E = A -> A
但是我们已经可以看出矛盾了!
步骤 2 中的结论与步骤 1 中的结论相矛盾:
E
不能同时是A
和A -> A
。
因此我们不能组合 add
和 add1_mul
并且我们的 add1_mul_add
会抛出错误。
让我们尝试解决这个问题并修复它,这违反了我们对 pointfree 风格的承诺。
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)
但是现在,对于链中的每个新函数,您都必须创建更高阶的 owl 运算符。
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)
所以我真的建议您使用无点样式的一元函数。或者使用其他构造,如列表:
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)
当参数应该散布在柯里化组合函数中时,我正在尝试找出是否存在用于编写 pointfree 组合函数的模式
即(使用 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
周围并摆脱结果函数的柯里化。
const add_1_and_multiply = R.uncurryN(2, R.compose(R.multiply, R.add(1)))
let result2 = add_1_and_multiply(3, 5) //> 20
要向 "chain" 添加另一个函数,您需要将其与之前的函数组合。
// 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 而没有任何 "points"。 让我们尝试简单的组合并分析它:
let add1_mul_add = compose(add)(add1_mul)
记住撰写签名:(E -> F) -> (D -> E) -> D -> F
!
分步分析:
我们提供
add
函数签名而不是(E -> F)
(E -> F ) (A -> A -> A)
我们得出结论
E = A F = A -> A
我们对
做同样的事情(D -> E)
和add1_mul
(D -> E ) (A -> A -> A)
我们得出结论
D = A E = A -> A
但是我们已经可以看出矛盾了!
步骤 2 中的结论与步骤 1 中的结论相矛盾:
E
不能同时是A
和A -> A
。
因此我们不能组合 add
和 add1_mul
并且我们的 add1_mul_add
会抛出错误。
让我们尝试解决这个问题并修复它,这违反了我们对 pointfree 风格的承诺。
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)
但是现在,对于链中的每个新函数,您都必须创建更高阶的 owl 运算符。
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)
所以我真的建议您使用无点样式的一元函数。或者使用其他构造,如列表:
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)