【问题标题】:Implementing an unusual sequence as an infinite list in Haskell在 Haskell 中将不寻常的序列实现为无限列表
【发布时间】:2023-03-10 11:29:01
【问题描述】:

我有两个元素作为列表的开头 [1, 2]

这个不寻常的序列是它复制了在三个元素之后的某种类型的数字中的数字。例如,在 1 和 2 之后,我们将有另一个 2,然后是两个 1。 所需列表的前几个元素将产生

[1, 2, 2, 1, 1, 2, 1, 2, 2, 1, 2, 2, 1, 1, 2, 1, 1, 2, 2] 因为

1  2   2  1 1   2  1  2   2  1   2   2

其中前面的数字表示同一数字的小序列的长度。

到目前为止,我已经尝试使用 replicate 函数根据列表中较早的元素重复相同的数字。

selfrle :: [Int]
selfrle = 1 : 2 : [x | a <- [0..], let x = replicate (selfrle !! a) (selfrle !! (a + 1))) ]

问题是我不知道为什么它不起作用。

【问题讨论】:

  • 我无法对您要生成的序列的描述做出正面或反面。
  • 原来它是一个无限的符号序列 {1,2},以 [1,2, ...] 开头,并且是来自其自己的 RLE 编码的运行长度序列:ks == ([1,2] ++) . drop 2 . map fst . rle $ ks

标签: algorithm haskell sequence lazy-sequences


【解决方案1】:

在 OEIS 中出现的 https://oeis.org/A000002 并没有那么不寻常,并在那里被命名为 Kolakoski 序列。他们甚至在 2011 年提供了 John Tromp 的 Haskell 程序:

a = 1:2: drop 2 (concat . zipWith replicate a . cycle $ [1, 2])

【讨论】:

    【解决方案2】:

    你可以做的第一件事就是让你的类型正确。 replicate 构建一个列表,因此列表推导中的x 具有[Int] 类型,整个列表推导是所有x 值的列表,其类型为[[Int]]。当前两个元素是12 时,您不能使用Int列表 列表作为列表的尾部。类型不匹配:您需要确定这是Int 的列表,还是Int列表 列表。

    根据您的描述,我怀疑您想通过使用concat 展平列表理解来解决此问题,如下所示:

    selfrie = 1 : 2 : concat [x | a <- [0..],
                                  let x = replicate (selfrie !! a) (selfrie !! (a + 1))]
    

    如果您尝试这样做,您将无法获得您描述的列表。我不知道如何为下一部分提供帮助,那是因为我不理解您对所需列表的描述。这与其说是一个编程问题,不如说是一个规范问题,所以也许您可以回到原始来源,看看那里是如何解释的?

    【解决方案3】:

    我们可以比@DanD's answer 写得稍微优雅一点,通过使用一个额外的变量b 来“打结”:

    a :: [Int]
    a = 1 : 2 : b
        where b = 2 : concat (zipWith replicate b (cycle [1, 2]))
    

    因此,我们在这里首先生成1 : 2 : bb2 开头,然后将b 的复制与[1, 2, 1, 2, …] 的无限列表连接起来。

    【讨论】:

      【解决方案4】:

      接受的答案似乎简洁且非常有效,但很难推理,特别是如果您是初学者并且忽略了 Haskell 的懒惰。

      我们可以改写成这样

      a = 1:2:2: (concat . zipWith replicate (drop 2 a) . cycle) [1, 2]
      

      我们必须记住 a 只是像 1:2:2:?: ... ?:[] 这样的半构造列表,我们只是通过丢弃前两个项目 (drop 2 a) 开始使用它,这让我们将 2:?: ... ?:[] 应用于 zipWith replicate 和这个足以让我们无限期地运行我们的逻辑。请记住2:?: ... ?:[] 是一个要像cycle [1,2] 一样计算的列表。初始项目2 的可用性可以计算下一个项目,然后是下一个项目,然后是下一个项目,以此类推。

      如果您知道初始项目,这是 Haskell 中生成无限列表的一个漂亮模式。以斐波那契数列为例

      fib = 1:1: (zipWith (+) fib (tail fib))
      

      您需要做的就是调整您的思维方式,让您认为fib 已经存在但尚未计算。

      State Monad 的另一种方法:

      但是,我相信给定的 Kolakoski 系列也可能是学习 State monad 的一个很好的案例研究。让我们看看我们是否可以得到相同的性能..?

      import Control.Monad.Trans.State
      
      invert :: Int -> Int
      invert = (+1) . (`mod` 2)
      
      rle :: Int -> State Int [Int]
      rle n = state $ \s -> (replicate n s, invert s)
      
      kolakoski :: [Int] -> Int -> [Int]
      kolakoski ps s = ps ++ kolakoski xs i
                       where (xs,i) = runState (mapM rle ps >>= pure . concat) s
      
      • 我们有一个invert 函数,当使用1 时,会得到2,当使用2 时,会得到1
      • rle 接受 Int 类型并返回 State 类型,该函数获取状态并通过 n 复制它,并返回复制列表以及新状态 invert s。所以我们的状态会像1,2,1,2...一样交替变化。

      但是,当使用 ghci (:set +s) 进行测试时,虽然两者都显示出线性时间复杂度,但这需要 10 倍的时间才能完成。有人愿意发表评论吗..?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-04-16
        • 1970-01-01
        • 2017-09-28
        • 1970-01-01
        • 2015-01-24
        • 2020-12-06
        • 1970-01-01
        相关资源
        最近更新 更多