我仍然是Haskell的初学者。我尝试做一些模式匹配。我想重复列表中的每个元素n次。 N由列表中每个元素的索引位置确定。
例如java
应该给我:['1', '2', '3']
。
这是一个练习,我不应该使用prebuild-list-functions。
我尝试过类似的东西:
['1', '2', '2', '3', '3', '3']
但它只是在第一个元素后加倍了每个元素。我想到elemIndex并复制但我不应该使用这些功能。我的想法是使用elemIndex并使用它作为我的“n”并使用复制或类似之后的“n”和递归。在模式匹配中我需要类似的东西。但我想,我觉得太复杂了。有没有人有想法?
答案 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
,我们将其定义如下:
i
次列表的头部,然后递归列表的尾部并递增i
。我们可以这样实现:
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
列表l
与test'
相同使用该列表,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"]
其中rep1
和rep2
是前两个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]]
枚举参数非常有趣。枚举可以用作任何类型的其他列表的索引参数。枚举可以为其他函数和其他枚举提供参数。