自我成长清单的Haskell递归清单

时间:2018-07-18 22:38:06

标签: haskell where-clause tail-recursion

我在Contract.hs的第147行Pricing Financial Contracts with Haskell中找到了以下代码:

konstSlices :: a -> [[a]]
konstSlices x = nextSlice [x]
    where nextSlice sl = sl : nextSlice (x:sl)

这将产生一个无限的列表列表:

konstSlices 100 = [[100],[100,100],[100,100,100],...]

我不确定where子句中发生了什么。如果我们只进行3次迭代,那么此时nextSlice内应该是什么

  

[100]:[100,100]:nextSlice (100 :[100,100])吗?

终止符:[]的显示方式如何将列表打包在列表[100]:[100,100]:[100,100,100]:[] = [[100],[100,100],[100,100,100]]中 递归构造真的很难遵循。我很好奇,是否有工具可以遵循这样的迭代并查看如何构建这些值?实际上,在这种情况下,我使用笔和纸来掌握正在发生的事情。递归列表并不是最坏的情况。(使我想到这个问题的是,对at t函数(第130行)的分析是在其他一些较小的函数或数据的基础上,在应用函数中包含了liftA2的内容函数类型的构造函数,您很快就会看到大量相互关联的计算在增长,并且您完全迷失了-洗脑了。.

4 个答案:

答案 0 :(得分:2)

这对您来说更简单

Prelude> let ones = 1 : ones
Prelude> take 3 ones
[1,1,1]

ones被定义为1的无限列表。没有尽头,所以没有最终的空列表构造函数。 take n启动第一个n元素的生成,此处为n=3

答案 1 :(得分:1)

karakfa很好地说明了这里发生的事情,但我会做一点扩展。

没有任何结局]。列表是一种数据结构,其头是数据项,而尾是列表。此外,Haskell中的对象会延迟评估

让我们再看一下这个例子:

konstSlices :: a -> [[a]] 
konstSlices x = nextSlice [x]
    where nextSlice sl = sl : nextSlice (x:sl)

惰性评估意味着,如果您尝试使用konstSlices 100,则该程序将仅计算所需数量的列表项。因此,如果您take 1 (konstSlices 100),该程序将进行计算

konstSlices 100 = [100]:
  nextSlice (100:[100]))

列表的尾部,[100]:之后的所有内容,都存储为 thunk 。其值尚未计算。

如果您要求take 2 (konstSlices 100)怎么办?然后,程序需要计算重击,直到找到第二个元素。这就是它所需要的,所以它将在到达时停止,

    konstSlices 100 = [100]:
      [100,100]:
      (nextSlice (100:[100,100]))

以此类推,对于许多条目,您都需要计算。

没有任何内容可以与右方括号相对应。不需要。 konstSlices的递归定义永远不会产生类似的东西,只会产生更多的重击。那是允许的。

另一方面,如果尝试使用length (konstSlices 100),则程序将尝试生成无限数量的节点,用尽内存并崩溃。如果您尝试计算整个{strong> circular 列表(如xs = 1:xs),则无需分配任何新节点,因为它链接回相同的节点,并且不会不需要生成新的堆栈帧,因为它是尾递归模态,因此会陷入无限循环。

答案 2 :(得分:0)

逻辑很简单

konstSlices :: a -> [[a]]
konstSlices x = nextSlice [x]
    where nextSlice sl = sl : nextSlice (x:sl)

如果x = 100然后nextSlice将返回[100],则它将递归获取第一个元素并将其附加到上一个列表nextSlice (x:sl),在这种情况下,sl将每次通话都会增长,并按列表索引排序

0 -> [100] = [100]
1 -> [100,100] = nextSlice (100:[100])
2 -> [100 ,100,100] = nextSlice (100:[100 ,100])
3 -> [100 ,100 , 100,100] = nextSlice (100:[100 , 100,100])

,此过程将继续, x:sl sl列表的第一个元素是x。 并且每个nextSlice调用都将返回一个列表。 sl:sl:sl ...

  

与此acc = 1

相同
num = 100 : num
slice acc = take acc num :slice (acc+1)

答案 3 :(得分:0)

您的代码仅供参考。

每次迭代都会显示一个带有一个值的列表,以便您获得

[100]
[100,100]
[100,100,100]

您想要多次。列出的每个列表都是全新的列表。 Haskell每次迭代时都将一个值添加到上一个列表中,从而构建一个新列表。递归函数的作用是在下一个值中使用前一个值。参见下面的第一个示例。

在实际程序中,您可能对列表的构建不感兴趣,而仅对最终结果感兴趣。

Haskell具有可帮助您在构建列表时查看列表的功能。该函数可以概括原始递归,因此可以使用您可能需要的所有递归函数中的90%。

功能为foldl / foldrscanl / scanr。当您要查看正在构建的列表时,请使用scan?。如果只需要最终结果,请使用fold?

您可能仅对以下结构感兴趣,以构建最多12个的斐波那契列表。

scanl (\(a,b) x -> (a+b,a)) (1,0) [2..12]

[(1,0),(1,1),(2,1),(3,2),(5,3),(8,5),(13,8),(21,13) ),(34,21),(55,34),(89,55),(144,89)]

其中将前两个值相加以形成下一个第一个值,而上一个第一个值成为下一个第二个值。

在经过3次迭代的代码中,您可以轻松看到每次迭代的结果。

 take 3.konstSlices $ 100

[[100],[100,100],[100,100,100]]

scanl (\b a -> a : b) [] $ take 3 $ repeat 100

[[],[100],[100,100],[100,100,100]]

但这显示了更多。它具有100附加到的下一个值的初始空列表值。

如果只想要最终结果,

foldl (\b a -> a : b) [] $ take 3 $ repeat 100

[100,100,100]

完全是

100 : []        = [100]
100 : [100]     = [100,100]
100 : [100,100] = [100,100,100]