Эликсир: идиоматический способ расчета разности последовательных элементов? - PullRequest
0 голосов
/ 02 ноября 2018

Я хотел бы рассчитать разницу последовательных элементов в числовом списке, например, input = [1, 2, 3, 5] должен выдавать [-1, -1, -2].

Я придумал два способа, которые кажутся более или менее сложными. Есть ли более простой идиоматичный способ сделать это?

Использование почтового индекса:

Enum.zip(input, Enum.drop(input,1)) 
|> Enum.map(fn {x,y} -> x-y end)

Использование сканирования (слишком сложное):

Enum.scan(input, [0, 0], fn x, [_delta, prev] -> [prev-x, x] end) 
|> Enum.map(fn [x, _y] -> x end) 
|> Enum.drop(1)

Ответы [ 3 ]

0 голосов
/ 02 ноября 2018

На самом деле, все может быть сделано с чистым Enum.reduce/3.

input
|> Enum.reduce({nil, []}, fn
  e, {nil, acc} -> {e, acc}
  e, {prev, acc} -> {e, [prev - e | acc]}
end) # this does already contain the answer btw
|> elem(1)
|> :lists.reverse()
#⇒ [-1, -1, -2]
0 голосов
/ 04 ноября 2018

Хотя мне больше нравится уменьшенная версия, использование zip помогает лучше понять проблему.

Сначала мы делаем сдвинутую версию ввода:

[_ | next] = input

А затем мы застегиваем input и next и вычисляем вычитания:

input |> Enum.zip(next) |> Enum.map(fn {a, b} -> a - b end)

Значение input |> Enum.zip(next) part равно [{1, 2}, {2, 3}, {3, 5}]. На этом этапе каждый элемент соединяется со следующим.

0 голосов
/ 02 ноября 2018

Хорошо, это очень специфический вариант использования, поэтому нет встроенного решения, которое бы точно так и делало, но я думаю, что использование Enum.chunk_every/4 - это большинство идиоматических, которые вы можете быть:

chunk_every(enumerable, count, step, leftover \\ [])

Возвращает список списков, содержащих count элементов каждый, где каждый новый блок начинается с step элементов в перечисляемом.


Пример и объяснение:

input
|> Enum.chunk_every(2, 1, :discard)
|> Enum.map(fn [x, y] -> x - y end) 
  • Члены сгруппированы в списки 2 элементов каждый
  • Следующий блок начинается после 1 шага от начального элемента предыдущего списка
  • :discard означает, что он отбрасывает последний остаток [5] порции, так как он нам не нужен.
  • Возвращает элементы в таких группах: [[1, 2], [2, 3], [3, 5]]
  • Окончательно вычисляет разницу для каждого куска, используя Enum.map
...