Частичное применение и каррирование в JavaScript

preview_player
Показать описание


#каррирование #частичное #применение #javascript #программирование #лекции #js
Рекомендации по теме
Комментарии
Автор

Частичное применение ( Partial application )
00:00:00 Замыкание
00:04:54 Лямбда- функция
00:06:06 Метод bind
00:08:30 Абстрагированный пример функции
00:13:42 Абстрагированный пример функции (расширение)
Рекурсивное частичное применение ( Currying )
00:15:47 Неправильная реализация каррирования
00:22:22 Правильная реализация каррирования
00:28:54 Правильная реализация каррирования (расширение)
Объединение из нескольких функций одной ( Composition )
00:31:41 Функциональные абстракции (superposition)
00:38:02 Функциональные абстракции, с сохранением выражений (composition)
00:45:05 Композиция с 2 функциями
00:46:05 Композиция с n - функциями, при помощи метода reduce
00:51:25 Композиция с n - функциями, при помощи цикла
00:54:29 Композиция с n - функциями, при помощи рекурсии
00:57:40 Композиция асинхронных функций

01:01:27 Обобщение

anatolykobzisty
Автор

Шикарная лекция, аж гордость за КПИ берет.

cassinid
Автор

Дякую за пояснення та наведені приклади! 🚀

TimaGixe
Автор

Досмотрел до 3:30 и понял всю суть видео. Хоча достаточно просто прочитать код. Хорошее обьяснение того, зачем применяються замыкания.

Andriy
Автор

не, ну это невозможно....неделю по пару видео в день смотрю и не могу остановиться. У вас совесть есть?

СтройС-яо
Автор

Спасибо! Путал частичное применение и каррирование

ИльяБондаренко-те
Автор

10:10 Говориться что второй аргумент переданный в f11 передается в sum4 как первый аргумент (как 'а'). Но это не совсем так, и если было бы так то вызов с тремя аргументами вместо кидать ошибку, этот третьей аргумент пересылался бы на sum4 как второй аргумент. Так что второй аргумент которого передаем в partial, передается вместо аргумента 'x', а не вместо 'a' у функции sum4. Но когда partial вызываем второй раз, то тогда замкнутый 'x' предается в sum4 как первый аргумент

TimurSevimli
Автор

25:37
Как я понял проблема в том, что когда идет рекурсивный вызов curry в неё передаем IIFE, которая возвращает анонимную функцию (...args2) => (
fn(...args1.concat(args2)) ). Если мы используем rest в функции, то вызов length у такой функции будет возвращать 0. Поэтому мы ограницены только двумя вызовами функции f. Нам надо передать все аргументы за один или два раза. Уже f(1)(2)(3, 4) - не пройдёт.
Вот мое решение :
const curry = fn => (...args) => (
fn.length > args.length ? curry( fn.bind(null, ...args )) : fn(...args)
)

buksirchik
Автор

проблема в плохом карировании в том, что concat может соидениять только 2 массива аргументов

ДаніілДенисюк-рь
Автор

Если вдруг кому понадобится функция каррирования функций с любым количеством аргументов, то вот поставил себе как д/з и сделал:
function curry(fn){
return function func(...args){
function f(...ar){
return func(...args.concat(ar));
}

f.toString = () => fn(...args);

return f;
}
}

работает без ограничений, по крайней мере я их не нашёл:)

exeleNt
Автор

Причина не корректной работы curry-bad заключается в том что мы ограничены двумя частичными вызовами и это из за первое выражение которого возвращается из тернарного оператора. А решение очень простая и оно вот так выгладить:

const curry = (fn) => (...args) => (
fn.length > args.length ?
curry(fn.bind(null, ...args)) :
fn(...args)
);

TimurSevimli
Автор

в примере 6-curry-bad.js
функция curry(((fn, args1) => (...args2) => fn(...args1.concat(args2)) ) )(fn, ...args) может вызваться коректно ровно 1 раз...
когда у args1 и у args2 числовые аргументы и в сумме их минимум 4
так как функция sum4 ожидает 4 аргумента - а точнее 4 числа,
то вызов fn(...args1.concat(args2)) с 3 или меньше арнументами, заменит нехватку до четырех на undefined,
а число или числa + undefined вернет NaN а не функцию с уже присвоенными арнументами
а вот в примере 7-curry.js
Вы при помощи bind и спред оператора говорите что мол у тебя fn первыми аргументами будут число или масив чисел из ...args1.concat(args2) и возвращаете функцию carry с аргументом fn ( с етими числами) (без имидиат инвокайшен)
а при последующем вызове carry eсли аргументов 4 или более - игнорится if и возвращается fn, тоесть sum4 с первыми 4-мя аргументами и результат;

igorizbish
Автор

Правильно ли я понимаю что все отличие каррированной функции от частично-примененной только в том, что каррированная функция это просто преобразованная функция которая может получать аргументы по одному или в случае усовершенствованной версии порциями по одному или по несколько аргументов, а частично-примененная это по сути такая же каррированная функция но с заданным одним или несколькими параметрами? И в принципе как мы привязали эти параметры неважно. То есть например const curriedSum = curry(sum) . Здесь функция curriedSum каррированная. const partialSum = curriedSum(2) А здесь partialSum частично-примененная с параметром 2 . Можно сразу сделать const partialSum = curry(sum)(2). Вопрос в том, зачем тогда нужна функция частичного применения если мы ее можем получить из каррированной функции? Ну и мой вариант каррированной функции: const curry = func => (...args) => args.length >= func.length ? func(...args) : (...args2) => curry(func)(...args, ...args2)

ДенисЕгоров-щд
Автор

Дня 4 потратил я наверно не меньше чтоб это все осознать и разобраться - но не все конечно понял. Подходы для меня новые - никогда не пользовался карированием.

dmytrohaponov
Автор

В примере 6-curry-bad.js если вы напишите вот так "const curry = fn => (...args) => (
fn.length > args.length ? curry(
((fn, ...args1) => (...args2) => (...args3) => (

))(fn, ...args)

) : fn(...args));",

то у вас выполнится функция y4, которая равна f(1, 2)(3)(4), функции y6, y1, y2, и не выполнятся все остальные. Также можно выполнить и функцию y5, добавив еще одну лямбду с аргументами (...args4). То есть придется делать 3 разных варианта функции curry для всех восьми случаев, проверяя каждый раз если одна функция вернула ошибку, то вызвать другую и так далее, но это очень много кода. По сути, это и есть дебаг шестого примера :D

delimobilstories
Автор

Почему, если нужно два массива раздробить на аргументы, используется (...arr1.concat(arr2)), а не (...arr1, ...arr2)?
Примеры 5 и 6.

АндрейКотомкин-ою
Автор

const carry = (fn) => (...a1) =>
fn.length > a1.length
? (...a2) => carry(fn)(...a1.concat(a2))
: fn(...a1)

sfmict
Автор

slice же не отрезает, а копирует. Тут в связи с упоминаем типа "string" вспомнил вопрос: строка является неизменяемым типом данных в js, при этом работает такое выражение str = str[0].toUpperCase() + str.slice(1). Как в таком случае понимать про неизменяемость строк?

igorsavelev
Автор

А я что-то не понял, смысл от вот такого скрытия операторов, если они все равно в функции вызываются? Где это можно будет применить?

gaddyya
Автор

26:27 Вот так проходят варианты 4 и 7 а для 5 го варианта нужно добавить args4. (или ещё раз провести инструкцию сравнение внутри if -а )
const curry = fn => (...args) => {
if (fn.length > args.length) {
const partial = (fn, ...args1) => (...args2) => (...args3) =>
return curry(partial)(fn, ...args);
} else {
return fn(...args);
}
};

const sum4 = (a, b, c, d) => (a + b + c + d);
const f = curry(sum4);
const y4 = f(1, 2)(3)(4);
//const y5 = f(1)(2)(3)(4);
const y7 = f(1)(2)(3, 4);
console.log(y4, y7)

ziyadseykhanov