无限列表,懒惰的评估和长度

时间:2016-03-12 14:26:24

标签: haskell lazy-evaluation infinite

Haskell noob在这里:我仍然试图理解语言的机制,所以如果我的问题很简单,请原谅我并指出一些我可以学习的链接(我已经搜索过了)在stackoverflow上有类似主题的一段时间,但我仍然无法得到这个。)

我出来了这个功能:

chunks :: Int -> [a] -> [[a]]
chunks n xs
    | length xs <= n = [xs]
    | otherwise = let (ch, rest) = splitAt n xs in ch:chunks n rest

这样

ghci> chunks 4 "abracadabra"
["abra","cada","bra"]
ghci> 
ghci> chunks 3 [1..6]
[[1,2,3],[4,5,6]]

我对此很满意,然后我想&#34;懒惰的评价!我甚至可以在无限的序列中使用它!&#34;。所以我尝试了take 4 $ chunks 3 [1..]。我希望懒惰的haskell魔法会产生[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]],相反,似乎这个时候懒惰无法帮助我:它无法达到计算的终点(它是否全部走了方式到[1..]结尾?)

我认为问题出在&#34;长度xs&#34; part:ghci似乎也被困在一个简单的length [1..]上。所以我问:长度实际上是在迭代整个列表来给出响应吗?如果是这样,我想每次我尝试使用懒惰评估实现一些正常工作时,应该避免长度,所以有一些替代方案吗? (例如,如何改进我的示例以使用无限列表?)

3 个答案:

答案 0 :(得分:5)

  

是实际迭代整个列表的长度以给出响应吗?

  

我想每次尝试使用延迟评估实现一些正常的工作时,应避免长度

不仅如此,当懒惰不是因素时(如果O(1)检查通常足够 1 的情况下为O(n)),它也会给你带来不良的运行时间,所以你一般来说,大部分时间都应该避免使用它。

  

如何改进我的示例以使用无限列表?

您不需要检查列表的长度是否小于n,您只需要检查它是否为零。而且您可以使用简单的模式匹配。

1 例如f xs | length xs >= 2 = ...之类的东西(O(n))可以替换为f (x1 : x2 : xs) = ...,即O(1)。

答案 1 :(得分:4)

你可以做的另一个技巧(我在Data.Text看过,但我很惊讶的不是一般列表的前奏)是通过返回使length尽快短路Ordering而不是Bool

compareLength :: [a] -> Int -> Ordering
compareLength [] n = compare 0 n
compareLength _ 0 = GT
compareLength (x : xs) n = compareLength xs (n - 1)

然后您可以在chunks中使用它。

chunks :: Int -> [a] -> [[a]]
chunks n xs = case compareLength xs n of
                   LT -> [xs]
                   _  -> let (ch, rest) = splitAt n xs in ch:chunks n rest

这很好用。

*Main> take 4 $ chunks 3 [1..]
[[1,2,3],[4,5,6],[7,8,9],[10,11,12]]

对于这种特殊情况,其他实现可能更惯用,但希望这是一个很好的技巧。

答案 2 :(得分:3)

  

是实际迭代整个列表的长度以给出响应吗?

是的,绝对的。

  每次我尝试使用延迟评估实现一些良好的工作时,应该避免长度

是的,绝对。

  

所以还有其他选择吗?

是:在不引用length的情况下解决问题。没有解决问题的一般方法,因此您需要解决每个特定情况。

  

如何改进我的示例以使用无限列表

你是一名铁路工人。如果汽车从您站立的地方开始并延伸到地平线上,那么这是一辆巨大的火车。你不知道它在哪里结束,如果有的话。你的工作是把它分成三列车的小火车。你怎么办?