Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么Haskell';s foldr不是stackoverflow,而Scala实现是?_Scala_Haskell_Functional Programming_Fold - Fatal编程技术网

为什么Haskell';s foldr不是stackoverflow,而Scala实现是?

为什么Haskell';s foldr不是stackoverflow,而Scala实现是?,scala,haskell,functional-programming,fold,Scala,Haskell,Functional Programming,Fold,我正在读书 练习3.10说明foldRight溢出(见下图)。 但据我所知,Haskell中的foldr却没有 这种不同的行为是如何可能的 导致这种不同行为的两种语言/编译器之间的差异是什么 这种差异从何而来?站台?语言?编译器 可以在Scala中编写堆栈安全foldRight吗?如果是,如何进行 哈斯克尔很懒。因此foldr在堆上分配,而不是在堆栈上。根据参数函数的严格程度,它可以分配单个(小)结果,也可以分配大结构 与严格的尾部递归实现相比,您仍然在损失空间,但这看起来并不明显,因为您已

我正在读书

练习3.10说明
foldRight
溢出(见下图)。
但据我所知,Haskell中的foldr却没有

这种不同的行为是如何可能的

导致这种不同行为的两种语言/编译器之间的差异是什么

这种差异从何而来?站台?语言?编译器

可以在Scala中编写堆栈安全foldRight吗?如果是,如何进行


哈斯克尔很懒。因此
foldr
在堆上分配,而不是在堆栈上。根据参数函数的严格程度,它可以分配单个(小)结果,也可以分配大结构


与严格的尾部递归实现相比,您仍然在损失空间,但这看起来并不明显,因为您已经用堆栈交换了堆。

Haskell很懒惰。定义

foldr f z (x:xs) = f x (foldr f z xs)
告诉我们,具有非空列表的
foldr f z xs
的行为是由组合函数
f
的惰性决定的

特别是调用
foldr f z(x:xs)
只在堆上分配一个thunk,
{foldr f z xs}
(为包含表达式的thunk写入
{…}
),并使用两个参数调用
f
,以及thunk。接下来发生的事情是
f
的责任

特别是,如果它是一个惰性数据构造函数(例如,
(:)
),它将立即返回给
foldr
调用的调用者(构造函数的两个插槽由(引用)这两个值填充)

如果
f
确实需要其右边的值,则在最小的编译器优化下,不应创建任何thunk(或者最多创建一个thunk-当前thunk),因为立即需要
foldr f z xs
的值,并且可以使用通常的基于堆栈的求值:

foldr f z [a,b,c,....,n] ==
    a `f` (b `f` (c `f` (... (n `f` z)...)))

因此,当与超长输入列表上的严格组合函数一起使用时,foldr确实会导致这种情况。但是,如果组合函数不立即要求其右侧的值,或者只要求其一部分,则计算将暂停,并立即返回由
f
创建的部分结果。与左边的参数相同,但它们可能在输入列表中以thunk的形式出现。

请注意,这里的作者并不是指scala标准库中的任何foldRight定义,例如列表中定义的foldRight定义。他们参考了上文第3.4节中给出的foldRight定义

scala标准库根据foldLeft定义foldRight,方法是反转列表(可以在常量堆栈空间中完成),然后调用foldLeft并反转传递函数的参数。这适用于列表,但不适用于无法安全反转的结构,例如:

scala> Stream.continually(false)
res0: scala.collection.immutable.Stream[Boolean] = Stream(false, ?)

scala> res0.reverse
java.lang.OutOfMemoryError: GC overhead limit exceeded
现在让我们思考一下此操作的结果:

Stream.continually(false).foldRight(true)(_ && _)
答案应该是假的,不管流中有多少假值,或者它是无限的,如果我们将它们与一个连词组合,结果将是假的

haskell当然可以毫无问题地得到这个:

Prelude> foldr (&&) True (repeat False)
False
这是因为两件重要的事情:haskell的foldr将从左到右而不是从右到左遍历流,而haskell在默认情况下是懒惰的。这里的第一项,foldr实际上从左到右遍历列表,可能会让一些认为右折叠是从右开始的人感到惊讶或困惑,但右折叠的重要特征不是它从结构的哪一端开始,而是关联性在哪个方向。因此,给出一个列表[1,2,3,4]和一个名为
op
,左折为

((1 op 2) op 3) op 4)
右边的褶皱是

(1 op (2 op (3 op 4)))
但评估的顺序不重要。因此,作者在第3章中所做的是给你一个从左到右遍历列表的折叠,但是由于scala默认是严格的,我们仍然无法遍历我们的无限错误流,但是有一些耐心,他们会在第5章中做到:)我会给你一个偷看,让我们看看标准库中定义的foldRight与scalaz中可折叠typeclass中定义的foldRight之间的区别:

下面是scala标准库的实现:

def foldRight[B](z: B)(op: (A, B) => B): B
以下是scalaz的可折叠文件的定义:

def foldRight[B](z: => B)(f: (A, => B) => B): B
不同之处在于Bs都是惰性的,现在我们可以再次折叠无限流,只要我们给出一个第二个参数足够惰性的函数:

scala> Foldable[Stream].foldRight(Stream.continually(false),true)(_ && _)
res0: Boolean = false

在Haskell中演示这一点的一个简单方法是使用等式推理来演示惰性评估。让我们根据
foldr
编写
find
函数:

-- Return the first element of the list that satisfies the predicate, or `Nothing`.
find :: (a -> Bool) -> [a] -> Maybe a
find p = foldr (step p) Nothing 
    where step pred x next = if pred x then Just x else next

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f z []     = z 
foldr f z (x:xs) = f x (foldr f z xs)
在一种急切的语言中,如果您使用
foldr
编写
find
,它将遍历整个列表并使用O(n)空间。对于惰性计算,它在满足谓词的第一个元素处停止,并且只使用O(1)空间(模垃圾收集):

尽管列表
[0..]
是无限的,但此计算会在有限的步骤中停止,因此我们知道我们不会遍历整个列表。此外,每一步表达式的复杂度都有一个上限,这将转化为计算该值所需内存的恒定上限

这里的关键是我们正在折叠的
step
函数具有以下属性:无论
x
next
的值是什么,它都将:

  • 计算为
    仅x
    ,而不调用
    下一个
    thunk,或
  • Tail调用
    next
    thunk(实际上,如果不是字面意思的话)

  • Haskell中的foldr
    不是尾部递归的,所以我
    -- Return the first element of the list that satisfies the predicate, or `Nothing`.
    find :: (a -> Bool) -> [a] -> Maybe a
    find p = foldr (step p) Nothing 
        where step pred x next = if pred x then Just x else next
    
    foldr :: (a -> b -> b) -> b -> [a] -> b
    foldr f z []     = z 
    foldr f z (x:xs) = f x (foldr f z xs)
    
    find odd [0..]
        == foldr (step odd) Nothing [0..]
        == step odd 0 (foldr (step odd) Nothing [1..])
        == if odd 0 then Just 0 else (foldr (step odd) Nothing [1..])
        == if False then Just 0 else (foldr (step odd) Nothing [1..])
        == foldr (step odd) Nothing [1..]
        == step odd 1 (foldr (step odd) Nothing [2..])
        == if odd 1 then Just 1 else (foldr (step odd) Nothing [2..])
        == if True then Just 1 else (foldr (step odd) Nothing [2..])
        == Just 1