haskell – 为什么产品[0 ..]评估为0“瞬间”?

我正在努力理解懒惰.因为0乘以任何数字是0,不应该产品[0 ..]计算为0?我也试过折叠(*)1 [0 ..],并定义我自己的产品

myProduct 0 _ = 0
myProduct _ 0 = 0
myProduct a b = a*b

一旦找到0,为什么折叠停止?

因为乘法运算符不知道它被链接,并且fold函数不知道乘法运算符对于任何参数的特定行为.通过这种组合,它需要耗尽列表来完成折叠.事实上,由于这个原因,foldl在无限列表上根本不起作用. foldr是,因为它可以从列表的头部扩展功能.

foldl (*) 1 [0..] -> (((..(((1*0)*1)*2)*3....)*inf

因为列表是无限的,所以foldl的最外面的乘法是永远无法找到的.因此,不能跟踪链结果结果为零.它可以,并且可以计算列表中的产品,该产品恰好保持为零,但不会终止.如果你使用scanl,你可以看到这些中间产品.

foldr (*) 1 [0..] -> 0*(1*(2*(3*((...((inf*1)))...)))

由于剩下的列表实际上是一个懒惰的thunk,所以在foldr的情况下最外面的乘法被立即找到.它只运行一步:

foldr (*) 1 [0..] -> 0*(foldr (*) 1 [1..])

因此,如果第一个参数为零,则您的自定义乘法运算符myProduct在第二个参数中不严格,foldr myProduct 1 [0 ..]可以终止.

作为附注,前奏产品功能仅限于有限列表(可以使用foldl实现).即使它使用折叠,它可能不会快捷,因为标准乘法运算符是严格的;否则,在产品既不零也不链接的常见情况下,其计算成本将是昂贵的.

-- sum and product compute the sum or product of a finite list of numbers.

sum, product     :: (Num a) => [a] -> a
sum              =  foldl (+) 0  
product          =  foldl (*) 1

另外还有一个原因,它不使用foldr;正如我们在扩展和scanl函数中可以看到的,左折叠可以在消费列表时计算.如果运算符不快捷,右边的折叠需要建立与列表本身一样大的表达式,以便开始计算.这个差异是因为它是在严格的情况下开始计算的最内层的表达式,而是产生结果的最外层表达式,允许懒惰的情况.在Haskell维基中的Lazy vs. non-strict可能会比我更好的解释,甚至提到你用来描述myProduct中快捷方式的模式匹配可以是严格的.

http://stackoverflow.com/questions/30502941/why-doesnt-product-0-evaluate-to-0-instantly

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:haskell – 为什么产品[0 ..]评估为0“瞬间”?