我正在阅读Scala函数式编程。
第3.10题说foldRight
会溢出(参见下面的图片)。
就我所知,然而,在Haskell中foldr
不会。
http://www.haskell.org/haskellwiki/
-- if the list is empty, the result is the initial value z; else
-- apply f to the first element and the result of folding the rest
foldr f z [] = z
foldr f z (x:xs) = f x (foldr f z xs)
-- if the list is empty, the result is the initial value; else
-- we recurse immediately, making the new initial value the result
-- of combining the old initial value with the first element.
foldl f z [] = z
foldl f z (x:xs) = foldl f (f z x) xs
这种不同的行为是如何实现的?这两种语言/编译器之间有什么区别,导致出现这种不同的行为?
这种差异来自哪里?平台?语言?编译器?
在Scala中是否可能编写一个堆栈安全的foldRight?如果是,怎么做?
foldr
不是尾递归的,因此仍然容易出现堆栈溢出错误。而在Haskell中与众不同的是惰性求值语义,有时允许foldr
不评估折叠操作符的两个参数。更多细节请参见:http://www.haskell.org/haskellwiki/Stack_overflow - Ionuț G. StanfoldRight
方法现在不易导致堆栈溢出,因为它在反转列表上调用foldLeft
方法,而foldLeft
方法甚至不是递归的。https://github.com/scala/scala/blob/v2.11.2/src/library/scala/collection/immutable/List.scala#L396-L397 https://github.com/scala/scala/blob/v2.11.2/src/library/scala/collection/LinearSeqOptimized.scala#L105-L114 - Ionuț G. StanfoldRight
问题票:https://issues.scala-lang.org/browse/SI-3295 - Ionuț G. StanfoldRight
和foldLeft
是严格的。在Haskell中,foldr'
和foldl'
也是严格的。但是在Haskell中,foldr
和foldl
是惰性的。因此,foldRight
、foldr'
和foldl
可能会溢出。 - vioriorPrelude
或Data.List
中没有foldr'
,因为它根本没有用处——将一个对第二个参数严格的函数传递给foldr
会得到严格的行为(如果列表很长可能会导致堆栈溢出)。 - dfeuer