Вот частично бессмысленное однострочное решение Haskell:
ma p = reverse . map ((/ (fromIntegral p)) . sum . take p) . (drop p) . reverse . tails
Сначала он применяет хвосты к списку, чтобы получить списки "хвостов", поэтому:
Prelude List> tails [2.0, 4.0, 7.0, 6.0, 3.0]
[[2.0,4.0,7.0,6.0,3.0],[4.0,7.0,6.0,3.0],[7.0,6.0,3.0],[6.0,3.0],[3.0],[]]
Обращает его и удаляет первые записи 'p' (принимая p за 2 здесь):
Prelude List> (drop 2 . reverse . tails) [2.0, 4.0, 7.0, 6.0, 3.0]
[[6.0,3.0],[7.0,6.0,3.0],[4.0,7.0,6.0,3.0],[2.0,4.0,7.0,6.0,3.0]]
Если вы не знакомы с символом (.) Точка / ниппель , это оператор для «функциональной композиции», то есть он передает выходные данные одной функции как входные данные другой, «составляя» их в одну функцию. (g. f) означает «запустить f на значении, а затем передать результат в g», поэтому ((f. g) x) совпадает с (g (f x)). Обычно его использование приводит к более ясному стилю программирования.
Затем он отображает функцию ((/ (fromIntegral p)). Sum. Take p) в список. Таким образом, для каждого списка в списке он берет первые элементы 'p', суммирует их, а затем делит их на 'p'. Затем мы просто переворачиваем список обратно с помощью «reverse».
Prelude List> map ((/ (fromIntegral 2)) . sum . take 2) [[6.0,3.0],[7.0,6.0,3.0]
,[4.0,7.0,6.0,3.0],[2.0,4.0,7.0,6.0,3.0]]
[4.5,6.5,5.5,3.0]
Все это выглядит гораздо более неэффективно, чем есть; «reverse» физически не меняет порядок списка до тех пор, пока список не будет оценен, он просто размещает его в стеке (добрый ленивый Haskell). "tails" также не создает все эти отдельные списки, он просто ссылается на различные разделы исходного списка. Это все еще не очень хорошее решение, но оно длиной в одну строку:)
Вот немного более приятное, но более длинное решение, использующее mapAccum для скользящего вычитания и сложения:
ma p l = snd $ mapAccumL ma' a l'
where
(h, t) = splitAt p l
a = sum h
l' = (0, 0) : (zip l t)
ma' s (x, y) = let s' = (s - x) + y in (s', s' / (fromIntegral p))
Сначала мы разбиваем список на две части в «p», поэтому:
Prelude List> splitAt 2 [2.0, 4.0, 7.0, 6.0, 3.0]
([2.0,4.0],[7.0,6.0,3.0])
Суммируйте первый бит:
Prelude List> sum [2.0, 4.0]
6.0
Сжать второй бит с исходным списком (это просто объединяет элементы в порядке из двух списков). Оригинальный список явно длиннее, но мы теряем этот дополнительный бит:
Prelude List> zip [2.0, 4.0, 7.0, 6.0, 3.0] [7.0,6.0,3.0]
[(2.0,7.0),(4.0,6.0),(7.0,3.0)]
Теперь мы определим функцию для нашего mapAccum (ulator). mapAccumL - это то же самое, что и «map», но с дополнительным параметром состояния / накопителя, который передается от предыдущего «отображения» к следующему при прохождении карты по списку. Мы используем аккумулятор в качестве нашей скользящей средней, и поскольку наш список состоит из элемента, который только что покинул скользящее окно, и элемента, который только что вошел в него (список, который мы только что заархивировали), наша скользящая функция принимает первое число «x» от среднего и добавляет второе число «у». Затем мы передаем новые 's' и возвращаем 's', разделенные на 'p'. «snd» (second) просто берет второй член пары (кортеж), который используется для получения второго возвращаемого значения mapAccumL, поскольку mapAccumL будет возвращать как аккумулятор, так и отображенный список.
Для тех из вас, кто не знаком с символом $ , это «оператор приложения». Он на самом деле ничего не делает, но у него есть «низкий приоритет связывания справа», так что это означает, что вы можете опустить скобки (обратите внимание на LISPers), то есть (fx) совпадает с f $ x
Запуск (ma 4 [2.0, 4.0, 7.0, 6.0, 3.0, 8.0, 12.0, 9.0, 4.0, 1.0]) дает [4.75, 5.0, 6.0, 7.25, 8.0, 8.25, 6.5] для любого решения.
Да, и вам нужно будет импортировать модуль "Список", чтобы скомпилировать любое решение.