考虑以下Haskell定义,取自this excellent Haskell video on YouTube:
import Data.List
greeting = "Hello"
swedish = intersperse 'f'
very f x = f (f (f x))
如果我们将它们加载到GHCi中,我们会看到以下结果:
ghci> swedish greeting
"Hfeflflfo"
ghci> very swedish greeting
"Hfffffffeffffffflffffffflfffffffo"
ghci> very very swedish greeting
"Hffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
fffffffffffffffffffffffffffff... (536,870,913 chars total)
前两个输出我完全理解。 swedish greeting
散布着f
,而very swedish greeting
只是swedish (swedish (swedish greeting))
,散布出来。{/ p>
但是第三个输入行究竟发生了什么?我对Haskell语法的理解(相当不完整)表明,以空格分隔的表达式序列被解释为函数调用,其中第一个表达式是函数,其余表达式是参数。在这种情况下,当只定义接受两个参数时,如何使用三个参数(very
,very
和swedish
)调用最外层的greeting
?
如果有帮助,则very very swedish greeting
似乎等同于swedish $ swedish $ swedish $ swedish $ ... (27 layers of swedish) ... $ swedish $ swedish greeting
。
答案 0 :(得分:11)
你说:
我对Haskell语法的理解(相当不完整)说,空格分隔的表达式序列被解释为函数调用,其中第一个表达式是函数,其余的是参数。
你是对的,这不是对实际情况的完整理解。从你的例子:
very very swedish greeting
这与:
相同((very very) swedish) greeting
这是因为函数应用程序是左关联的。此外,Haskell中的每个函数都接受一个输入并返回一个结果。您认为接受多个输入的函数实际上是接受单个输入并返回函数的函数。
这也解释了为什么箭头( - >)分隔函数输入和会导致函数类型。考虑++
:
(++) :: [a] -> [a] -> [a]
这与:
相同(++) :: [a] -> ([a] -> [a])
您可能会认为++
运算符占用两个列表并返回一个列表,但实际上它是一个输入(列表)的函数,它返回一个输入(另一个列表)的函数返回一个清单。
总之,你可以看到very very
本身就是一个有效的表达式(并且恰好与very
具有相同的类型)。
very very x
相当于:
very (very (very x))
答案 1 :(得分:3)
函数应用程序是左关联的,所以
very very swedish greeting
相当于
((very very) swedish) greeting
very
的类型为(t -> t) -> t -> t
。 very
可以作为very
very :: (t -> t ) -> t -> t
very :: (t -> t) -> (t -> t)
very very :: (t -> t) -> (t -> t)
very very
也是一个函数,它的类型为(t -> t) -> t -> t
。由于swedish
的类型为String -> String
,因此可以将其传递给very very
。结果函数的类型为String -> String
((very very) swedish) :: String -> String
类型为String -> String
的函数可以应用于greeting :: String
(((very very) swedish) greeting) :: String
答案 2 :(得分:1)
丹尼尔·普拉特(Daniel Pratt)的答案解释了句法层面上正在发生的事情。但是,为什么(very very)
导致swedish
被应用27次而不是其他次数?为了弄清楚这一点,让我们创建一个比very
更通用的函数,该函数应用一次n
次函数:
-- Takes number of times to apply `n` and a function `f`, and returns a
-- function that applies `f` `n` times.
appn n f
| n == 1 = f
| otherwise = f . (appn (n - 1) f)
-- If necessary, you could define very = (appn 3)
very = (appn 3)
让我们停止使用swedish
,而改为使用(+1)
,这样可以更轻松地计算出该功能被应用的次数:
Vandelay Industries> (appn 3) (appn 3) (+1) 0
27
Vandelay Industries> (appn 2) (appn 3) (+1) 0
9
Vandelay Industries> (appn 2) (appn 4) (+1) 0
16
Vandelay Industries> (appn 4) (appn 10) (+1) 0
10000
Vandelay Industries> (appn 2) (appn 2) (appn 2) (+1) 0
16
您现在可能会看到该模式。 (appn x) (appn y)
生成与appn $ y ^ x
等效的内容。但为什么?考虑(appn 2) (appn 3)
。 (appn 3)
表示f (f (f x))
或f . f . f
。将其放入(appn 2)
,实际上每个f
被应用3次:(f . f . f) . (f . f . f) . (f . f . f)
或总共9次。