我目前正在开始使用Haskell(阅读Learn Yourself a Haskell),并遇到了类似于以下内容的行:
map (++"!") ["a", "b"] -- ["a!", "b!"]
map ("!"++) ["a", "b"] -- ["!a", "!b"]
为什么这是可能的,或者它是如何工作的?我无法对其他非交换运算做同样的事情,比如除法:
map (3/) [1..3] -- [3.0,1.5,1.0]
map ((/)3) [1..3] -- [3.0,1.5,1.0]
map (3(/)) [1..3] -- error
我觉得我在这里错过了一些东西,但是map
的实现并没有给我任何提示。
此代码无效:
map (3(/)) [1..3]
(/)
是前缀函数,但您将其用作中缀。编译器在您尝试函数3
(没有参数的函数)时看到它,(/)
添加为参数。
/
是中缀功能。因此,您可以执行以下操作:
map ( / 3) [1..3] -- [0.3333333333333333,0.6666666666666666,1.0]
map (3 / ) [1..3] -- [3.0,1.5,1.0]
这与map完全无关;map的参数可以是任何函数。
要了解您传递的功能,请查看此 GHCi 会话:
Prelude> :t (++"!")
(++"!") :: [Char] -> [Char]
Prelude> (++"!") "Hello"
"Hello!"
Prelude> ("!"++) "Hello"
"!Hello"
Prelude> :t ("!"++)
("!"++) :: [Char] -> [Char]
这里发生的是操作部分的句法思想(Haskell报告,第3.4节),可以理解为:
(x •) == (y. x • y)
(• x) == (y. y • x)
其中•
可以是任何操作,如++
,*
甚至是有趣的自定义运算符,如^_^
。
如果一个函数在括号中声明:(++) :: [a] -> [a] -> [a],它可以与它们一起使用,也可以不使用它们。如果不带括号使用,它们必须出现在参数之间:"!" ++ "?"
,但使用括号,它们就像普通函数一样:(++) "!" "?"
。
Haskell允许函数的"部分应用",因此("!"++)
与(++) "!"
或x -> (++) "!" x
相同,(++"?")
与x -> (++) x "?"
相同。("部分应用"用引号引起来,因为Haskell中的函数总是只有一个参数,所以应用不再是"部分"的;在其他语言中(++)将被视为两个参数的函数,所以当只应用一个参数时,该函数被视为部分应用 - 从这个意义上说,查看("!++) 作为部分应用 (++))
您的第二个示例是使用 (/) 的有效方式,但如果使用 (/),它实际上不再是中缀函数,因此尝试在函数名称之前将第一个参数指定为 (/) 时会出现错误:3(/)
。如果删除括号,它仍然有效:(3 /)
与((/) 3)
或(x -> (/) 3 x)
或(x -> 3 / x)
相同