【问题标题】:infinite lists, lazy evaluation and length无限列表、惰性求值和长度
【发布时间】:2016-06-27 19:07:26
【问题描述】:

Haskell noob here:我仍在尝试理解该语言的机制,所以如果我的问题很愚蠢,请原谅我并指出我可以从中学习的一些链接(我在类似的主题中搜索了一段时间在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]]

我对此非常满意,然后我想“有惰性评估!我什至可以在无限序列上使用它!”。所以我尝试了take 4 $ chunks 3 [1..]。我希望懒惰的 haskell 魔法会产生[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]],相反,这一次懒惰似乎无法帮助我:它无法到达计算的终点(它是一直走到终点吗? [1..]?)

我认为问题出在“长度 xs”部分:ghci 似乎也卡在了一个简单的 length [1..] 上。所以我在问:长度实际上是在迭代整个列表以给出响应吗?如果是这样,我想每次我尝试实现与惰性评估一起工作的东西时都要避免长度,所以有一些替代方案吗? (例如,如何改进我的示例以使用无限列表?)

【问题讨论】:

  • 回答你自己问题的方法当然是追踪length的实现(参见haskell标签信息部分的资源),或者更好地尝试自己定义它并说服自己它只能以一种方式表现。
  • 如果您喜欢冒险,请尝试用Peano numbers 而不是Int 重新实现splitAt 和length,看看您是否可以使您的chunks 好-表现在无限列表中
  • 您可能想看看genericLength 和懒惰的Natural 类型——只需将chunks 类型中的Int 更改为Natural 并将length 更改为@987654341 @ 将使您的示例有效。

标签: haskell lazy-evaluation infinite


【解决方案1】:

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

是的,当然。

每次我尝试实现与惰性求值配合良好的东西时,都要避免长度

是的,绝对的。

那么还有其他选择吗?

是的:在不引用length 的情况下解决问题。没有解决问题的通用方法,因此您需要解决每个具体案例。

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

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

【讨论】:

    【解决方案2】:

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

    是的。

    我想每次我尝试实现与惰性评估一起工作的东西时都要避免长度

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

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

    你不需要检查列表的长度是否小于n,你只需要检查它是否为零。您可以通过简单的模式匹配来做到这一点。


    1 例如f xs | length xs &gt;= 2 = ...,它是O(n),可以替换为f (x1 : x2 : xs) = ...,它是O(1)。

    【讨论】:

      【解决方案3】:

      您可以做的另一个技巧(我在Data.Text 中看到过,但令我惊讶的是一般列表的 Prelude 中没有)是通过返回 Ordering 尽快使 length 短路而不是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]]
      

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-12-25
        • 2021-05-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-02-11
        • 2021-04-26
        相关资源
        最近更新 更多