netch: (Default)
[personal profile] netch
Просто красивые примеры простейших трамплинов на питоне и перле. (Сейчас меня функциональщики пинать начнут;)) Да, пока не убежало - в чём их отличие от closures (замыканий)?

Начнём с питона.

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) - уже замыкание. Однако много гитик, много.
This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

Profile

netch: (Default)
netch

December 2023

S M T W T F S
     12
3456789
10111213141516
171819 20212223
24252627282930
31      

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 6th, 2026 08:34 pm
Powered by Dreamwidth Studios