关于haskell:组合和功能应用之间的差异

Difference between composition and function application

本问题已经有最佳答案,请猛点这里访问。

99个haskell问题的第一个问题是"查找列表的最后一个元素"。我想出了两个解决方案:

解决方案1(这项工作)

1
2
myLast :: [a] -> a
myLast = head . reverse

解决方案2(不起作用)

1
2
myLast :: [a] -> a
myLast = head $ reverse

问题

  • 为什么解决方案1有效,而不是解决方案2?我最困惑的是两个实现如何提供模式匹配。


head是一个论点1的函数。如果你用f $ x把一个函数应用到某个东西上,这和简单地写f x是一样的(或者如果你喜欢f(x)(f)x是一样的……更丑):参数由指定的变量"填充"。因此,head $ reverse的结果只是head给你的任何结果,当用reverse类型的论点时……但这不起作用,因为head需要一个列表,而reverse是一个函数。$本身并不关心这个问题,只关心参数,例如您可以编写

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

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

(.)不同。这关系到右边的参数有什么类型(也必须是一个函数),并且它不会直接将它提供给左边的函数。相反,f . g生成另一个函数,它执行以下操作:等待x的某个参数,它将该参数反馈给g,然后将该参数的结果反馈给f

现在,如果你写

1
myLast' = head . reverse

也就是说,你把myLast定义为(.)给你的函数,它是headreverse的组合。这里没有提到关于myLast的参数,这并不重要:[a] -> a只是一种类型,所以您可以用这种类型定义变量(比如mylast),将它们分配给恰好具有这种函数类型的值(比如head . reverse)。如果需要,可以显式地设置参数:

1
myLast'' x = (head . reverse) x

请注意,之所以需要parens,是因为它被解析为head . (reverse x),这是不起作用的,因为reverse x不再是一个函数,只是一个结果列表。因此,你不能用head组合它;但是你可以做的是将head应用于它:

1
myLast''' x = head $ reverse x

实际上,haskell中的每个函数只有一个参数…但是我们对(+) :: Int -> Int -> Int这样的东西说"双参数函数",尽管实际上这是一个返回单参数函数的单参数函数,返回Int(+) :: Int -> (Int -> Int)


大致上,应用程序($用于函数与其参数(本例中为列表)之间,而组合(.用于两个函数之间。

关于模式匹配:在第一个解决方案中,函数reverse将为您执行所需的模式匹配,因此myLast不必执行。