使用中缀函数的部分应用

13

虽然我对数学上的柯里化有些了解,但是部分应用中缀函数是一个新概念,我是在深入学习Learn You a Haskell for Great Good这本书后才发现的。

给定以下函数:

applyTwice :: (a -> a) -> a -> a
applyTwice f x = f (f x)
作者以一种有趣的方式使用它:
ghci> applyTwice (++ [0]) [1]  
[1,0,0]
ghci> applyTwice ([0] ++) [1]
[0,0,1]

我可以清楚地看到,生成的函数传递了不同的参数,这在考虑到它是柯里化函数时通常不会发生(不会吗?)。那么,Haskell是否对中缀运算符有任何特殊处理?是否适用于所有中缀函数?


顺便说一句,这是我学习 Haskell 和函数式编程的第一周,我还在读这本书。

3个回答

19

是的,你可以通过指定中缀操作符的左或右操作数来部分应用它,只需将另一个留空(就像你写的两个例子一样)。

因此,([0] ++)(++) [0]\x -> [0] ++ x相同(请记住,您可以通过括号将中缀运算符转换为标准函数),而(++ [0])等于\x -> x ++ [0]

同时了解反引号(``)的用法也很有用,它使您能够将具有两个参数的任何标准函数转换为中缀运算符:

Prelude> elem 2 [1,2,3]
True
Prelude> 2 `elem` [1,2,3] -- this is the same as before
True
Prelude> let f = (`elem` [1,2,3]) -- partial application, second operand
Prelude> f 1
True
Prelude> f 4
False
Prelude> let g = (1 `elem`) -- partial application, first operand
Prelude> g [1,2]
True
Prelude> g [2,3]
False

所以,我不知道 Haskell,但是 (1 \elem`)elem 1` 是一样的吗? - Neil
4
我认为(++)可以理解为一个省略了两个输入的函数。 - Dan Burton

16

是的,这就是区段语法在起作用。

区段写为( op e )( e op ),其中op是二元运算符,e是一个表达式。区段是对于二元运算符的部分应用的方便语法。

下列等式成立:

(op e)  =   \ x -> x op e
(e op)  =   \ x -> e op x

5
所有中缀运算符都可以在 Haskell 的节中使用 - 除了 -,因为它与一元否定的奇怪性有关。这甚至包括通过使用反引号将非中缀函数转换为中缀的函数。您甚至可以将制作运算符成为正常函数的公式视为双面部分:

(x + y) -> (+ y) -> (+)

节(在某些罕见的角落情况下)被视为简单的 lambda。 (/ 2) 与以下内容相同:

\x -> (x / 2)

对于具有非交换运算符的示例,(2 /)\x -> (2 / x) 相同。
在这里没有什么理论上的深刻有趣的事情。它只是中缀运算符的部分应用的语法糖。通常会使代码变得更漂亮一些(当然也有反例)。

3
注意,“一元否定的奇怪现象”是由于不确定(- 1)应该被解释为数值文字-1还是函数\x -> x - 1。 Haskell创建者的选择是将其解释为数值文字,并提供函数subtract,满足subtract x = \y -> y - x。他们还提供了negate作为一元负号函数,即negate x = -x - Chris Taylor
@ChrisTaylor:差不多。(- x) 转换为 negate x,所以 (- 1)negate (fromInteger 1),而不是 fromInteger (-1)。当然,对于良好行为的 Num 实例,这两者应该是等价的。 - hammar
@hammar 哦,有点倒推逻辑。感谢您的纠正! - Chris Taylor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接