(Введение — FPF#0 можно найти здесь)

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

Основы:

Википедия говорит:

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

…Wat?

По сути, функциональный шаблон проектирования должен:

1. Быть чистыми функциями.

При одинаковых входных данных мы должны ожидать одинаковых выходных данных. Например, это чистая функция:

function add5(x) {
  return  x + 5
}

Каждый раз, когда мы вызываем add5(10), мы получаем 15.

Это, с другой стороны, нечисто.

let count = 5
function addCount(x) {
  return count + x
}

Если мы позвоним addCount(10), то получим 15. Отлично! Но что происходит, когда появляется какая-то другая функция и искажает нашу переменную?

someOtherFunction(spud) {
  ...
  count = spud
}

Теперь, когда мы запускаем addCount(10), наша программа внезапно выдает 'potato10'. Это резко отличается от 15, которые мы ожидали. Мы, как люди-программисты, должны уметь рассуждать о своем коде (если вы программист роботов из будущего, я вас не исключаю. Извините, если обидел вас, добрые и любящие Роботы-Повелители! Приветствую! ).

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

2. Функции являются гражданами первого сорта.

Так все объясняют это, когда пишут о функциональном программировании. По сути, это означает, что вы можете взять эту функцию, сохранить ее в переменной и передать ее другим пользователям. Вы можете использовать его в качестве аргумента для другой функции или вернуть из другой функции (именно так работают функции каррирования и компоновки. Мы поговорим об этом в следующем посте). Как программисту, пришедшему на сцену с Javascript, мне это не казалось большой проблемой, потому что я почти всегда знал их именно такими. Если вы исходите из какого-то другого языка, это может быть не так ясно. Из-за того, как Javascript обрабатывает функции, я могу сделать это:

let person = function(name) { 
  return function(mood) {
    return `${name} is dancin' cause they're ${mood}!`
  }
}
//internet points for proper they're use, plz.

Теперь я могу сделать вас и я! Давайте потанцуем!

let bryan = person(‘Bryan’)
let stranger = person('Internet Stranger')
bryan('happy') //"Bryan is dancin' cause they're happy!"
stranger('learning about closures') //"Internet Stranger is dancin' cause they're learning about closures"

Видишь, что я там сделал? Я тайком научил вас кое-чему замыканиям. Причина, по которой составление этих функций работает, заключается в замыканиях — внутренняя функция никогда не теряет доступ к аргументам внешней функции. Вот почему мы можем использовать ${name} как во внутренней, так и во внешней функции! Без этой сверхспособности ничего из этого не было бы возможно.

Последнее, о чем я хочу поговорить в этом первом посте, это все о…

3. Неизменяемые данные.

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

let arr = [1,2,3,4,5] 

Аккуратный! Но внезапно….

arr.push('rattata')
console.log(arr) // [1, 2, 3, 4, 5, 'rattata']

Итак, теперь Раттата не только просто глупый 2-й уровень, но и наш первоначальный массив не такой, каким мы его планировали. Я не уверен, что вы делаете со своими массивами, но этот непреднамеренный эффект, скорее всего, все испортит. Например, скажем, весь смысл вашего массива состоял в том, чтобы удвоить ваши числа, внезапно у вас появляется эта уродливая NaN, с которой нужно иметь дело. Все потому, что мы что-то не так мутировали.

arr.map(num => num * 2)
// [2, 4, 6, 8, 10, NaN]

Ew.

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

Но что, если по какой-то причине нам действительно нужна Раттата?

let arr = [1,2,3,4,5]
function plusRattata(array) {
  return array.concat('Rattata')
}
let arrWithRattata = plusRattata(arr) 
console.log(arr) // [1, 2, 3, 4, 5]
console.log(arrWithRattata) //[1, 2, 3, 4, 5, 'Rattata']

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

Хорошо, так что есть немного, чтобы мы начали. На следующей неделе я хочу поговорить о рекурсии, некоторых полезных встроенных функциях, таких как Array#map/reduce/filter, и мы займемся забавными задачками! А пока я оставлю вам несколько ссылок, с которых можно начать, и несколько проблем, которые нужно решить. Попробуйте использовать то, о чем мы говорили выше, на задачах ниже. Я хотел бы услышать, как вы собираетесь решать эти проблемы, поэтому не стесняйтесь комментировать этот пост или пишите мне в Твиттере @spacebrayn со своими вопросами, решениями, жизненными целями, самыми глубокими страхами или любыми милыми фотографиями жирафа. А если серьезно, пожалуйста, пришлите милые фотографии жирафа.

// 1. Write a program that takes an array and returns every other item
  function everyOther(array) {
    ... your code here 
    //(hint: perhaps look in to Array#filter)
  }
// 2. Write a function that returns another function. This one is as freeform as you want it to be!
  function returnF(input) {
    ...your code here
  }
// 3. Write a program that prints out the numbers 0 through x on the screen WITHOUT using loops!
  function printNumbers(x) {
    ...your code here 
    //(hint: look into recursion)
  }

Читайте (мы поговорим об этом в следующий раз):

  1. MDN — Массив#Фильтр
  2. MDN — Массив#Карта
  3. MDN — Массив#Сократить
  4. MDN — Функции и рекурсия
  5. XKCD — Водка

Спасибо, друзья! До следующего раза!

❤ Брайан.