Вы можете сделать это без каких-либо чисел. Давайте работать до этого. Мы будем использовать накопительный подход, но вместо того, чтобы хранить число в аккумуляторе, мы сохраним функцию , которая повторяет свой аргумент определенное количество раз.
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
Это действительно не эффективный способ решения проблемы, но это довольно минималистский подход.