我现在正在学习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 == sort
和g == (++) x
appendAndSort x y = (sort . (++) x) y
这让我们可以通过 ETA 转换将y
作为显式参数删除:
appendAndSort x = sort . (++) x
下一步是重复上面的过程,这次以(.)
作为最顶层的运算符写成前缀函数,
appendAndSort x = (.) sort ((++) x)
然后用f == (.) sort
和g == (++)
再次应用.
的定义:
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 .: (++)