例如:
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;功能。
有人可以从哈斯克尔和伊德里斯的位置向我解释额外模式的政治,这样我就能理解并看到差异。
答案 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中的相同。可以基于证明函数属性的容易性来选择使用哪个,或者可以基于简单性。