Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

  • задайте вопрос нашим менторам. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
  • расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
  • изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.
Об обучении на Хекслете

Каррирование

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

В предыдущем уроке нам приходилось писать функцию-обертку для применения.

const getProgrammersSalaryByCountry =
  (country) => getAverageSalary('programmer', country);

const salary1 = getProgrammersSalaryByCountry('spain');
const salary2 = getProgrammersSalaryByCountry('russia');
const salary3 = getProgrammersSalaryByCountry('usa');

Eсли бы наша функция getAverageSalary была каррирована, то все было бы значительно проще. Каррирование — это процесс превращения функции от n аргументов в цепочку вложенных n-функций от одного аргумента. Соответственно, каррированная функция — это множество функций от одного аргумента.

Предположим, что у нас есть функция const sum = (a, b, c) => a + b + c, которая складывает три числа. Тогда ее каррированная версия будет выглядеть так: const sum2 = a => b => c => a + b + c, а использование таким: sum2(5)(10)(-2). То же самое, разложенное по функциям:

const sum2 = (a) => {
  return (b) => {
    return (c) => {
      return a + b + c;
    };
  };
};

Посмотрите внимательно на определение a => b => c => a + b + c. Эта запись очень краткая и одновременно очень емкая. Суммарное количество функций считается очень легко, оно равно сумме всех стрелок =>. Для лучшего понимания можно добавить скобки a => (b => (c => a + b + c)). Для каждого определения функции все, что находится справа от стрелки, является ее телом, каким бы сложным оно не было. Поначалу такая запись может помочь понять, как друг в друга вложены функции, но со временем, когда вы привыкните к обычной записи, она будет больше мешать.

С другой стороны, если нам нужно каррировать существующую функцию без ее реализации, то делается это так: const sum2 = a => b => c => originalSum(a, b, c). То есть создается цепочка вложенных функций, в которой количество вложений равно количеству аргументов исходной функции и в конце которой происходит вызов оригинальной функции.

Разберем происходящее по шагам.

const sum = (a) => (b) => (c) => a + b + c;
const sum1 = sum(10); // sum1 = b => c => 10 + b + c
const sum2 = sum1(3); // sum2 = c => 10 + 3 + c
const result = sum2(0); // result = 10 + 3 + 0
console.log(result); // => 13

То же самое происходит и при таком вызове: sum(10)(3)(0), разница только в том, что вызовы происходят без создания промежуточных констант.

И задача для самоконтроля. Сколько раз нужно вызвать цепочку функций const greeting = () => () => () => () => console.log('Hey!'), чтобы дойти до конца? Обязательно попробуйте прямо сейчас выполнить это задание на repl.it.

Теперь вернемся к нашей функции расчета зарплаты в ее каррированном виде. Представим, что теперь у нас в распоряжении две вложенных функции от одного аргумента: const getAverageSalary = job => country => /* body */:

const salary1 = getAverageSalary('programmer')('spain');
const salary2 = getAverageSalary('programmer')('russia');
const salary3 = getAverageSalary('programmer')('usa');

Попробуем частично применить:

const getProgrammersSalaryByCountry = getAverageSalary('programmer');

const salary1 = getProgrammersSalaryByCountry('spain');
const salary2 = getProgrammersSalaryByCountry('russia');
const salary3 = getProgrammersSalaryByCountry('usa');

Применение в реальной жизни

В функциональных языках, подобных haskell, вопросов об использовании каррированных функций просто не встает. Они там используются постоянно, начиная с самых азов. Это одна из причин, почему стоит учить подобные языки, даже если вы не будете писать на них программы. В императивных языках такое происходит значительно реже, но не в JS. JS по своей сути очень близок к функциональным языкам. Изначально он должен был быть написан на Scheme, языке из семейства Lisp, и функции в нем занимают центральное место. Каррирование используется в различных библиотеках и иногда является их ключевой "фичей", как, например, здесь: https://github.com/substantial/updeep.


<span class="translation_missing" title="translation missing: ru.web.courses.lessons.mentors.mentor_avatars">Mentor Avatars</span>

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят менторы из команды Хекслета или другие студенты.

Для полного доступа к курсу, нужна профессиональная подписка

Профессиональная подписка откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, даст возможность обращаться за помощью к менторам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
115
курсов
892
упражнения
2241
час теории
3196
тестов

Зарегистрироваться

или войти в аккаунт

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

  • 115 курсов, 2000+ часов теории
  • 800 практических заданий в браузере
  • 250 000 студентов

Нажимая кнопку «Зарегистрироваться», вы даёте своё согласие на обработку персональных данных в соответствии с «Политикой конфиденциальности» и соглашаетесь с «Условиями оказания услуг».

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Rambler
Логотип компании Bookmate
Логотип компании Botmother

Есть вопрос или хотите участвовать в обсуждении?

Зарегистрируйтесь или войдите в свой аккаунт

Нажимая кнопку «Зарегистрироваться», вы даёте своё согласие на обработку персональных данных в соответствии с «Политикой конфиденциальности» и соглашаетесь с «Условиями оказания услуг».