组合和函数应用的区别

Jon*_*Jon 1 haskell

99 Haskell Problems 中的第一个问题是“找到列表的最后一个元素”。我想出了两个解决方案:

解决方案 1(这有效)

myLast :: [a] -> a
myLast = head . reverse
Run Code Online (Sandbox Code Playgroud)

解决方案2(这不起作用)

myLast :: [a] -> a
myLast = head $ reverse
Run Code Online (Sandbox Code Playgroud)

  • 为什么解决方案 1 有效,但解决方案 2 无效?我最困惑的是这两种实现都没有提供模式匹配。

lef*_*out 5

head是一个参数1的函数。如果您f $ x习惯将函数应用于某事,则与简单地书写相同f x(或者,如果您喜欢f(x), 或(f)x,所有相同的事情...只是更丑):参数由指定的变量“填充”。因此,当使用's 类型的参数喂食时,结果head $ reverse将只是head给你的任何结果reverse... ...然而这不起作用,因为head需要一个列表但它reverse是一个函数。$本身并不关心这个,而只是处理参数,例如你可以写

Prelude> :t map $ reverse
map $ reverse :: [[a]] -> [[a]]

因为第一个参数map实际上是一个函数。

有了(.)它就不一样了。这关心它右边的参数有什么类型(也必须是一个函数),它不会简单地立即将它提供给左边的函数。相反,f . g产生另一个函数,它执行以下操作:它等待一些参数x,它提供给g,然后将结果提供f

现在,如果你写

myLast' = head . reverse
Run Code Online (Sandbox Code Playgroud)

它的意思只是,你定义myLast为这个功能是(.)让你为一体的组成headreverse。这里没有提到的参数myLast并不重要:[a] -> a只是某种类型,因此您可以定义具有这种类型的变量(如 myLast),通过将它们分配给碰巧具有此类函数类型(如head . reverse)的值。如果需要,您可以使参数显式:

myLast'' x = (head . reverse) x
Run Code Online (Sandbox Code Playgroud)

请注意,括号是必需的,否则它会被解析为head . (reverse x)– 这将不起作用,因为reverse x它不再是一个函数,只是一个结果列表。因此,你不能构成head; 然而,你可以做的是适用 head于它:

myLast''' x = head $ reverse x
Run Code Online (Sandbox Code Playgroud)

1事实上,Haskell 中的每个函数都只有一个参数……但是我们说“双参数函数”,例如(+) :: Int -> Int -> Int,尽管实际上这是一个单参数函数,返回一个单参数函数,返回一个Int: (+) :: Int -> (Int -> Int)