Haskell: удвоить каждый второй элемент в списке
Я только начал использовать Haskell и хотел написать функцию, которая, учитывая список, возвращает список, в котором каждый второй элемент был удвоен.
До сих пор я придумал следующее:
double_2nd :: [Int] -> [Int]
double_2nd [] = []
double_2nd (x:xs) = x : (2 * head xs) : double_2nd (tail xs)
Что работает, но мне было интересно, как вы, ребята, напишете эту функцию. Есть ли более общий/лучший способ или это выглядит правильно?
Ответы
Ответ 1
Это неплохо, по модулю предложенных исправлений. Как только вы познакомитесь с базовой библиотекой, вы, скорее всего, избегаете явной рекурсии в пользу некоторых функций более высокого уровня, например, вы можете создать список функций, где каждый другой *2
и применить (zip) этот список функции к вашему списку чисел:
double = zipWith ($) (cycle [id,(*2)])
Ответ 2
Вы можете избежать исключений "пустого списка" с помощью умного сопоставления с образцом.
double2nd (x:y:xs) = x : 2 * y : double2nd xs
double2nd a = a
это просто синтаксический сахар для следующего
double2nd xss = case xss of
x:y:xs -> x : 2 * y : double2nd xs
a -> a
сопоставление с образцом выполняется по порядку, поэтому сначала xs
будет сопоставляться с образцом x:y:xs
. Затем, если это не удастся, шаблон всеохвата a
будет успешным.
Ответ 3
Немного некромантии, но я думаю, что этот метод очень хорошо работал у меня и хотел бы поделиться:
double2nd n = zipWith (*) n (cycle [1,2])
zipWith принимает функцию, а затем применяет эту функцию для сопоставления элементов в двух списках (первый элемент - первый элемент, второй элемент - второй элемент и т.д.). Функция - это умножение, а занесенный в список список - бесконечный цикл из 1s и 2s. zipWith (и все варианты zip) останавливается в конце более короткого списка.
Ответ 4
Попробуйте его в списке нечетной длины:
Prelude> double_2nd [1]
[1,*** Exception: Prelude.head: empty list
И вы можете увидеть проблему с вашим кодом. "Голова" и "хвост" никогда не являются хорошей идеей.
Ответ 5
Для нечетных списков или double_2nd [x] вы всегда можете добавить
double_2nd (x:xs) | length xs == 0 = [x]
| otherwise = x : (2 * head xs) : double_2nd (tail xs)
Спасибо.
Ответ 6
Здесь решение foldr
-based.
bar :: Num a => [a] -> [a]
bar xs = foldr (\ x r f g -> f x (r g f))
(\ _ _ -> [])
xs
(:)
((:) . (*2))
Тестирование:
> bar [1..9]
[1,4,3,8,5,12,7,16,9]