Я продолжаю думать о том, чтобы писать больше, но чем больше я думаю об этом, тем меньше я это делаю. Итак, сегодня я перестал думать и начал писать.

Отсюда можно только спускаться…

В основном, я не мог придумать, о чем писать. Большинство вещей, которые я знаю, были написаны ДО СМЕРТИ. Но сегодня я понял: все в порядке! Каждый усваивает вещи по-разному (что, кстати, тоже нормально), так что может случиться так, что кто-то чего-то не понимает, потому что это просто не было продемонстрировано или объяснено способом, соответствующим их стилю обучения.

Кроме того, они говорят, что лучший способ закрепить свое знание чего-либо — это записать это или передать другим. Может быть, это поможет кому-то, даже если этот кто-то только я!

Этим утром я думал о композиционной цепочке функций в Javascript — о том, что мы делаем все больше и больше на работе по мере того, как переходим к более функциональному стилю (да, да — я знаю, что это не ДЕЙСТВИТЕЛЬНО функциональное программирование, но это шаг в правильное направление. Обратите внимание, я сказал СТИЛЬ. Мы пока не собираемся использовать полномасштабный Haskell и крутые усы…).

Во всяком случае, я решил изложить свои мысли о старых фаворитах - map, filter и reduce. Это потребовало некоторой мозговой борьбы, чтобы прийти в себя. Я много читал, смотрел много видео (спасибо Professor Frisby), но все никак не заживало. Они несложные, но мой мозг просто не мог с этим справиться по какой-то причине.

Итак, сначала идет Карта:

Map можно вызывать для массива, который затем вызывает переданную ему функцию для каждого элемента в первом массиве.

const addOne = num => num + 1;
let nums = [1,2,3,4,5,6,7,8,9];
let jsMap = nums.map(addOne); // [ 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

Вы можете вставить это в свой редактор или IDE и запустить с помощью Node, чтобы убедиться в этом самостоятельно (то же самое со всеми этими примерами).

Также полезно помнить, что это не обязательно должна быть переданная функция (хотя это, вероятно, окажется более полезным в filter и reduce):

let nums = [1,2,3,4,5,6,7,8,9]; 
let jsMap = nums.map(num => num>= 5);
// [ false, false, false, false, true, true, true, true, true ]

Важной особенностью map является то, что исходный массив не изменяется:

const addOne = num => num + 1;
let nums = [1,2,3,4,5,6,7,8,9];
let jsMap = nums.map(addOne); // [ 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
console.log(nums); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

Мне кажется, что это один из главных бонусов функционального программирования — неизменяемость. По сути, это означает, что исходная вещь остается неизменной. Не мутировал. Радиацией или чем-то еще.

Итак, вернемся к моему вступительному гамбиту — чем отличается это объяснение? Я уверен, что вы видели функцию addOne, написанную триллион раз, если вы пытаетесь разобраться в этих «чистых» функциях. Ну ничего. Уже.

Но теперь я подумал, что напишу функцию map вручную. Использование старых добрых JavaScript-циклов for (хотя сейчас они в значительной степени дьявольские). Все понимают forloop. Если нет, я бы вернулся к чему-то более простому…

Итак, вот (используя нашу функцию addOne ранее):

const map = (arr, func) => {
  let results = [];
  for(let i = 0; i < arr.length; i++) {
    let result = func(arr[i]);
    results.push(result);
  };
  // Note that results is returned -
  // the original array that was passed in is non-radioactive
  return results;
  }
let ourOwnVerySpecialMapResults = map(nums, addOne);
// [ 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
// Nums is still unchanged
console.log(nums); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

Следует отметить пару вещей: исходный массив nums не стал радиоактивным (или каким-либо образом изменился). Там, где функция addOne передается нашей map, передается только имя без вызываемой функции - это сделано в руководстве map, которое мы написали.

Но главное, что нужно вынести из этого, это то, что это просто цикл for!!

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

Я предполагаю, что следующий вопрос: если это так просто, почему мы используем JS map? Ну, мы можем легко связать другие функции с концом нашего результата mapped. Подробнее об этом позже. Часть 2, возможно (если она есть)…

Далее Фильтр:

Как и в случае с map, filter на самом деле просто еще один замаскированный цикл for. Он проходит через массив, проверяет каждое значение и передает его в новый массив, если значение соответствует ожиданиям. Нравится:

let nums = [1,2,3,4,5,6,7,8,9];
let jsFilter = nums.filter(num => num > 5); // [ 6, 7, 8, 9 ]

Итак, он проходит через nums, проверяет каждое значение, чтобы увидеть, больше ли оно пяти:

num => num > 5

И возвращает его в массиве, если он есть. Простые.

Вот он, простой фильтр JS. Как и прежде, давайте напишем свой собственный:

const moreThanFive = num => {
  if (num > 5) {
    return num;
  }
}
const filter = (arr, check) => {
  let results = [];
  for(let i = 0; i < arr.length; i++) {
    if (check(arr[i])) {
      results.push(arr[i]);
    }
  };
  return results; // nums is still non-mutant
};
let ourOwnVerySpecialFilterResults = filter(nums, moreThanFive);
// [ 6, 7, 8, 9 ]

Как и раньше, это всего лишь цикл for, но с оператором if внутри, который управляет тем, что pushed помещается в массив results.

Имеет ли это смысл? Ты со мной? Я надеюсь, что это так…

Наконец, Уменьшить:

Так. Reduce требует две вещи: функцию и «3,5-дюймовую дискету» (или запоминающее устройство по вашему выбору). Он проходит через массив, к которому он вызывается, передает каждое значение функции, а затем сохраняет его на дискету (флэш-накопитель Minions USB). Дискета не обязательно должна быть числом, это может быть массив и т. д. — просто помните, что он будет придерживаться своего типа: если ваша дискета (64-гигабайтная карта памяти Sandisk micro SD) изначально является массивом, результаты функции будет pushed в этот массив. Если это число, вы можете диктовать, что делать — прибавлять, вычитать и т. д.

let jsReduce = nums.reduce((sum, num) => {
  if (moreThanFive(num)) {
    return sum + num;
  } else {
    return sum; // nums is unaffected still
  }
// The floppy disk here is the (number) 0 passed after the function:
}, 0);
// 30 (6 + 7 + 8 + 9)

Это пройдет через наш массив nums, проверьте, равно ли значение moreThanFive. Если это так, он сохранит его на дискету (спиральный блокнот в линейку).

Вы можете думать об этом как о функции map, но все результаты сводятся к одному.

Это, вероятно, более сложный из трех. Уделите некоторое время тому, чтобы убедиться, что вы поняли что, а не как.

Итак, давайте попробуем написать нашу собственную функцию сокращения:

const reduce = (floppyDisk, arr) => {
  for(let i = 0; i < arr.length; i++) {
    if (arr[i] > 5) {
      floppyDisk += arr[i];
    }
  };
  return floppyDisk; // nums is still not a mutant/zombie/whatever
}

Это очень простая версия — она будет работать только с числами, но вы поняли идею. Версия JS намного мощнее — она может нажимать на массивы и т. д.

НО, вы можете видеть, что это все ещепросто forloop с проверкой if внутри него.

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

Попробуйте их, посмотрите, как это работает для вас.

Если это поможет, дайте мне знать — оставьте комментарий, ретвит или что-то еще.

Если это не так, дайте мне знать.

Возможно, есть лучший способ сделать это, чем этот, но я старался сделать его ясным и простым.

ПОМНИТЕ ДЕТЕЙ: сокращение переменных до одной буквы ДЕЙСТВИТЕЛЬНО никому не помогает, когда они пытаются учиться.