Ответ 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
с правильным хвостом?