如何处理共同递归?

m09*_*m09 17 recursion haskell

好的,基本上我知道选项1或2是否适用于以下情况:

naturals = 0 : map (+ 1) naturals
Run Code Online (Sandbox Code Playgroud)

选项包括:
1.执行很糟糕,每一步都重新计算:

naturals     = [0]
naturals'    = 0:map (+ 1) [0]          // == [0, 1]
naturals''   = 0:map (+ 1) [0, 1]       // == [0, 1, 2]
naturals'''  = 0:map (+ 1) [0, 1, 2]    // == [0, 1, 2, 3]
naturals'''' = 0:map (+ 1) [0, 1, 2, 3] // == [0, 1, 2, 3, 4]
Run Code Online (Sandbox Code Playgroud)

2.执行不是很糟糕,列表总是无限的,map只应用一次

naturals     = 0:something
                                  |
naturals'    = 0:      map (+ 1) (0:      something)
                                    |
naturals''   = 0:1:    map (+ 1) (0:1:    something')
                                      |
naturals'''  = 0:1:2:  map (+ 1) (0:1:2:  something'')
                                        |
naturals'''' = 0:1:2:3:map (+ 1) (0:1:2:3:something''')
Run Code Online (Sandbox Code Playgroud)

|指示其中map是在其执行.

我知道答案可能只有12但我也很欣赏一些关于共同递归的好解释的指示,以清除最后的疑惑:)

Ste*_*ans 32

正如你所说,执行不会是"可怕的".:)懒惰的评价是你最好的朋友.懒惰是什么意思?

  1. 在真正需要结果之前,不对事情进行评估;
  2. 事情最多只评估一次.

这里的"事物"是"尚未评估的表达",也称为"thunk".

这是发生的事情:

你定义

naturals = 0 : map (+1) naturals
Run Code Online (Sandbox Code Playgroud)

仅仅定义naturals并没有引入评估它的需要,所以最初naturals只会指出未评估表达式的一个thunk 0 : map (+1) naturals:

naturals = <thunk0>
Run Code Online (Sandbox Code Playgroud)

在某些时候,你的程序可能会模仿自然的匹配.(模式匹配本质上是唯一强制在Haskell程序中进行评估的东西.)也就是说,你的程序需要知道自然是空列表还是头元素后跟尾列表.这是你定义的右侧将被评估,但只需要尽可能找出是否naturals是通过构造[](:):

naturals = 0 : <thunk1>
Run Code Online (Sandbox Code Playgroud)

现在,自然会指向构造函数(:)在head元素上的应用,0以及仍未评估的尾部的thunk.(实际上,头部元素也将仍然没有评估,所以真的naturals会指向某种形式<thunk> : <thunk>,但我将把这个细节留下来.)

直到你的程序中的某个后期点,你可以在尾部模式匹配,尾部的thunk被"强制",即评估.这意味着map (+1) naturals要评估表达式.评估此表达式会减少map模式匹配naturals:它需要知道是否naturals[]或构造(:).我们看到,此时,不是指向thunk,naturals而是指向应用程序(:),因此这种模式匹配map不需要进一步评估.应用程序map确实立即看到足以naturals弄清楚它需要生成(:)自己的应用程序,因此它确实:map生成1 : <thunk2>thunk包含表单的未评估表达式map (+1) <?>.(再说一遍,1我们实际上有一个thunk 0 + 1.)<?>指向什么?嗯,naturals正好map是生产的尾巴.因此,我们现在有

naturals = 0 : 1 : <thunk2>
Run Code Online (Sandbox Code Playgroud)

<thunk2>含有尚未未计算的表达map (+1) (1 : <thunk2>).

在你的程序的稍后阶段,模式匹配可能会强制<thunk2>,所以我们得到

naturals = 0 : 1 : 2 : <thunk3>
Run Code Online (Sandbox Code Playgroud)

<thunk3>含有尚未未计算的表达map (+1) (2 : <thunk3>).等等.

  • @TikhonJelvis是的,除非程序强制要求评估元素.这就是懒惰评估程序的空间复杂性有时难以推理的原因:即使经验Haskell程序员有时会对无辜的程序的内存要求感到惊讶.这就是为什么我们有`seq`之类的东西. (3认同)
  • 因此,每一个元素都将是一堆由一堆新增内容构成的更大的元素? (2认同)