我应该在哪里以及为什么要使用额外的空图案?

时间:2015-04-22 09:36:07

标签: haskell pattern-matching idris

例如:

intersectBy : (a -> a -> Bool) -> List a -> List a -> List a
intersectBy _  [] _     =  []
intersectBy _  _  []    =  []
intersectBy eq xs ys    =  [x | x <- xs, any (eq x) ys]

[]有额外的模式,看起来它们在Haskell Data.List中使用,但是那是什么样的优化?和伊德里斯在哪里有区别?

我问,因为我听说过#34;它会让它变得更难以推理#34;和我说我没时间充分解释它的人。

我怀疑我是否能理解它&#34;减少证据&#34;功能。

有人可以从哈斯克尔和伊德里斯的位置向我解释额外模式的政治,这样我就能理解并看到差异。

4 个答案:

答案 0 :(得分:13)

从语义上讲,模式

intersectBy _  [] _     =  []
即使从性能的角度来看,

看起来也是多余的。相反,在Haskell中

intersectBy _  _  []    =  []

不是多余的,否则

intersectBy (==) [0..] []

会发散,因为理解会尝试尝试所有元素x <- [0..]

但是,我不确定我是否喜欢这种特殊情况。为什么我们不应该添加一个涵盖intersectBy (==) [0..] [2]的特殊案例,以便它返回[2]?此外,如果考虑性能,在许多情况下我希望通过预排序使用O(n log n)方法,即使这不适用于无限列表并且需要Ord a。< / p>

答案 1 :(得分:11)

您无需猜测何时可以通过git blame,GHC Trac和图书馆邮件列表查找历史记录。

最初的定义只是第三个等式,

intersectBy eq xs ys    =  [x | x <- xs, any (eq x) ys]

https://github.com/ghc/ghc/commit/8e8128252ee5d91a73526479f01ace8ba2537667中,第二个等式被添加为严格/性能改进,同时,添加了第一个等式,以使新定义始终至少与原始定义一样。否则,intersectBy f [] _|__|_之前为[]

在我看来,这个当前的定义现在最大程度上是懒惰的:它对于任何输入都是尽可能定义的,除了必须首先选择是否检查左侧或右侧列表的空白。 (并且,正如我上面提到的,这种选择与历史定义一致。)

答案 2 :(得分:5)

@chi解释了if案例,但-initWithCoder:也有用:它决定_ _ [] handles bottom的方式。定义如下:

_ [] _

删除第一个模式,它变为:

intersectBy

我不是百分之百确定这一点,但我相信在第一种模式中没有绑定任何东西都有性能优势。最终模式将为λ. intersectBy undefined [] undefined [] λ. intersectBy (==) undefined [] *** Exception: Prelude.undefined 提供相同的结果,而不是评估 λ. intersectBy undefined undefined [] [] λ. intersectBy (==) [] undefined *** Exception: Prelude.undefined xs == [],但AFAIK仍然会allocates stack space为他们的thunk。

答案 3 :(得分:4)

在伊德里斯有很大的不同:伊德里斯列表总是有限的!此外,Idris是一种最严格的(按值调用)语言,并且可选地使用整体检查器,因此假设不存在隐藏在参数列表中的任何底部是非常合理的。这种差异的重要性在于,Idris中的两个定义在语义上几乎与Haskell中的相同。可以基于证明函数属性的容易性来选择使用哪个,或者可以基于简单性。