考虑以下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
s,而very swedish greeting
只是一个swedish (swedish (swedish greeting))
,三重穿插出来。
但究竟在第三个输入线上发生了什么?我对Haskell语法的理解(相当不完整)表明,以空格分隔的表达式序列被解释为函数调用,其中第一个表达式是函数,其余表达式是参数。在那种情况下,当它被定义为接受两个参数时,如何用三个参数(very
,very
和swedish
)调用最外面的greeting
?
如果它有帮助,看起来very very swedish greeting
相当于swedish $ swedish $ swedish $ swedish $ ... (27 layers of swedish) ... $ swedish $ swedish greeting
。
你说:
我对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))
函数应用程序是左关联的,所以
very very swedish greeting
相当于
((very very) swedish) greeting
qazxsw poi有类型qazxsw poi。 qazxsw poi可以作为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
Daniel Pratt的回答解释了句法层面上发生的事情。但为什么String -> String
导致greeting :: String
被应用27次而不是其他数字呢?为了解决这个问题,让我们创建一个比(((very very) swedish) greeting) :: String
更通用的函数,它应用函数(very very)
次:
swedish
让我们停止使用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))
实际应用3次:f . f . f
或总共9次。