Функциональный способ для пользовательской итерации

Как я могу использовать только map, reduce или filter или любой функциональный способ создания пользовательской итерации в массиве?

Скажем, я хочу сопоставить массив с другим массивом, который содержит сумму каждого из трех смежных элементов в исходном массиве:

var source = [1, 2, 3, 4, 6, 7, 8] // to [6, 17, 8]

Или создайте ведро из двух элементов:

var source = [1, 2, 3, 4, 5, 6, 7] // to [[1, 2], [3, 4], [5, 6], [7]]

Для второго я имею следующее, но это не выглядит очень функциональным, поскольку я обращаюсь к массиву по индексу:

function* pairMap(data) {
    yield* data.map((item, index) => {
        if (index > 0) {
            return [data[index - 1], item];
        }
    });
}

Я заинтересован в функциональном способе этого.

Ответ 1

Скажем, я хочу сопоставить массив с другим массивом, который содержит сумму каждого из трех смежных элементов в исходном массиве:

var source = [1, 2, 3, 4, 6, 7, 8] // to [6, 17, 8]

Карты создают отношения 1:1, поэтому это не будет подходящим использованием map. Вместо этого лучше было бы reduce или ( "fold" ).

const comp = f=> g=> x=> f (g (x));
const len = xs=> xs.length;
const isEmpty = xs=> len(xs) === 0;
const concat = xs=> ys=> ys.concat(xs);

const chunk= n=> xs=>
  isEmpty (xs)
    ? []
    : concat (chunk (n) (xs.slice(n))) ([xs.slice(0,n)]);

const reduce = f=> y=> xs=> xs.reduce((y,x)=> f(y)(x), y);
const map = f=> xs=> xs.map(x=> f(x));
const add = x=> y=> y + x;
const sum = reduce (add) (0);

var source = [1, 2, 3, 4, 6, 7, 8];
comp (map (sum)) (chunk (3)) (source);
//=> [ 6, 17, 8 ]

Итак, как вы можете видеть, мы сначала преобразуем source в куски 3, а затем map функцию sum по каждому фрагменту.

Когда вы слышите, как люди говорят о "декларативном" коде, последняя строка достаточно ясна и мало беспокоится о реализации. Мы не говорим компьютеру о том, как выполнять свою работу. Там нет циклов for/while, нет посторонних варов или итераторов, нет логики и т.д.

"idgaf, просто разделите source на группы по 3, а затем суммируйте каждую часть

// very declaration, wow
comp (map (sum)) (chunk (3)) (source);

Или создайте ведро из двух элементов:

var source = [1, 2, 3, 4, 5, 6, 7] // to [[1, 2], [3, 4], [5, 6], [7]]

Используя тот же код выше

var source = [1, 2, 3, 4, 5, 6, 7];
chunk (2) (source);
// => [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ], [ 7 ] ]

Для второго я имею следующее, но это не выглядит очень функциональным, поскольку я обращаюсь к массиву по индексу:

function* pairMap(data) {
      yield* data.map((item, index) => {
         if (index > 0) {
              return [data[index - 1], item];
          }
      });
  }

Используя приведенный выше код, вы можете легко реализовать pairMap

const pairMap = f=> comp (map (f)) (chunk (2));

var source = [1, 2, 3, 4, 5, 6, 7];
pairMap (pair => console.log(pair)) (source);
// [ 1, 2 ]
// [ 3, 4 ]
// [ 5, 6 ]
// [ 7 ]

Узнать все вещи

Вопрос - "функциональный способ для пользовательской итерации". Вы заметите, что мои коды сортируются с помощью Array.prototype.reduce и Array.prototype.map. Изучение того, как их строить самостоятельно, было хорошим инструментом обучения для меня, чтобы понять, что функциональные петли/итераторы/управление функциональными функциями - это весело и просто.

const isEmpty = xs=> xs.length === 0
const head = xs=> xs[0];
const tail = xs=> xs.slice(1);

const reduce = f=> y=> xs=>
  isEmpty (xs)
    ? y
    : reduce (f) (f (y) (head (xs))) (tail (xs));

const add = x=> y=> y + x;
reduce (add) (0) ([1,2,3]);
//=> 6

Это работает!

Хорошо, посмотрим, как мы будем делать карту

const concat = xs=> ys=> ys.concat(xs);
const append = x=> concat ([x]);

const map = f=>
  reduce (ys=> x=> append (f (x)) (ys)) ([]);

const sq = x => x * x;
map (sq) ([1,2,3])
//=> [ 1, 4, 9 ]

Quiz 1: Можете ли вы написать filter, some и every с помощью reduce?

Предупреждение о тролле: Существует множество способов реализации этих функций. Если вы начнете писать рекурсивные функции, первое, что вы хотите узнать, это то, что есть tail call. ES6 получает оптимизацию хвостового звонка, но она не будет распространяться некоторое время. Некоторое время Babel мог перевести его с помощью цикла while, но он временно отключен в версии 6 и будет возвращаться после их исправления.

Quiz 2: Как вы можете переписать мой reduce с правильным хвостом?

Ответ 2

Для первой части вопроса вы можете использовать reduce и slice для группировки всех трех элементов из массива, а затем вы можете использовать map и reduce для получения суммы каждой группы.

var source = [1, 2, 3, 4, 6, 7, 8];

var result = source.reduce((r, elem, i) => {
  if(i % 3 == 0) r.push(source.slice(i, i+3));
  return r;
}, []);

result = result.map(e => {return e.reduce((a, el) => { return a + el })});
console.log(result)

Ответ 3

Первая задача с одним reduce

var source = [1, 2, 3, 4, 5, 6, 7];

var r1 = source.reduce((p, c, i, a) => {
    if (i % 2) p[p.length - 1].push(c);
    else p.push([a[i]]);
    return p;
}, []);

console.log(JSON.stringify(r1, 0, 2));