/prog/ trampolines
Aug. 20th, 2005 09:15 amПросто красивые примеры простейших трамплинов на питоне и перле. (Сейчас меня функциональщики пинать начнут;)) Да, пока не убежало - в чём их отличие от closures (замыканий)?
Начнём с питона.
При запуске рисует:
К сожалению, не получилось сделать просто "lambda y: print x,y" - питон не даёт воспринять оператор (statement) как выражение (expression). Впрочем, это даже в Си не всегда просто - не зря gcc завёл расширение в виде ({...}).
Как можно было бы наивно попробовать сделать то же самое на перле:
В результате печатает:
Не то. А вот как лечится (простейшим способом):
do я поставил для красоты. В чём хитрость? В том, что мы создаём копию переменной, затем в новосозданной функции ссылаемся на неё, затем выходим из области определения... переменная теряет ссылку на себя в блоке кода, ссылок на неё кроме как из функции нету.
То же самое с промежуточной функцией-создателем, показывая, что неважно, сколько раз выполняется код с такой "подвешиваемой" переменной:
Пишет в обоих случаях:
то есть новосозданная функция запоминает ссылку на переменную, которая по выходу из блока становится доступной только ей и сохраняет последнее значение, которое у неё было до выхода из блока.
А теперь поиздеваемся над "подвешенной" переменной:
Выполняем:
Ну не хакерство? :))))
Компилируемые языки решают подобную задачу значительно грубее - через дополнительный параметр (который обычно void* или что-то подобное). Иногда мне хотелось без этого (а практически всегда для ясности кода приходилось строить промежуточную структуру) сделать функцию-трамплин, которая бы дополняла аргументами.
P.S. А почему Россум хочет отменить лямбду? "Мама, мама, как я буду жить?"
UPDATE: в ru.programming.languages внушают: если я напишу s1 = lambda y: myprint(1,y) - это ещё не замыкание. А вот если вынесу в функцию которую буду вызывать как make_myprint(1) - уже замыкание. Однако много гитик, много.
Начнём с питона.
def myprint(x,y): print x, y if __name__ == '__main__': f1 = lambda y: myprint(1, y) f2 = lambda y: myprint(2, y) f1(3) f1(4) f2(5) f2(6)
При запуске рисует:
1 3 1 4 2 5 2 6
К сожалению, не получилось сделать просто "lambda y: print x,y" - питон не даёт воспринять оператор (statement) как выражение (expression). Впрочем, это даже в Си не всегда просто - не зря gcc завёл расширение в виде ({...}).
Как можно было бы наивно попробовать сделать то же самое на перле:
my $x;
$x = 1;
$s1 = sub { return $x; };
$x = 2;
$s2 = sub { return $x; };
print &$s1(), "\n";
print &$s2(), "\n";
В результате печатает:
2 2
Не то. А вот как лечится (простейшим способом):
my $x;
$x = 1;
$s1 = do { my $xx = $x; sub { return $xx; }; };
$x = 2;
$s2 = do { my $xx = $x; sub { return $xx; }; };
print &$s1(), "\n";
print &$s2(), "\n";
do я поставил для красоты. В чём хитрость? В том, что мы создаём копию переменной, затем в новосозданной функции ссылаемся на неё, затем выходим из области определения... переменная теряет ссылку на себя в блоке кода, ссылок на неё кроме как из функции нету.
То же самое с промежуточной функцией-создателем, показывая, что неважно, сколько раз выполняется код с такой "подвешиваемой" переменной:
sub gene {
my $x = shift;
my $s = sub { return $x; };
return $s;
}
sub main {
$s1 = &gene(1);
$s2 = &gene(2);
print &$s1(), "\n";
print &$s2(), "\n";
}
&main();
Пишет в обоих случаях:
1 2
то есть новосозданная функция запоминает ссылку на переменную, которая по выходу из блока становится доступной только ей и сохраняет последнее значение, которое у неё было до выхода из блока.
А теперь поиздеваемся над "подвешенной" переменной:
sub gene {
my $x = shift;
my $s = sub { $x += 10; return $x; };
return $s;
}
sub main {
$s1 = &gene(1);
$s2 = &gene(2);
print &$s1(), "\n";
print &$s2(), "\n";
print &$s1(), "\n";
print &$s2(), "\n";
}
&main();
Выполняем:
11 12 21 22
Ну не хакерство? :))))
Компилируемые языки решают подобную задачу значительно грубее - через дополнительный параметр (который обычно void* или что-то подобное). Иногда мне хотелось без этого (а практически всегда для ясности кода приходилось строить промежуточную структуру) сделать функцию-трамплин, которая бы дополняла аргументами.
P.S. А почему Россум хочет отменить лямбду? "Мама, мама, как я буду жить?"
UPDATE: в ru.programming.languages внушают: если я напишу s1 = lambda y: myprint(1,y) - это ещё не замыкание. А вот если вынесу в функцию которую буду вызывать как make_myprint(1) - уже замыкание. Однако много гитик, много.
no subject
Date: 2005-08-20 08:52 am (UTC)Нашёл сейчас интервью с Гвидо Ван Россумом за 1998 год (http://www.amk.ca/python/writing/gvr-interview.html). Судя по всему, он уже который год недоволен этой лямбдой. Неужели посли стольки лет у него поднимется рука? :)
no subject
Date: 2005-08-20 04:21 pm (UTC)по-моему, я рядом ответил себе, а не...
no subject
Date: 2005-08-21 03:10 pm (UTC)no subject
Date: 2005-08-21 03:25 pm (UTC)no subject
Date: 2005-08-25 08:05 pm (UTC)Wikipedia - Closure (computer science) (http://en.wikipedia.org/wiki/Closure_(computer_science))
Wikipedia - Trampoline (computer science) (http://en.wikipedia.org/wiki/Trampoline_(computers))
no subject
Date: 2005-08-25 08:31 pm (UTC)несмотря на то, что действие функции из этого кода понятно однозначно и независимо от всего остального:) Обстановка (значение x) на момент создания зафиксировалась? Да. Ч.Т.Д.
Опровергайте :)
Впрочем, сильно я этим заморачиваться всё равно не буду:)
no subject
Date: 2005-08-25 09:05 pm (UTC)настоящее замыкание.
no subject
Date: 2005-08-25 11:49 pm (UTC)Ничего не зафиксируется. Питон не использует глобальные переменные для создания замыканий. Просто используется ссылка на нее по имени.Наличие замыкания для функции в питоне можно определить:
print f.func_closure
если вернет None - замыкания нет.
А вот следующее, действительно называется "зафиксировалось".
# Если-бы x определить перед функцией, то все равно формальный # аргумент "закроет функцию" от "глобального имени икс". def foo(x): z = 20 return lambda y: x + y + z x = 10 f = foo(x) f(1) # 31 del x del foo f(1) # 31no subject
Date: 2005-08-26 05:50 am (UTC)Получается, что питон и перл сделали это одинаково: просто захватывают переменную ссылкой на неё, а корректная работа в отдельной функции получается за счёт того, что в ней копируется значение переданной переменной в отдельную переменную (которая и фиксируется). М-да, таки хакерство;)))
Про func_closure не знал, спасибо. Вообще организация документации питона ужасна.