В этом форуме (да и в соседних тоже) часто упоминаются такие термины как Closure, Lambda, Currying.
У меня так и не сложилось четкого представления об этих вещах. И думаю, что я такой не один.
Интересно, что в языках, поддерживающих такие понятия, обычно не задумываешься где lambda, где closure и т.д. Просто используешь это все дело и все (блоки Smalltalk, Ruby, анонимные методы и делегаты в С#). Но т.к. термины все таки употребляются достаточно часто хотелось бы найти в сети/книгах (или совместными усилиями выработать) их понятное определение на русском языке.
А пока я опишу свое понимание. Пусть имеем лямбду
labmda(x) (x + n)
Здесь
x — связанная переменная, т.к. она является параметров функции
n — свободная переменная, она не является параметров функции и появляется из внешнего контекста.
Lambda:
— имеет свободны переменные (не определенные в контексте, но используемые внутри функци)
— при подстановке свободных переменных возвращает другую лямбду
Для того, чтобы определять функции на основе этой lambda создадим функцию:
В ней все переменные являются связанными и фактическа она аналогична обычной функции с параметрами. Т.к. все свободные переменные закрыты, эта функция называется closure.
Closure:
— не имеет свободных переменных, т.е. это "закрытая" лямбда
— при подстановке параметров возвращает обычный результат (не функцию)
— формируется из лямбды, путем закрытия свободных переменных
И это аналогично функции
funcAdd(x) (x + 3)
// пример использования
funcAdd(10) => результат 13
Остался карринг. Я понимаю его так: Currying:
— это процесс закрытия свободных переменных в лямбдах
Пусть есть лямбда
aLambda := lambda(p) (p + x + y) // x, y - свободные
xLambda := aLambda(1) => результат lambda(p) (p + 1 + y) // закрыли x, произошел карринг, получили другую лямбду
yLambda := xLambda(2) => результат lambda(p) (p + 1 + 2) // закрыли y, произошел карринг, теперь получили уже closure
Т.е. при карринге каждый раз мы закрываем по одной свободной переменной. Можно остановится, сформировав новую лямбду (xLambda в примере), а можно закрыть все свободные переменные и получить closure (yLamda в примере). Еще можно записать так:
yLambda := aLambda(1)(2) => результат lambda(p) (p + 1 + 2)
// использование
yLambda(25) => результат 28
Вопросы:
— правильны ли эти краткие определения терминов?
— какие отличия между closure и обычными (локальными) функциями?
— делегаты C# 1.0, анонимные методы 2.0 и "лямбды" в 3.0 это вовсе и не лямбды, а closure?
А может я все напутал, и вопросы заданы не правильно?
К сожалению, без конкретного определения терминов трудно обсуждать что-то еще, но очень хотелось бы поднять вопросы о практическом использования subject в различных языках (и частоту использования), а также поддержку этих понятий в различных языках (а при ее отсутствии — возможную реализацию).
Вопросы скорее теоретические, но мы же в "Философии" вроде находимся. Ой..., а может я не там вопросы задаю?
Здравствуйте, Beam, Вы писали:
B>В этом форуме (да и в соседних тоже) часто упоминаются такие термины как Closure, Lambda, Currying. B>У меня так и не сложилось четкого представления об этих вещах. И думаю, что я такой не один.
Здравствуйте, Plague, Вы писали:
P>Если б эту статью уже выложили, а так какой толк сейчас?
А если почитать тред про эту статью, то можно найти вот такую ссылку
... << RSDN@Home 1.2.0 alpha rev. 655>>
"Бог не терпит голой сингулярности" -- Роджер Пенроуз
Здравствуйте, CiViLiS, Вы писали:
CVL>Здравствуйте, Plague, Вы писали:
P>>Если б эту статью уже выложили, а так какой толк сейчас? CVL>А если почитать тред про эту статью, то можно найти вот такую ссылку
Надеюсь, ты понимаешь, чем отличается полтора экрана полезного материала от нудного разглагольствования на 15 страниц текста?
Здравствуйте, Beam, Вы писали:
B>А пока я опишу свое понимание.
B>Lambda: B>- имеет свободны переменные (не определенные в контексте, но используемые внутри функци)
Совсем не обязательно. Лямбда может совсем не иметь свободных переменных:
lambda х, у: х+у
B>- при подстановке свободных переменных возвращает другую лямбду
Что это значит я вообще не понял. Куда подставляются свободные переменные?
Надо проще быть. Я лично для себя определил так: лямбда — это банальная функция, не больше не меньше и есть ли у нее свободные переменные уже не важно.
B>Closure: B>- при подстановке параметров возвращает обычный результат (не функцию)
Почему функция не может быть обычным результатом?
B>- не имеет свободных переменных, т.е. это "закрытая" лямбда B>- формируется из лямбды, путем закрытия свободных переменных
Вот это уже ближе.
Действительно, замыкания с лямбдами тесно связаны. Замыкание — это функция которая имеет ссылки на свободные переменные. Заметь, она "закрывается" не значениями (как я думал еще вчера , привет АВК и Ллойду ), а ссылками на свободные переменные.
B>Currying: B>- это процесс закрытия свободных переменных в лямбдах
Нет. Карринг — это процесс закрытия связаных переменных.
inc = (1+) -- тут каррировали (почти кастрировали, кстати, почти одно и тоже) функцию + (имеющюю 2 параметра), теперь получили функцию inc с одним параметром.
B>Вопросы: B>- правильны ли эти краткие определения терминов?
см. выше B>- какие отличия между closure и обычными (локальными) функциями?
см. выше B>- делегаты C# 1.0, анонимные методы 2.0 и "лямбды" в 3.0 это вовсе и не лямбды, а closure?
Да, кстати, тут уже приводили ссылку на статью Lazy Cjow Rhrr, и скорее всего там это все описано гораздо правильней, подробней и ясней, но я принципиально не читал ее, т.к. заказал журнал и жду, когда тот придет, чтоб прочитать в нем
Здравствуйте, buriy, Вы писали:
P>>>Если б эту статью уже выложили, а так какой толк сейчас? CVL>>А если почитать тред про эту статью, то можно найти вот такую ссылку B>Надеюсь, ты понимаешь, чем отличается полтора экрана полезного материала от нудного разглагольствования на 15 страниц текста?
Юрик, тебя в универе 5 лет учили отделять море нудного разглагольствования от грамма полезного
Да и вряд ли можно даже о таких понятиях карринг, замыкания и особенно лямбда разглагольствовать на 15ти! страницах.
B>Lambda: B>- имеет свободны переменные (не определенные в контексте, но используемые внутри функци) B>- при подстановке свободных переменных возвращает другую лямбду
Лямбда на практике просто безымянная функция и ничего больше Без всяких надуманных тобой ограничений.
B>Closure: B>- не имеет свободных переменных, т.е. это "закрытая" лямбда B>- при подстановке параметров возвращает обычный результат (не функцию) B>- формируется из лямбды, путем закрытия свободных переменных
Замыкание это просто функция которая помнит контекст в котором определена, она вообще не связана с лямбдой, например может быть простой функцией.
B>Т.е. при карринге каждый раз мы закрываем по одной свободной переменной. Можно остановится, сформировав новую лямбду (xLambda в примере), а можно закрыть все свободные переменные и получить closure (yLamda в примере). Еще можно записать так:
Карринг просто удобный способ получить новую ФВП.
B>Вопросы: B>- правильны ли эти краткие определения терминов?
по моему нет.
B>- какие отличия между closure и обычными (локальными) функциями?
В языках где функции первоклассные объекты различий обычно никаких. Это просто упрощенный способ объявления функции.
B>- делегаты C# 1.0, анонимные методы 2.0 и "лямбды" в 3.0 это вовсе и не лямбды, а closure?
B>>- какие отличия между closure и обычными (локальными) функциями?
FR>В языках где функции первоклассные объекты различий обычно никаких. Это просто упрощенный способ объявления функции.
Это я тут про лямбду
Между замыканиями и обычными функциями тоже различий никаких, любая функция может быть и замыканием.
Здравствуйте, ie, Вы писали:
B>>Lambda: B>>- имеет свободны переменные (не определенные в контексте, но используемые внутри функци)
ie>Совсем не обязательно. Лямбда может совсем не иметь свободных переменных: ie>
ie>lambda х, у: х+у
ie>
ОК.
B>>- при подстановке свободных переменных возвращает другую лямбду
ie>Что это значит я вообще не понял. Куда подставляются свободные переменные?
Под подстановкой я имею ввиду их связывание с контекстом. Т.е. привязку их к реальным переменным/значениям.
ie>Надо проще быть. Я лично для себя определил так: лямбда — это банальная функция, не больше не меньше и есть ли у нее свободные переменные уже не важно.
Пусть будет так.
Lambda (лямбда) — это функция, которая может иметь свободные переменные (т.е. переменные, используемые в функции, но не являющимися ее параметрами).
lambda (x) (x) => lambda, нет свободных переменных
lamda (x, p) (x + p) => тоже lambda, есть свободные переменные - p
B>>Closure: B>>- при подстановке параметров возвращает обычный результат (не функцию)
ie>Почему функция не может быть обычным результатом?
Согласен. Я хотел сделать акцент на ризличиях Closure и Lambda.
Ведь обычно лямбды возвращает функцию, а closure — простое значение, не функцию.
Но действительно, это не всегда так.
B>>- не имеет свободных переменных, т.е. это "закрытая" лямбда B>>- формируется из лямбды, путем закрытия свободных переменных
ie>Вот это уже ближе.
ie>Действительно, замыкания с лямбдами тесно связаны.
Как раз и хотелось четко (однозначно) определить эту связь.
ie>Замыкание — это функция которая имеет ссылки на свободные переменные. Заметь, она "закрывается" не значениями (как я думал еще вчера , привет АВК и Ллойду ), а ссылками на свободные переменные.
Скорее, замыкание имеет привязанные к контексту ссылки (в противопоставление лямбде, где ссылки тоже есть, но они не привязаны).
Итак.
Closure (замыкание) — это лямбда (?), переменные которой связаны с внешним контекстом (т.е. всем (?) ее свободным переменным присвоены конкретные значения/ссылки, определенные вне этой функции).
define makeAdder(n) (
lambda(x) (x + n)
)
inc := makeAdder(1) // closure, в лямбде связали переменную n со значением 1
// получили функцию это равносильную этой:
define inc(x) (x+1)
Будет ли полученная функция замыканием, если не будут привязаны к контексту все свободные переменные.
define makeFunc(n) (
lambda(x) (x + n + k) // n, k - свободные
)
// в лямбде свяжем переменную n со значением 1
func := makeFunc(1)
// получили такую функцию:
define func(x) (x + 1 + k)
Как видно, мы получили другую лямбду. Но является ли это замыканием? Я думаю, да, т.к. связывание с контекстом все таки было.
Т.е. из определения выше, условие о связывании всех свободных переменных надо убрать?
Будет ли полученная функция замыканием, если будет присвоено конкретное значение параметрам функции:
define makeFunc(x) (
lambda(x, y) (x + y)
)
// в лямбде свяжем переменную x со значением 1
func := makeFunc(1)
// получили такую функцию:
define func(y) (1 + y)
Это тоже замыкание?
B>>Currying: B>>- это процесс закрытия свободных переменных в лямбдах
ie>Нет. Карринг — это процесс закрытия связаных переменных.
Не согласен. Тогда уж неважно, какие переменные связывать — свободные или переменные-параметры (связанных).
ie>
ie>inc = (1+) -- тут каррировали (почти кастрировали, кстати, почти одно и тоже) функцию + (имеющюю 2 параметра), теперь получили функцию inc с одним параметром.
ie>
Итак.
Карринг — процесс связывания переменных в лямбдах.
Т.е. карринг — процесс связывание, замыкание — результат
B>>Вопросы: B>>- правильны ли эти краткие определения терминов? ie>см. выше
Вы согласны с измененной формулировкой?
B>>- делегаты C# 1.0, анонимные методы 2.0 и "лямбды" в 3.0 это вовсе и не лямбды, а closure?
Если трактовать лямбды, как написано сейчас, тогда да — лямбды
P.S. Вообще-то я хотел поговорить именно о терминологии, т.е. получить/найти определения — лямбда, замыкание, карринг. Т.е. объяснение не на примерах, а именно определение терминов.
Здравствуйте, FR, Вы писали:
FR>Лямбда на практике просто безымянная функция и ничего больше Без всяких надуманных тобой ограничений.
Уже согласен
FR>Замыкание это просто функция которая помнит контекст в котором определена, она вообще не связана с лямбдой, например может быть простой функцией.
На мой взгляд, замыкание определяется (создается) в момент выполнения программы. И это основное отличие от обычной функции. И что значит "простой функцией"?
FR>Карринг просто удобный способ получить новую ФВП.
Здравствуйте, Beam, Вы писали:
ie>>Почему функция не может быть обычным результатом?
B>Согласен. Я хотел сделать акцент на ризличиях Closure и Lambda. B>Ведь обычно лямбды возвращает функцию, а closure — простое значение, не функцию. B>Но действительно, это не всегда так.
Давай лучше так. И лямбды и замыкания всегда возвращают результат (отсутствие результата, как обычно, будем считать результатом), а что является этим результатом, функция или значение, суть дела не меняет.
ie>>Действительно, замыкания с лямбдами тесно связаны. B>Как раз и хотелось четко (однозначно) определить эту связь.
замыкание ::= (лямбда + ссылки на свободные переменные) — так пойдет ?
ie>>Замыкание — это функция которая имеет ссылки на свободные переменные. Заметь, она "закрывается" не значениями (как я думал еще вчера , привет АВК и Ллойду ), а ссылками на свободные переменные.
B>Скорее, замыкание имеет привязанные к контексту ссылки (в противопоставление лямбде, где ссылки тоже есть, но они не привязаны).
Не согласен. См. ниже
B>Итак. B>
B>Closure (замыкание) — это лямбда (?), переменные которой связаны с внешним контекстом (т.е. всем (?) ее свободным переменным присвоены конкретные значения/ссылки, определенные вне этой функции).
B>
B>define makeAdder(n) (
B> lambda(x) (x + n)
B>)
B>inc := makeAdder(1) // closure, в лямбде связали переменную n со значением 1
B>// получили функцию это равносильную этой:
B>define inc(x) (x+1)
B>
Вот на этом примере давай остановимся. У тебя
lambda(x) (x + n)
является лямбдой, но не является замыканием. Почему?
define makeAdder() (
n := 1
l := lambda(x) (x + n)
l(3)
)
Вот в этом примере является замыканием?
B>Будет ли полученная функция замыканием, если не будут привязаны к контексту все свободные переменные.
B>
B>define makeFunc(n) (
B> lambda(x) (x + n + k) // n, k - свободные (***)
B>)
B>// в лямбде свяжем переменную n со значением 1
B>func := makeFunc(1) (+++)
B>// получили такую функцию:
B>define func(x) (x + 1 + k)
B>
B>Как видно, мы получили другую лямбду. Но является ли это замыканием? Я думаю, да, т.к. связывание с контекстом все таки было.
Является, только замыкание у нас опять появилось в (***), а не в (+++). Еще раз повторю: замыкание ссылается на свободные переменные, а не на значения.
B>Т.е. из определения выше, условие о связывании всех свободных переменных надо убрать?
Если у тебя k не является переменной лексического контекста в котом находится лямбда, то я вообще не уверен, что такой код скомпилируется.
B>Будет ли полученная функция замыканием, если будет присвоено конкретное значение параметрам функции:
B>
B>define makeFunc(x) (
B> lambda(x, y) (x + y)
B>)
B>// в лямбде свяжем переменную x со значением 1
B>func := makeFunc(1)
B>// получили такую функцию:
B>define func(y) (1 + y)
B>
B>Это тоже замыкание?
Я выделил замыкание.
B>>>Currying: B>>>- это процесс закрытия свободных переменных в лямбдах
ie>>Нет. Карринг — это процесс закрытия связаных переменных.
B>Не согласен. Тогда уж неважно, какие переменные связывать — свободные или переменные-параметры (связанных).
Ссылки на свободные мы получаем благодаря замыканию и карринг тут не причем.
ie>>
ie>>inc = (1+) -- тут каррировали (почти кастрировали, кстати, почти одно и тоже) функцию + (имеющюю 2 параметра), теперь получили функцию inc с одним параметром.
ie>>
B>Итак. B>
B>Карринг — процесс связывания переменных в лямбдах.
B>Т.е. карринг — процесс связывание, замыкание — результат B>Вы согласны с измененной формулировкой?
Нет. Не согласен.
Карринг — создание новой функции путем задания значений одному или более агрументу:
f n p = ... -- тут был тип функции a -> a -> a
g = f 1 -- тут получили с типом a -> a
Никакие свободные переменные тут не учавствуют и учавствовать не могут.
B>На мой взгляд, замыкание определяется (создается) в момент выполнения программы. И это основное отличие от обычной функции. И что значит "простой функцией"?
Простая в смысле первоклассная функция, именованная не лямбда. Какая разница когда создается? Главное то что помнит контекст.
FR>>Карринг просто удобный способ получить новую ФВП.
B>Просто дай определения: лямбда — это ... и т.д.
А зачем?
Ищи в декларативном программирование, вроде были строгие определения.
Beam wrote: > P.S. Вообще-то я хотел поговорить именно о терминологии, т.е.получить/найти определения — лямбда, замыкание, карринг. Т.е. объяснение не на примерах, а именно определение терминов.
В таком случае проще прочесть в Википедии.
Из-за того, что в лямбда-исчислении, функции прекрасно обходятся без имен, конструкции языков программирования, позволяющие в контексте выражения определить безымянную функцию/процедуру, часто называют лямбда-функциями, чаще всего неправомерно, просто по аналогии с Лиспом, в котором такая конструкция появилась впервые.
Карринг — простое преобразование, позволяющее свести все функции к функциям одного аргумента. Если язык программирования поддерживает функции высшего порядка, такое преобразование всегда можно выполнить вручную (Scheme):
(define foo_curried
(lambda (x)
(lambda (y)
(foo x y)))))
Языки, о которых говорят, что они поддерживают карринг, это преобразование делают автоматически, т.е. (Ocaml)
let foo x y z = expr
(* эквивалентно *)let foo =fun x -> fun y -> fun z -> expr
Здравствуйте, ie, Вы писали:
ie>Давай лучше так. И лямбды и замыкания всегда возвращают результат (отсутствие результата, как обычно, будем считать результатом), а что является этим результатом, функция или значение, суть дела не меняет.
ОК.
ie>замыкание ::= (лямбда + ссылки на свободные переменные) — так пойдет ?
Что такое ссылки на свободные переменные? Если имеется ввиду ссылки на переменные, определенные во внешнем контексте, то да, согласен.
Если нет — тогда что?
B>>Скорее, замыкание имеет привязанные к контексту ссылки (в противопоставление лямбде, где ссылки тоже есть, но они не привязаны).
ie>Не согласен. См. ниже
Может я не понят. Я имею ввиду, что в лямбде описаны свободные переменные (внешние), но их значения не определены. А вот в замыкания они имеют конкретные значения/ссылки, т.е. привязаны к контексту.
ie>Вот на этом примере давай остановимся. У тебя ie>
ie>lambda(x) (x + n)
ie>
ie>является лямбдой, но не является замыканием. Почему?
Не является замыканием, потому что не имеет ни одной переменной, привязанной к внешнему контексту. Его вообще здесь нет
ie>
ie>define makeAdder() (
ie> n := 1
ie> l := lambda(x) (x + n)
ie> l(3)
ie>)
ie>
ie>Вот в этом примере является замыканием?
Да. Сделана привязка переменной n (она реально существует). В данном случае контекст — makeAdder.
B>>Будет ли полученная функция замыканием, если не будут привязаны к контексту все свободные переменные.
B>>
B>>define makeFunc(n) (
B>> lambda(x) (x + n + k) // n, k - свободные (***)
B>>)
B>>// в лямбде свяжем переменную n со значением 1
B>>func := makeFunc(1) (+++)
B>>// получили такую функцию:
B>>define func(x) (x + 1 + k)
B>>
B>>Как видно, мы получили другую лямбду. Но является ли это замыканием? Я думаю, да, т.к. связывание с контекстом все таки было.
ie>Является, только замыкание у нас опять появилось в (***), а не в (+++).
Не спорю, замыкание появилось в (***). Именно там была создана новая функция. В (+++) всего лишь присваивание. См. чуть ниже
ie>Еще раз повторю: замыкание ссылается на свободные переменные, а не на значения.
И с этим я не спорю. Просто в примерах используются значения.
B>>Будет ли полученная функция замыканием, если будет присвоено конкретное значение параметрам функции:
B>>
B>>define makeFunc(x) (
B>> lambda(x, y) (x + y)
B>>)
B>>// в лямбде свяжем переменную x со значением 1
B>>func := makeFunc(1)
B>>// получили такую функцию:
B>>define func(y) (1 + y)
B>>
ie>Я выделил замыкание.
Не понял. Похоже ты называешь замыканием описание лямбды, расположенной в каком либо контексте, а я — называю замыканием конкретную функцию, которая существует в программе (ну как объект) и создается динамически. Я понятно разницу объяснил?
B>>>>Currying: B>>>>- это процесс закрытия свободных переменных в лямбдах
ie>>>Нет. Карринг — это процесс закрытия связаных переменных.
B>>Не согласен. Тогда уж неважно, какие переменные связывать — свободные или переменные-параметры (связанных).
ie>Ссылки на свободные мы получаем благодаря замыканию и карринг тут не причем.
ОК. см. ниже
ie>>>
ie>>>inc = (1+) -- тут каррировали (почти кастрировали, кстати, почти одно и тоже) функцию + (имеющюю 2 параметра), теперь получили функцию inc с одним параметром.
ie>>>
B>>Итак. B>>
B>>Карринг — процесс связывания переменных в лямбдах.
B>>Т.е. карринг — процесс связывание, замыкание — результат B>>Вы согласны с измененной формулировкой?
ie>Нет. Не согласен. ie>Карринг — создание новой функции путем задания значений одному или более агрументу: ie>
ie>f n p = ... -- тут был тип функции a -> a -> a
ie>g = f 1 -- тут получили с типом a -> a
ie>
ie>Никакие свободные переменные тут не учавствуют и учавствовать не могут.
Начал писать пример, почему это неправда. И пришел к выводу, что ты прав
Карринг относится только к параметрам функции и уменьшает количество опять же параметров, но никак не свободных (внешних) переменных.
Здравствуйте, FR, Вы писали:
FR>Здравствуйте, Beam, Вы писали:
B>>На мой взгляд, замыкание определяется (создается) в момент выполнения программы. И это основное отличие от обычной функции. И что значит "простой функцией"?
FR>Простая в смысле первоклассная функция, именованная не лямбда. Какая разница когда создается? Главное то что помнит контекст.
FR>>>Карринг просто удобный способ получить новую ФВП.
B>>Просто дай определения: лямбда — это ... и т.д.
FR>А зачем? FR>Ищи в декларативном программирование, вроде были строгие определения.
Не нашел. Может плохо искал. Поэтому и задал вопрос
Здравствуйте, Трурль, Вы писали:
Т>Использование чужой терминологии в ИТ считается верхом неприличия, но «вообще говоря»:
Т>Лямбда-выражение – выражение с выделенными переменными-аргументами, обозначающее функцию этих агрументов.
а как же возможность обращаться к свободным переменным (не определенных в функции)?
Т>Замыкание – структрура данных для представления замкнутого функционального выражения.
Структура данных? Я думал, что замыкание — это функция.
Замкнутое выражение — это что?
Т>Карринг — представление функции нескольких переменных в виде функции, возвращающей функцию, возвращающую функцию и.т.д. по необходимости.
Ну вот. Карринг — это представление функцмм, или все-таки преобразование? Или и то, и другое.
P.S. Ну откуда-то термины появились. Должны же быть однозначные определения.
Я не хочу развязывать спор. Я просто хочу знать.
CiViLiS,
P>>Если б эту статью уже выложили, а так какой толк сейчас? CVL>А если почитать тред про эту статью, то можно найти вот такую ссылку
Статья неплохая , но там лямбды, карринг и замыкания даются в нетрадиционной для CS форме, то есть это совсем не определения, а выражение этих понятий через обычные понятия объекта и класса. Что впрочем, имхо, гораздо продуктивнее, чем зубрёжка определений.