带有排序和 (++) 的 Haskell 点运算符



我现在正在学习Haskell,并试图找出前缀,中缀,优先级等的所有规则。

在尝试实现一个附加两个列表并对它们进行排序的函数时,我从:

appendAndSort :: [a] -> [a] -> [a]
appendAndSort = sort . (++)

不编译。

以后:

appendAndSort :: Ord a => [a] -> [a] -> [a]
appendAndSort = (sort .) . (++)

另一方面确实有效。

为什么我必须在排序时添加第二个点并在其周围添加括号?

让我们从使用显式参数的版本开始。

appendAndSort x y = sort (x ++ y)

++写为前缀函数而不是运算符会产生

appendAndSort x y = sort ((++) x y)

知道(f . g) x == f (g x),我们可以确定f == sortg == (++) x

得到
appendAndSort x y = (sort . (++) x) y

这让我们可以通过 ETA 转换将y作为显式参数删除:

appendAndSort x = sort . (++) x

下一步是重复上面的过程,这次以(.)作为最顶层的运算符写成前缀函数,

appendAndSort x = (.) sort ((++) x)

然后用f == (.) sortg == (++)再次应用.的定义:

appendAndSort x = (((.) sort) . (++)) x

并通过ETA转换消除x

appendAndSort = ((.) sort) . (++)

最后一步是将(.) sort编写为运算符部分,我们完成了推导。

appendAndSort = (sort .) . (++)

表达式(f . g) x表示f (g x)

连贯地,(f . g) x y意味着f (g x) y.

请注意y如何作为第二个参数传递给f,而不是传递给g。结果不是f (g x y).

在您的情况下,(sort . (++)) x y意味着sort ((++) x) y,它将使用第一个参数(++) x(将列表x附加到其列表参数的函数)和第二个参数y调用sort。唉,这是错误的类型,因为sort只接受一个参数。

因此,这也是无效的

appendAndSort x y = (sort . (++)) x y

因此,这也是

appendAndSort = sort . (++)

相比之下,((f .) . g) x y确实按预期工作。让我们计算一下:

((f .) . g) x y
=  -- same reasoning as above, y is passed to (f.), not g
(f .) (g x) y
=  -- application associates on the left
((f .) (g x)) y
=  -- definition of `(f.)`
(f . (g x)) y
= -- definition of .
f ((g x) y)
=  -- application associates on the left
f (g x y)

所以这确实使y传递给g(而不是f)。

在我看来,"成语"(f .) . g不值得使用。有针对性x y -> f (g x y)阅读起来要简单得多,而且不是很长。


如果您确实需要,可以定义一个自定义组合运算符来处理双参数情况。

(.:) f g = x y -> f (g x y)

然后,你可以写

appendAndSort = sort .: (++)

最新更新