Выразительный JavaScript (Хавербеке) - страница 52

Параметры функции >reduce, кроме массива – комбинирующая функция и начальное значение. Эта функция чуть менее понятная, чем >filter или >map, поэтому обратите на неё пристальное внимание.

>function reduce(array, combine, start) {

>  var current = start;

>  for (var i = 0; i < array.length; i++)

>    current = combine(current, array[i]);

>  return current;

>}


>console.log(reduce([1, 2, 3, 4], function(a, b) {

>  return a + b;

>}, 0));

>// → 10

Стандартный метод массивов >reduce, который, конечно, работает так же, ещё более удобен. Если массив содержит хотя бы один элемент, вы можете не указывать аргумент >start. Метод возьмёт в качестве стартового значения первый элемент массива и начнёт работу со второго.

Чтобы при помощи >reduce найти самого древнего из известных моих предков, мы можем написать нечто вроде:

>console.log(ancestry.reduce(function(min, cur) {

>  if (cur.born < min.born) return cur;

>  else return min;

>}));

>// → {name: "Pauwels van Haverbeke", born: 1535, …}

Компонуемость

Как бы мы могли написать предыдущий пример (поиск человека с самой ранней датой рождения) без функций высшего порядка? На самом деле, код не такой уж и ужасный:

>var min = ancestry[0];

>for (var i = 1; i < ancestry.length; i++) {

>  var cur = ancestry[i];

>  if (cur.born < min.born)

>    min = cur;

>}

>console.log(min);

>// → {name: "Pauwels van Haverbeke", born: 1535, …}

Чуть больше переменных, на две строчки длиннее – но пока достаточно понятный код.

Функции высшего порядка раскрывают свои возможности по-настоящему, когда вам приходится комбинировать функции. К примеру, напишем код, находящий средний возраст мужчин и женщин в наборе.

>function average(array) {

>  function plus(a, b) { return a + b; }

>  return array.reduce(plus) / array.length;

>}

>function age(p) { return p.died - p.born; }

>function male(p) { return p.sex == "m"; }

>function female(p) { return p.sex == "f"; }


>console.log(average(ancestry.filter(male).map(age)));

>// → 61.67

>console.log(average(ancestry.filter(female).map(age)));

>// → 54.56

(Глупо, что нам приходится определять сложение как функцию >plus, но операторы в JavaScript не являются значениями, поэтому их не передашь в качестве аргументов.)

Вместо того, чтобы впутывать алгоритм в большой цикл, всё распределено по концепциям, которые нас интересуют – определение пола, подсчёт возраста и усреднение чисел. Мы применяем их по очереди для получения результата.

Для написания понятного кода это прямо-таки сказочная возможность. Конечно, ясность не достаётся бесплатно.

Цена

В счастливом краю элегантного кода и красивых радуг живёт гадское чудище по имени Неэффективность.