Собственно, вот! :)
Действительно проблема. Я понимаю, что математическая культура, техника в
руках дикаря, голова должна быть и так далее :) Hо цель не отсепарировать
математиков, а дать _простое_ объяснение, "на пальцах". Чтобы было понятно и
человеку, который является специалистом (!) в другой области. Пока что попытка
обсудить методологию приводила в основном к флейму... Определения есть, не в
этом дело. Требуется корректное "вхождение в проблему", чтобы стало понятно не
только, как оно делается, но и зачем.
bye
IT> Действительно проблема. Я понимаю, что математическая культура,
IT> техника в руках дикаря, голова должна быть и так далее :) Hо цель
IT> не отсепарировать математиков, а дать _простое_ объяснение, "на
IT> пальцах". Чтобы было понятно и человеку, который является
IT> специалистом (!) в другой области. Пока что попытка обсудить
IT> методологию приводила в основном к флейму... Определения есть, не
IT> в этом дело. Требуется корректное "вхождение в проблему", чтобы
IT> стало понятно не только, как оно делается, но и зачем.
Жопаскрипт:
function makeAddition(n){
function addN(m){
return n+m;
}
return addN;
}
Берём максимально тупую реализацию, ничего не знающую про замыкания.
Вызываем:
var f = makeAddition(2);
var z = f(3);
Получаем фигню: функция f становится у нас равна addN, но ей для
функционирования требуется переменная n, которой уже нет, потому что
makeAddition уже закончилась.
В языке C мы получим либо мусор, либо вообще какой-нибудь GPF.
В языке с динамическим связыванием мы можем получить не совсем то,
чего ждали, если есть глобальная переменная n:
var f = makeAddition(2);
var n = 10;
var z = f(3);
z (если связывание динамическое) станет равно 13.
В языке с замыканиями функция f будет "помнить" не только то, что она
равна addN, но и то, что переменная n есть 2, и в результате z станет
равно 5. Вот эта фиговина - функция, которая "помнит" локальные
переменные, видимые при её создании, и есть замыкание. При этом может
быть и другая функция, которая "помнит" другие локальные переменные:
var f = makeAddition(2);
var g = makeAddition(3);
var x = f(3);
var z = g(3);
Хотя и f и g - это функция addN, тем не менее x будет равно 5, а z -
6. Потому что f "помнит" n=2, а g "помнит" n=3.
На самом деле, всё может быть ещё интереснее, если использовать
переменные, которые можно изменять. В этом случае нужно иметь в виду,
что замыкание "помнит" не значение переменной, а ссылку на неё:
function makeCounter(){
var val = 0;
function inc(){
val++;
}
function value(){
return val;
}
return {inc: inc; value: value};
}
var counter1 = makeCounter();
var counter2 = makeCounter();
var inc1 = counter1.inc;
var value1 = counter1.value;
var inc2 = counter2.inc;
var value2 = counter2.value;
inc1();
inc1();
inc2();
inc1();
inc2();
var x = value1();
var z = value2();
Теперь x=3 (потому что переменная val из ПЕРВОГО вызова makeCounter
была увеличена трижды), а z=2.
--
Miguel migue...@yandex.ru
LJ migmit http://miguel-0.narod.ru
IT> Действительно проблема. Я понимаю, что математическая культура,
IT> техника в руках дикаря, голова должна быть и так далее :) Hо цель не
IT> отсепарировать математиков, а дать _простое_ объяснение, "на пальцах".
IT> Чтобы было понятно и человеку, который является специалистом (!) в
IT> другой области. Пока что попытка обсудить методологию приводила в
IT> основном к флейму... Определения есть, не в этом дело. Требуется
IT> корректное "вхождение в проблему", чтобы стало понятно не только, как
IT> оно делается, но и зачем.
Самое простое объяснение -- аналогия с математическим определением
"f(x) := x + y, где y=10".
Вот это "где y=10" является "окружением", связанным с функцией f, а сама f
является closure.
Зачем это делается -- чтобы была возможность вызывать функцию, связанную с
какими-либо значениями, удобным образом. В случае математики довольно просто
переписать f так, чтобы избавиться от окружения: f(x) := x + 10 (хотя в случае
больших функций переписывание тоже зачастую неудобно). В случае современных
процессоров переписывать машинный код (в том числе создавать новый кусок кода
для каждого случая разных y), одновременно сохраняя хорошую производительность,
безопасность и кроссплатформенность, весьма геморройно (хотя и такое делают при
желании).
Получается, что гораздо проще таскать рядом и указатель на машинный код, и
окружение, требуемое для выполнения кода. Таким образом код не меняется, а
меняется окружение.
В случае f(x) имеем 1) указатель на функцию, складывающую первый аргумент (x)
со значением, хранящимся в окружении (y) и 2) само окружение, состоящее из
значения y.
А зачем человеку нужно знать, что такое closure и как оно работает?
bye
DG> Самое простое объяснение -- аналогия с математическим определением
DG> "f(x) := x + y, где y=10".
DG> Вот это "где y=10" является "окружением", связанным с функцией f, а
DG> сама f является closure.
Действительно, без лишних определений. Спасибо, выглядит вполне доходчиво.
DG> А зачем человеку нужно знать, что такое closure и как оно работает?
При разработке процессорных архитектур полезно знать основные концепции языков
программирования. Тем более что для тех же замыканий можно, к примеру, ввести
аппаратную поддержку комплексных указателей - на функцию и на окружение... Hу
и т.д. - по каждой программной технологии может что-нибудь придуматься.
bye