Я начинающий на Хаскеле,

У меня есть функция

func :: Num a => [a] -> [a]
func [] = []
func (x:xs) = x + func xs

Каждую рекурсию я хочу добавить значение в список для моего вывода. Эта функция суммирует последовательные индексы в списке, так что на входе [1, 2, 3, 4] создается [1, 3, 6, 10].

Как добавить значение, сгенерированное каждый раз, в мой список?

1
James Hamish 20 Окт 2017 в 21:04

3 ответа

Лучший ответ

Ваша проблема здесь не в том, как добавить, а в том, как рассчитать значение в первую очередь. Каждый элемент должен быть заменен суммой самого себя со всеми элементами, предшествующими ему.

Вот один из способов сделать это:

Prelude> func (x:xs) = x:map (+ x) (func xs); func [] = []
Prelude> func [1, 2, 3, 4]
[1,3,6,10]

Как это работает? Нам дан список, который начинается с элемента x и содержит остальные элементы xs. Мы хотим увеличить каждый элемент в xs на x после рекурсивного применения алгоритма к xs.

Вот что делает x:map (+ x) (func xs). Он читается как «prepend x к результату отображения каждого элемента в func xs с шагом x».


Например. для [1, 2, 3, 4] мы хотим, чтобы 1 был добавлен к каждому члену результата рекурсивного применения алгоритма к [2, 3, 4], а затем добавлен перед ним. Для [2, 3, 4] мы хотим, чтобы 2 были ... до [3, 4]. И так до тех пор, пока в конечном итоге для [4] мы не хотим, чтобы 4 добавлялись и добавлялись к результату применения алгоритма к [].

Вот где начинается наш базовый случай (func [] = []): алгоритм определен так, что он возвращает пустой список без изменений. Следовательно, func [4] равен [4], func [3, 4] равен [3, 7], и вы продолжаете увеличиваться и увеличиваться до тех пор, пока не получите [1,3,6,10].

4
Asad Saeeduddin 20 Окт 2017 в 18:39

При переборе списков мы часто используем сгибы, что позволяет сократить список до определенного значения.

Есть также другой тип операции, которая представляет собой складку, которая собирает все результаты по пути, и называется scan (из docs):

scanl                   = scanlGo
  where
    scanlGo           :: (b -> a -> b) -> b -> [a] -> [b]
    scanlGo f q ls    = q : (case ls of
                               []   -> []
                               x:xs -> scanlGo f (f q x) xs)

Таким образом, сканирование принимает три аргумента: функция, которая принимает два значения и возвращает значение, начальное значение и список значений.

Сканирование вернет список.

Таким образом, вам нужна функция, которая принимает два значения и возвращает что-то того же типа, что и первое (это нормально, если оба они одинаковы). Двоичное дополнение будет работать здесь: +.

Вам также нужно начать с значения (b, который является вторым аргументом нашей функции), а 0 - это тождество для целочисленного сложения, поэтому мы должны его использовать.

Наконец, мы передаем ваш список, чтобы получить результат.

Попытайтесь выяснить, как написать, что вы работаете как фолд, а затем как скан, и вы найдете ответ.

0
erewok 20 Окт 2017 в 18:19

Я думаю, что в этом конкретном случае вы могли бы использовать scanl1 как:

scanl1 (+) [1,2,3,4] -- [1,3,6,10]
4
ryachza 20 Окт 2017 в 18:19