List Haskell列表复杂性
抱歉,如果已经问过这个问题,我没有找到它。对不起,我的英语很差 我正在学习Haskell并尝试使用列表。 我写了一个函数,它按照特定的模式转换列表,我现在无法检查它是否有效,但我想是的 此函数不是尾部调用函数,因此我认为用一个大列表计算此函数会很糟糕:List Haskell列表复杂性,list,haskell,concatenation,List,Haskell,Concatenation,抱歉,如果已经问过这个问题,我没有找到它。对不起,我的英语很差 我正在学习Haskell并尝试使用列表。 我写了一个函数,它按照特定的模式转换列表,我现在无法检查它是否有效,但我想是的 此函数不是尾部调用函数,因此我认为用一个大列表计算此函数会很糟糕: transform :: [Int] -> [Int] transform list = case list of (1:0:1:[]) -> [1,1,1,1] (1:1:[]) -> [1,0,1] [1
transform :: [Int] -> [Int]
transform list = case list of
(1:0:1:[]) -> [1,1,1,1]
(1:1:[]) -> [1,0,1]
[1] -> [1,1]
(1:0:1:0:s) -> 1:1:1:1: (transform s)
(1:1:0:s) -> 1:0:1: (transform s)
(1:0:s) -> 1:1: (transform s)
(0:s) -> 0: (transform s)
所以我想到了另一个功能,它会“更好”:
问题是我不知道它是如何编译的,也不知道第二个函数是否错了。你能解释一下列表是如何工作的吗?在最后使用(++)还是颠倒列表更好
提前感谢您的回答第一个功能非常好,我认为比第二个更好 原因是懒惰。如果代码中有一个
transform-someList
表达式,则除非您要求,否则不会计算结果列表。特别是,仅在需要时对清单进行评估print$take 10$transform xs
比print$take 20$transform xs
所做的工作要少
在严格的语言中,transform
确实会妨碍堆栈,因为它必须在返回任何有用的内容之前(以非尾部递归方式)计算整个列表。在Haskelltransform(0:xs)
中,计算结果为0:transform xs
,这是一个可用的部分结果。我们可以在不接触尾部的情况下检查结果的头部。也没有堆栈溢出的危险:在任何时候,列表末尾最多有一个未计算的thunk(如上例中的transformxs
)。如果您需要更多元素,thunk将被进一步向后推,并且可以对前一个thunk的堆栈帧进行垃圾收集
如果我们总是完全评估列表,那么这两个函数的性能应该是相似的,甚至因为缺少反转或额外的
++
-s,延迟版本可能会更快一些。因此,通过切换到第二个函数,我们失去了惰性,也没有获得额外的性能。在我看来,您的第一个版本要好得多。它不是尾部递归的,这很好:你不希望它是尾部递归的,你希望它是懒惰的。如果不处理整个输入列表,第二个版本甚至不能生成单个元素,因为为了反转aux
的结果,必须知道aux
的全部内容。但是,
take 10 . transform $ cycle [1,0,0,1,1,1]
对于您的第一个定义transform
,可以很好地使用,因为您只需要根据需要使用列表中的内容来做出决策
1但是请注意,(1:0:1:[])
只是[1,0,1]
非常感谢你的回答,我明白我现在在做什么,所以我不得不更多地依靠懒惰。我太习惯于严格的语言了。谢谢,我想,用模式匹配来匹配[1,0,1]是行不通的。。。但它只是一个值,所以它当然会起作用。
take 10 . transform $ cycle [1,0,0,1,1,1]