Я видел проблему под названием round-robin , но, возможно, interleave
- лучшее имя.Конечно, map
, reduce
и filter
являются функциональными процедурами, но не все функциональные программы должны полагаться на них.Когда это единственные функции, которые мы знаем, как использовать, результирующая программа иногда бывает неловкой, потому что зачастую она лучше подходит.
map
дает один-к-одному результат.Если у нас есть 4 подмассива, наш результат будет иметь 4 элемента.interleave
должен привести к результату, равному длине объединенных подмассивов, поэтому map
может только привести нас туда.Для получения окончательного результата потребуются дополнительные шаги.
reduce
выполняет итерацию по элементам ввода по одному для получения окончательного результата.В первом сокращении нам будет дан первый подмассив, но нет простого способа обработать весь подмассив перед тем, как перейти к следующему.Мы можем заставить нашу программу использовать reduce
, но при этом она заставляет нас думать о нашей процедуре сопоставления как о процедуре повторения, а не о том, чем она является на самом деле.
Реальность такова, что вы не ограничены использованием этих примитивных функциональных процедур.Вы можете написать interleave
таким способом, который непосредственно кодирует его намерение.Я думаю, что interleave
имеет красивое рекурсивное определение.Я думаю, что здесь хорошо использовать назначение глубокой деструктуризации, потому что сигнатура функции показывает форму данных, которые interleave
ожидает;массив массивов. Математическая индукция позволяет нам естественным образом обрабатывать ветви нашей программы -
const None =
Symbol ('None')
const interleave =
( [ [ v = None, ...vs ] = [] // first subarray
, ...rest // rest of subarrays
]
) =>
v === None
? rest.length === 0
? vs // base: no `v`, no `rest`
: interleave (rest) // inductive: some `rest`
: [ v, ...interleave ([ ...rest, vs ]) ] // inductive: some `v`, some `rest`
const input =
[ [ "one", "two", "three" ]
, [ "uno", "dos" ]
, [ "1", "2", "3", "4" ]
, [ "first", "second", "third" ]
]
console.log (interleave (input))
// [ "one", "uno", "1", "first", "two", "dos", "2", "second", "three", "3", "third", "4" ]
interleave
освободило нас от оков ограниченного мышления.Мне больше не нужно думать о моей проблеме с точки зрения деформированных частей, которые неловко сочетаются друг с другом - я не имею в виду индексы массивов, или sort
, или forEach
, или состояние мутации с push
, или сравнение с использованием>
или Math.max
.И при этом я не должен думать о извращенных вещах как как массив - вау, мы действительно принимаем как должное только то, как много мы узнали о JavaScript!
Выше, это должночувствовать себя освежающе, что есть нет зависимостей.Представьте, что новичок подходит к этой программе: ему / ей нужно только изучить 1) как определить функцию, 2) деструктурировать синтаксис, 3) троичные выражения.Программы, объединенные с бесчисленными небольшими зависимостями, потребуют, чтобы учащийся ознакомился с каждой из них до того, как можно будет получить интуицию для программы.
Тем не менее, синтаксисы JavaScript для деструктурирования значений не самые красивые и иногда обмениваются.для удобства сделаны для повышения читабельности -
const interleave = ([ v, ...vs ], acc = []) =>
v === undefined
? acc
: isEmpty (v)
? interleave (vs, acc)
: interleave
( [ ...vs, tail (v) ]
, [ ...acc, head (v) ]
)
Зависимости, которые здесь развивались, isEmpty
, tail
и head
-
const isEmpty = xs =>
xs.length === 0
const head = ([ x, ...xs ]) =>
x
const tail = ([ x, ...xs ]) =>
xs
Функциональность такая же -
const input =
[ [ "one", "two", "three" ]
, [ "uno", "dos" ]
, [ "1", "2", "3", "4" ]
, [ "first", "second", "third" ]
]
console.log (interleave (input))
// [ "one", "uno", "1", "first", "two", "dos", "2", "second", "three", "3", "third", "4" ]
Проверьте результаты в вашем собственном браузере ниже -
const isEmpty = xs =>
xs.length === 0
const head = ([ x , ...xs ]) =>
x
const tail = ([ x , ...xs ]) =>
xs
const interleave = ([ v, ...vs ], acc = []) =>
v === undefined
? acc
: isEmpty (v)
? interleave (vs, acc)
: interleave
( [ ...vs, tail (v) ]
, [ ...acc, head (v) ]
)
const input =
[ [ "one", "two", "three" ]
, [ "uno", "dos" ]
, [ "1", "2", "3", "4" ]
, [ "first", "second", "third" ]
]
console.log (interleave (input))
// [ "one", "uno", "1", "first", "two", "dos", "2", "second", "three", "3", "third", "4" ]
Если вы начинаете , думая о interleave
, используя map
, filter
и reduce
, то, скорее всего, они будутчасть окончательного решения.Если это ваш подход, вас должно удивить, что map
, filter
и reduce
нигде не видно в двух программах в этом ответе.Урок здесь - ты становишься пленником того, что знаешь.Иногда вам нужно забыть map
и reduce
, чтобы заметить, что другие проблемы имеют уникальную природу , и поэтому общий подход, хотя и потенциально действительный, не обязательно является наилучшим.