Частичное применение
Заранее заполняйте аргументы функций.
«Частичное применение» — бесплатный урок Lua Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Lua Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Lua Academy содержит 4 уроков всего.
Что такое частичное применение
Частичное применение означает, что некоторые аргументы функции фиксируются сейчас, а остальные передаются позже. В результате Вы получаете новую функцию, которой требуется меньше аргументов.
Это позволяет превратить универсальную функцию в специализированную, зафиксировав уже известные Вам значения.
Пример вручную
Предположим, у Вас есть функция add. Вы можете создать addFive, написав замыкание, которое захватывает число 5 и ожидает второе число.
Захваченное значение хранится в возвращённой функции, поэтому при каждом вызове требуется только оставшийся аргумент.
local function add(a, b)
return a + b
end
local function addFive(b)
return add(5, b)
end
print(addFive(10))
print(addFive(2))Фиксация первого аргумента
Можно обобщить этот шаблон. Вспомогательная функция bind1 принимает функцию и фиксированный первый аргумент, а затем возвращает новую функцию, ожидающую второй аргумент.
Замыкание захватывает и f, и зафиксированное значение, а при вызове передаёт все аргументы дальше.
local function bind1(f, a)
return function(b)
return f(a, b)
end
end
local function mul(x, y) return x * y end
local double = bind1(mul, 2)
print(double(7))
print(double(50))Использование переменного числа аргументов
Чтобы зафиксировать любое количество начальных аргументов, используйте синтаксис переменного числа аргументов Lua .... Сначала захватите фиксированные аргументы, а затем добавьте остальные во время вызова.
Функции table.pack и table.unpack помогают сохранять и раскрывать списки аргументов.
local function partial(f, ...)
local fixed = table.pack(...)
return function(...)
local rest = table.pack(...)
local args = {}
for i = 1, fixed.n do args[i] = fixed[i] end
for i = 1, rest.n do args[fixed.n + i] = rest[i] end
return f(table.unpack(args, 1, fixed.n + rest.n))
end
end
local function add3(a, b, c) return a + b + c end
local add10 = partial(add3, 10)
print(add10(2, 3))Фиксация нескольких аргументов
Поскольку partial принимает переменное число аргументов, можно зафиксировать сразу несколько аргументов. Возвращённой функции тогда нужны только остальные.
Здесь заранее заданы два из трёх аргументов, поэтому получается функция с одним аргументом.
local function partial(f, ...)
local fixed = table.pack(...)
return function(...)
local rest = table.pack(...)
local args = {}
for i = 1, fixed.n do args[i] = fixed[i] end
for i = 1, rest.n do args[fixed.n + i] = rest[i] end
return f(table.unpack(args, 1, fixed.n + rest.n))
end
end
local function label(prefix, sep, value) return prefix .. sep .. value end
local tag = partial(label, "id", ":")
print(tag(42))Практическое применение
Частичное применение особенно полезно для универсальных вспомогательных функций. Один раз задайте конфигурацию, а затем многократно используйте специализированную функцию.
Ниже универсальная функция greet превращается в функцию приветствия на заданном языке, которой нужно только имя.
local function greet(lang, name)
if lang == "tr" then return "Merhaba, " .. name end
return "Hello, " .. name
end
local function bind1(f, a) return function(b) return f(a, b) end end
local greetTr = bind1(greet, "tr")
print(greetTr("Ada"))
print(greetTr("Lin"))Частичное применение и каррирование
Каррирование превращает функцию с несколькими аргументами в цепочку функций с одним аргументом, каждая из которых возвращает следующую. Частичное применение лишь фиксирует часть аргументов, а остальные оставляет для обычного вызова.
Эти подходы связаны, но различаются: при каррировании аргументы обрабатываются по одному, а при частичном применении некоторые аргументы фиксируются сразу.
local function curryAdd(a)
return function(b)
return function(c)
return a + b + c
end
end
end
print(curryAdd(1)(2)(3))Каррирование функции с двумя аргументами
Небольшая вспомогательная функция может автоматически выполнить каррирование любой функции с двумя аргументами. Она возвращает функцию первого аргумента, которая возвращает функцию второго.
Так получаются повторно используемые функции с одним аргументом, которые удобно читаются в конвейерах.
local function curry2(f)
return function(a)
return function(b)
return f(a, b)
end
end
end
local function pow(base, exp) return base ^ exp end
local cpow = curry2(pow)
print(cpow(2)(10))Всё работает благодаря замыканиям
Каждая функция с частичным применением и каждая каррированная функция опирается на замыкания. Зафиксированные аргументы — это захваченные локальные переменные, которые помнит возвращаемая функция.
Поскольку у каждой возвращаемой функции есть собственные захваченные значения, Вы можете безопасно создавать множество специализированных вариантов.
local function bind1(f, a) return function(b) return f(a, b) end end
local function sub(x, y) return x - y end
local from100 = bind1(sub, 100)
local from10 = bind1(sub, 10)
print(from100(40))
print(from10(3))Когда это применять
Используйте частичное применение, чтобы устранить повторения, если снова и снова вызываете функцию с одними и теми же начальными аргументами.
Оно также помогает адаптировать функцию к интерфейсу, которому нужно меньше аргументов, например к функции обратного вызова, передающей только одно значение.
Настроенные функции обратного вызова
Частичное применение хорошо сочетается с отображением и фильтрацией. Вы можете заранее заполнить аргумент конфигурации, чтобы функция обратного вызова получала только элемент списка.
Это сохраняет чистоту конвейера и при этом позволяет настраивать поведение.
local function map(t, f) local o={} for i,v in ipairs(t) do o[i]=f(v) end return o end
local function bind1(f, a) return function(b) return f(a, b) end end
local function scale(factor, x) return factor * x end
local r = map({1,2,3,4}, bind1(scale, 10))
print(table.concat(r, ", "))Быстрая проверка
Вспомните различие между двумя связанными идеями.
Итоги
Частичное применение фиксирует часть аргументов и возвращает новую функцию, ожидающую остальные; это реализуется с помощью замыканий, захватывающих зафиксированные значения.
Вспомогательная функция partial с переменным числом аргументов обобщает этот подход, а каррирование объединяет функции с одним аргументом в цепочку. Оба подхода уменьшают повторения и адаптируют функции к функциям обратного вызова.
Часто задаваемые вопросы
Урок «Частичное применение» бесплатный?
Да — полный текст урока «Частичное применение» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Lua Academy, подпишись на CoddyKit PRO. Курс Lua Academy содержит 4 уроков всего.
Чему я научусь в уроке «Частичное применение»?
Заранее заполняйте аргументы функций. Ты практикуешь Lua Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Lua Academy?
Предыдущий опыт не требуется. Lua Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Частичное применение»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Lua Academy?
Да. Каждый урок Lua Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Функции как значения
- map, filter, reduce
- Частичное применение
- Композиция функций