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) - уже замыкание. Однако много гитик, много.

Profile

netch: (Default)
netch

December 2023

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

Most Popular Tags

Page Summary

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 3rd, 2026 05:02 am
Powered by Dreamwidth Studios