Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/fortran/2.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
foldR Tail在Haskell中是递归的吗?_Haskell_Optimization_Ghc_Tail Recursion_Fold - Fatal编程技术网

foldR Tail在Haskell中是递归的吗?

foldR Tail在Haskell中是递归的吗?,haskell,optimization,ghc,tail-recursion,fold,Haskell,Optimization,Ghc,Tail Recursion,Fold,我是Haskell的新手,读Haskell的第一原理, 在第384页的折叠章节中,我遇到了FoldR,似乎它的尾部不是递归的 foldr :: (a -> b -> b) -> b -> [a] -> b foldr f z [] = z foldr f z (x:xs) = f x (foldr f z xs) 1-我们能让它成为一个递归的尾巴吗 2-这样做会被优化吗?foldr不是尾部递归的。 有时它被称为真正的“递归”折叠,而左折叠是“迭代的”(因为尾部递归

我是Haskell的新手,读Haskell的第一原理, 在第384页的折叠章节中,我遇到了FoldR,似乎它的尾部不是递归的

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f z [] = z
foldr f z (x:xs) = f x (foldr f z xs)
1-我们能让它成为一个递归的尾巴吗


2-这样做会被优化吗?

foldr
不是尾部递归的。 有时它被称为真正的“递归”折叠,而左折叠是“迭代的”(因为尾部递归,它相当于迭代)


请注意,在Haskell中,由于懒惰的原因,
foldl
也不能保证常量空间,这就是为什么在Haskell这样的懒惰语言中存在
foldl'
,尾部递归通常是个坏主意。这就是其中之一

我们可以尝试使
foldr
尾部递归,例如从
reverse
开始(以尾部递归的方式也是可行的),然后以尾部递归的方式逐步累积
foldr
结果。然而,这将打破
foldr
的语义

使用标准
foldr
,我们有

foldr (\_ _ -> k1) k2 (x:xs) = k1
无论
xs
是什么,包括底部值(如
undefined
)或无限列表(如
[0..]
)。此外,当
xs
是一个有限但很长的列表时,上面的代码也是有效的,因为它在不扫描整个列表的情况下立即停止计算

作为一个更实际的例子

and :: [Bool] -> Bool
and = foldr (&&) True
xs
的某些元素计算为
False
时,使
和xs
返回
False
,而不扫描列表的其余部分

最后,将
foldr
转换为尾部递归函数将:

  • 在处理部分定义的列表(
    1:2:undefined
    )或无限列表(
    [0..]
    )时更改语义
  • 在有限长度的列表上效率较低,即使没有必要,也必须对其进行完全扫描

    • foldr
      不是尾部递归。。。但它可以用来编写在常量空间中处理列表的函数。迟浩田已经指出,它可以有效地实现
      。下面是它如何实现一个高效的列表求和函数:

      mySum :: Num a => [a] -> a
      mySum xs = foldr go id xs 0
        where
          go x r acc = r $! x + acc
      
      这工作怎么样?考虑<代码> MySoM[1,2,3] < /代码>。这扩展到

      foldr go id [1,2,3] 0
      ==> -- definition of foldr
      go 1 (foldr go id [2,3]) 0
      ==> -- definition of go
      foldr go id [2,3] $! 1 + 0
      ==> -- strict application
      foldr go id [2,3] 1
      
      我们已经将列表大小减少了一个,并且没有在“堆栈”上积累任何内容。同样的过程重复,直到我们得到

      foldr go id [] 6
      ==> definition of foldr
      id 6
      ==> definition of id
      6
      


      注意:如果GHC编译此代码时启用了优化(
      -O
      -O2
      ),那么它实际上会将其转换为炽热的快尾递归代码,而无需您的进一步帮助。但即使未优化,它也可以正常工作,不会消耗大量内存/堆栈。

      foldr
      本身不是尾部递归,但根据
      f
      ,可能是尾部递归。Haskell中的尾部递归非常微妙,因为它的计算很慢

      例如,考虑<代码> f=(& &)。在这种情况下,我们有

      foldr(&&)acc lst=
      第1宗
      []->acc
      (x:xs)->x&&foldr(&&&)acc xs
      = 
      第1宗
      []->acc
      (x:xs)->如果x
      然后foldr(&&)acc xs
      否则错误
      =
      第1宗
      []->acc
      (x:xs)->
      True->foldr(&&)acc xs
      假->假
      
      注意,在本例中,我们清楚地看到
      foldr(&&)
      是尾部递归的。实际上,
      foldr(| |)
      也是尾部递归的。请注意,
      foldr(&&)
      是尾部递归的,这基本上是因为懒惰。如果不是因为懒惰,在将结果代入
      x&&foldr(&&&)acc xs
      之前,我们必须评估
      foldr(&&&)acc xs
      。但由于懒惰,我们首先评估
      x
      ,然后才确定是否需要调用
      foldr(&&&)acc xs
      ,无论何时调用,都是尾部调用


      但是,在大多数情况下,
      foldr f
      将不是尾部递归函数。特别是,
      foldr((+)::Int->Int->Int)
      不是尾部递归的。

      你能给我fold的引用吗?@hanan'这里他们定义了foldr,使用continuation(对我来说,它就像返回一个函数一样)在haskell中使用它是否经过优化?或者它也会破坏语义?@hanan你认为“优化”是什么意思?@hanan你在比较苹果和橙子。我的答案考虑了像Haskell这样的懒惰语言,而你提到的答案是关于F#,它是渴望的,所以语义是不同的。由于这种差异,我们无法对两种语言应用相同的推理:一种语言中的良好实践很容易成为另一种语言中的不良实践,反之亦然。^^^^^具体来说,F#和Haskell中的这种“CPS”技术首先从n长列表构建一个depth-n闭包,然后将其应用于初始值(在您链接的答案中称为
      acc
      )。然后,在严格的语言中,执行n个应用程序链,从下到上构建结果,这是可以的。但在惰性语言中,应用程序
      cont{func x r}
      不会减少(即计算)应用程序
      func x r
      ——相反,它将按原样传递到
      cont
      。其效果是另外两个无关的遍历,其中一个在堆栈上,可能会导致堆栈溢出…@hanan…补救方法是使用
      (\r->cont$!func x r)
      作为每一步的延续,以获得所需的严格性。@Hana不知道为什么人们会投反对票,因为这是一个完全合理的问题。但是,正如答案正确指出的那样,“尾部递归”在Haskell中并不是真的那么重要。你更担心thunks的存储空间(这就是为什么
      foldl
      客观上比
      foldl'
      更糟糕的一个重要原因,例如,尽管可以以简单的方式递归实现这两个尾部。Don