В других ответах обсуждается, как использовать библиотечные функции в качестве строительных блоков для создания вашей функции. Я думаю, что было бы также интересно посмотреть, как написать рекурсию самостоятельно; в этом случае это особенно просто. Я последую прекрасному предложению Люки в комментариях: сначала написать функцию для чередования двух списков, а затем составить все функции в результате.
interleave :: [a] -> [a] -> [a]
interleave (x:xs) ys = x : interleave ys xs
interleave [] ys = ys
compose :: [a -> a] -> a -> a
compose [] = id
compose (f:fs) = f . compose fs
Затем ваша функция может запустить эти две последовательности:
carnage :: [a -> a] -> [a -> a] -> a -> a
carnage fs gs = compose (interleave fs gs)
Позже, когда вы станете более продвинутым, вы сможете заметить некоторые общие циклические структуры в приведенном выше коде. В частности, compose
является вполне стандартной «формой» итерации: сделайте что-нибудь, чтобы объединить определенный элемент с результатом выполнения рекурсивного вызова. Эта форма настолько распространена, что у нас есть стандартная библиотечная функция для ее инкапсуляции. foldr
принимает функцию объединения (которая объединяет один элемент с результатом рекурсивного вызова) и результат базового случая в качестве аргументов, затем выполняет итерацию, поэтому:
compose :: [a -> a] -> a -> a
compose = foldr (.) id
l oop в interleave
- менее распространенная форма, хотя иногда она возникает в различных обстоятельствах. Одним из способов является думать об этом как почтовый индекс; или вы можете заметить, что транспонирование матрицы - это, по сути, произвольный почтовый индекс, и повторно использовать эту операцию. Итак:
interleave :: [a] -> [a] -> [a]
interleave xs ys = concat (transpose [xs, ys])
На этом этапе реализации функций для compose
и interleave
настолько коротки, что вы можете рассмотреть возможность их вставки, если не считаете, что имена являются важной частью объяснительная сила кода. Итак:
carnage :: [a -> a] -> [a -> a] -> a -> a
-- was: carnage fs gs = compose (interleave fs gs)
carnage fs gs = foldr (.) id (concat (transpose [fs, gs]))
Идиоматически, Haskell программисты обычно предпочитают цепочки композиции функций вложенным скобкам:
carnage fs gs = foldr (.) id . concat . transpose $ [fs, gs]
На мой опытный взгляд, это выглядит компактно и читабельно; Я бы остановился там.