Функциональный способ для пользовательской итерации
Как я могу использовать только 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));