Haskell函数组合(前向管道)-为什么这行得通?

21

以下代码中,fibseq 表示斐波那契数列中的一系列数字。 (来自解决欧拉计划题目 #2 的代码)

我定义了一个中缀函数 |>

(|>) x y = y x.

这使我能够像Unix管道一样执行以下操作:

take 34 fibseq |> filter even |> filter (< 4000000) |> sum

我的问题是,为什么这样做是有效的?

我本以为 take 34 fibseq |> filter even 应该转化为 filter (take 34 fibseq) even,这可能会导致类型错误。

但实际上它似乎转化为了 filter even (take 34 fibseq),这个方法可以正常工作并且就是我想要的,但我不明白它为什么有效。


20
顺便提一下,您也可以使用中缀符号定义函数:x |> f = f x - Tom Lokhorst
2个回答

24

函数应用(如filter even)的优先级比任何运算符都要高,因此您的代码等价于:

(take 34 fibseq) |> (filter even) |> (filter (< 4000000)) |> sum

1
默认的运算符优先级不是infixl 9吗?那么filter even的优先级是多少? - CMCDragonkai
我记得在Haskell中有两个事物,它们的中缀优先级分别是0和10,作为最低和最高的"不可达到的"中缀选项。问题是,结果证明中缀0是可以到达的,所以也许是函数的优先级是10?但是函数甚至通常都不是中缀!唉,我的记忆! - Noein

9
这是因为运算符优先级的原因。函数应用运算符、并置或(即空格)具有最高的优先级,因此take 34 fibseq |> filter even被解析为((take 34) fibseq) |> (filter even),等价于(filter even) ((take 34) fibseq);由于函数应用是从左到右结合的,因此这等价于filter even (take 34 fibseq)
通常情况下,任何二元运算符都可以通过声明其结合性来赋予优先级,例如:
infixl 0 |> 
infixr 9 .
lr表示操作是左结合还是右结合(即a • b • c分组为(a • b) • c还是a • (b • c));数字——介于0和9之间的整数——指定了优先级。较高的数字意味着更高的优先级(应用程序具有∞的有效优先级);例如,*/ 的优先级为7,+- 的优先级为6。要在ghci中检查运算符的优先级,只需在提示符处键入:info $(或其他运算符)。
另外需要注意的是:您的代码可以工作,但这不是我通常写代码的方式。如果你好奇,在Haskell中,我会使用$运算符编写该代码,它只执行函数应用,但优先级很低:filter even $ take 34 fibseq。如果我有更多的函数要应用,我会使用组合运算符:fun1 arg1 . fun2 . fun3 arg2 arg3 . filter even $ take 34 fibseq。它的阅读顺序与前面相反,但这是你通常在Haskell中找到的写法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接