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

Как я могу использовать только 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];
        }
    });
}

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

3 ответа

Решение

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

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

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

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 циклы, никаких посторонних переменных или итераторов, никакой логики и т. д.

"Идгаф, как, просто сломаться 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 ]

Тест 1: Вы можете написать filter, some, а также every с помощью reduce?

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

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

Для первой части вопроса вы можете использовать reduce а также slice сгруппировать каждые 3 элемента из массива, а затем вы можете использовать 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)

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

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

source = source.reduce((r, e, i) => {
  if(i % 2 == 0) r.push(source.slice(i, i+2));
  return r;
}, [])

console.log(source)

Первое задание с одним 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));

Второе задание с reduce а также map

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

var r2 = source.reduce((p, c, i, a) => {
    if (p[p.length - 1].length < 3) p[p.length - 1].push(c);
    else p.push([a[i]]);
    return p;
}, [[]]).map(e => e.reduce((a, b) => a + b));

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

Другие вопросы по тегам