0

我现在正在学习haskell,并试图弄清楚前缀、中缀、优先级等的所有规则。

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

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

没有编译。

下列的:

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

另一方面确实有效。

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

4

2 回答 2

5

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

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

下一步就是重复上面的过程,这次用(.)as the top most operator 写成前缀函数,

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

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

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

x通过 eta 转换消除

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

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

appendAndSort = (sort .) . (++)
于 2022-01-21T20:23:05.920 回答
4

表达的(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, 它将sort使用第一个参数(++) x(将列表x添加到其列表参数的函数)和第二个参数调用y。唉,这是错误的类型,因为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 .: (++)
于 2022-01-21T20:11:53.483 回答