为什么产品[0 ..]不能立即评估为0"?

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中的快捷方式的模式匹配可能是严格的.

  • 是.模式匹配严格; 如果必须检查`_ 0`,它必须确定第二个参数是否为零,这意味着它必须被评估.快捷方式必须位于第一个模式中. (6认同)
  • 不确定.它对我来说是'myProduct`:`让myProduct ab = case(a,b)of(0,_) - > 0; (_,0) - > 0; (x,y) - > x*y`后跟`foldr myProduct 1 [0 ..]`在ghci中产生0. (2认同)