Haskell - 根据列表索引重复列表的元素

时间:2018-04-28 22:51:03

标签: list haskell replicate

我仍然是Haskell的初学者。我尝试做一些模式匹配。我想重复列表中的每个元素n次。 N由列表中每个元素的索引位置确定。 例如java应该给我:['1', '2', '3']。 这是一个练习,我不应该使用prebuild-list-functions。

我尝试过类似的东西:

['1', '2', '2', '3', '3', '3']

但它只是在第一个元素后加倍了每个元素。我想到elemIndex并复制但我不应该使用这些功能。我的想法是使用elemIndex并使用它作为我的“n”并使用复制或类似之后的“n”和递归。在模式匹配中我需要类似的东西。但我想,我觉得太复杂了。有没有人有想法?

5 个答案:

答案 0 :(得分:6)

Haskell的一个重要部分是将问题分解为更小的问题。所以,让我们解决你的问题。

您需要做的一件事就是重复一个元素。正如您已经发现的那样,此功能在Haskell中以replicate函数的形式存在。但你可以轻松地自己实现它。

repeatNTimes :: Int -> a -> [a]
repeatNTimes 0 _ = []
repeatNTimes n x = x : repeatNTimes (n - 1) x

如果我们重复零次,请返回空列表。否则,将元素放在前面并递归。

现在我们可以重复一些事情。让我们编写一个跟踪列表位置的函数。它看起来像这样。

testImpl :: Int -> [a] -> [a]

它取一个整数(当前位置)和列表的尾部,并返回给定列表的特定部分的结果。和以前一样,我们有两种情况:一种是列表为空,另一种是不对。第一种情况很简单;如果列表为空,则返回空列表。如果列表非空,请重复第一个元素,然后递归。

testImpl :: Int -> [a] -> [a]
testImpl _ [] = []
testImpl n (x:xs) = repeatNTimes n x ++ testImpl (n + 1) xs

++是一个内置函数,它连接两个列表。如果你真的想要,你也可以自己实现。现在,首先,我们将第一个元素视为元素1,因为我们想重复一次,所以我们通过将1传递给testImpl来开始递归。

test :: [a] -> [a]
test = testImpl 1

完整示例:

repeatNTimes :: Int -> a -> [a]
repeatNTimes 0 _ = []
repeatNTimes n x = x : repeatNTimes (n - 1) x

testImpl :: Int -> [a] -> [a]
testImpl _ [] = []
testImpl n (x:xs) = repeatNTimes n x ++ testImpl (n + 1) xs

test :: [a] -> [a]
test = testImpl 1

答案 1 :(得分:3)

列出对救援的理解:

test xs = [x | (i,x) <- zip [1..] xs, _ <- [1..i]]

除非你在&#34; prebuilt-list-functions&#34;中考虑zip,否则你很可能会这样做。

答案 2 :(得分:1)

在这种情况下,我们通常使用累加器。累加器是我们传递(和更新)以实现目标的额外参数。因此,我们可以使用两个参数实现函数test':列表l和索引i,我们将其定义如下:

  1. 如果列表为空,我们返回一个空列表;和
  2. 如果列表不为空,我们将列出i次列表的头部,然后递归列表的尾部并递增i
  3. 我们可以这样实现:

    test' :: Int -> [a] -> [a]
    test' _ [] = []
    test' i (x:xs) = rep i
        where rep j | j > 0 = x : rep (j-1)
                    | otherwise = test' (i+1) xs
    

    现在我们只需要根据test来定义test',我们可以说test列表ltest'相同使用该列表,1作为起始索引:

    test :: [a] -> [a]
    test = test' 1
        where test' _ [] = []
              test' i (x:xs) = rep i
                  where rep j | j > 0 = x : rep (j-1)
                              | otherwise = test' (i+1) xs
    

    然后我们获得如下测试结果:

    Prelude> test ['1'..'3']
    "122333"
    Prelude> test [1..3]
    [1,2,2,3,3,3]
    Prelude> test [1, 4, 2, 5]
    [1,4,4,2,2,2,5,5,5,5]
    Prelude> test "ia2b"
    "iaa222bbbb"
    

答案 3 :(得分:1)

你可以不用任何数字来做到这一点。让我们按照自己的方式努力吧。我们将使用累加器方法,但不是在累加器中保留数字,而是保持函数重复其参数一定次数。

test0 :: [a] -> [a]
test0 xs = go rep1 xs
  where
    rep1 :: a -> [a]
    rep1 a = [a]

    go :: (a -> [a]) -> [a] -> [a]
    go _rep [] = []
    go rep (a : as) = rep a ++ go (oneMore rep) as

    oneMore :: (a -> [a]) -> (a -> [a])
    oneMore rep a = a : rep a

我们开始使用go调用rep1,这是一个非常简单的函数,可将其参数转换为单例列表。然后在每次递归调用时,我们通过再次重复其参数来修改转发器函数。

test0工作正常,但它使用++函数,你不应该使用任何预定义的函数。在这里使用++也意味着您必须构建小型列表并将它们放在一起,这是我们可以轻松删除的低效率。

请注意,每次go调用rep时,它会立即在结果中添加其他内容。这表明了解决方案:不是让rep获取一个元素并生成一个列表,而是让它获取一个元素和一个列表,并生成一个列表,该列表包含重复一定次数的元素,后跟给定的列表!所以我们会有

rep1 "a" ["b", "c"] = ["a", "b", "c"]
rep2 "a" ["b", "c"] = ["a", "a", "b", "c"]

其中rep1rep2是前两个rep函数。只需要进行一些调整。

test :: [a] -> [a]
test = go rep1
  where
    rep1 :: a -> [a] -> [a]
    rep1 a as = a : as  -- Note: rep1 can be written as just (:)

    go :: (a -> [a] -> [a]) -> [a] -> [a]
    go _ [] = []
    go rep (a : as) = rep a (go (oneMore rep) as)

    oneMore :: (a -> [a] -> [a])
            -> a -> [a] -> [a]
    oneMore f a as = a : f a as

这确实不是解决问题的高效方法,但它是一种相当极简主义的方法。

答案 4 :(得分:0)

枚举可以在给定基数的情况下产生序数。指数是序数。这意味着列表中的数字a是枚举中的结束号。每个索引集(从序数设置的基数)是[0..index]第一个,实际上零是[0..1]。如果我们想要我们的序数它将是[1..1],那么[1..2]和[1..3]但是该函数使用零索引的习惯和基数必须被去除。

[b|b<-[1,2,3], a<-[0..b-1]]

枚举参数非常有趣。枚举可以用作任何类型的其他列表的索引参数。枚举可以为其他函数和其他枚举提供参数。