Ser*_*lla 16 haskell lazy-evaluation
我想要了解懒惰.因为0乘以任意数字是0,不应该product [0..]评估为0?我也试过foldl (*) 1 [0..],并将我自己的产品定义为
myProduct 0 _ = 0
myProduct _ 0 = 0
myProduct a b = a*b
Run Code Online (Sandbox Code Playgroud)
为什么一旦找到0,折叠就会停止?
Yan*_*ier 21
因为乘法运算符不知道它被链接,并且fold函数不知道乘法运算符对任何参数的特定行为.通过这种组合,它需要耗尽清单才能完成折叠.事实上,由于这个原因,foldl在无限列表中根本不起作用.foldr,因为它可以从列表的头部扩展功能.
foldl (*) 1 [0..] -> (((..(((1*0)*1)*2)*3....)*inf
Run Code Online (Sandbox Code Playgroud)
无法找到foldl情况下的最外层乘法,因为列表是无限的.因此,它不能遵循链条得出结论为零.它可以并且确实在列表中计算产品,并且该产品恰好保持为零,但它不会终止.如果您使用scanl,则可以看到这些中间产品.
foldr (*) 1 [0..] -> 0*(1*(2*(3*((...((inf*1)))...)))
Run Code Online (Sandbox Code Playgroud)
在foldr情况下最外面的乘法是立即找到的,因为列表的其余部分实际上是一个懒惰的thunk.它只运行一步:
foldr (*) 1 [0..] -> 0*(foldr (*) 1 [1..])
Run Code Online (Sandbox Code Playgroud)
因此,myProduct如果第一个参数为零,foldr myProduct 1 [0..]则自定义乘法运算符在第二个参数中不严格,可以终止.
作为旁注,前奏产品功能仅限于有限列表(并且可以使用foldl实现).即使它使用了foldr,它也可能不会快捷,因为标准乘法运算符是严格的; 否则在产品既不为零也不链接的常见情况下,计算成本会很高.
-- 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
Run Code Online (Sandbox Code Playgroud)
另外,有一个原因是它不使用foldr; 正如我们在扩展和scanl函数中看到的那样,左侧折叠可以在消耗列表时进行计算.如果操作符没有快捷方式,右侧折叠需要构建一个与列表本身一样大的表达式,甚至可以开始计算.这种差异是因为它是在严格情况下开始计算的最内层表达式,但是产生结果的最外层表达式允许惰性情况.Haskell wiki中的Lazy与非严格可能比我能更好地解释,甚至提到用于描述myProduct中的快捷方式的模式匹配可能是严格的.