メメメモモ

プログラミング、筋トレ、ゲーム、etc

クロージャの動作確認

クロージャというのは下記のようなものだと認識していました。

use strict;
use warnings;

sub create_closure {
    my $i = 0;
    my $c = sub { 
             $i++;
             print $i, "\n"; 
    };
    return $c;
}

my $c1 = create_closure();
$c1->();    # 1
$c1->();    # 2

my $c2 = create_closure();
$c2->();    # 1
$c2->();    # 2


こういう感じかー、と単純に考えていたら、引っかかったことがありました。

use strict;
use warnings;


sub create_closures {
    my @closures;
    for (my $i = 0; $i < 5; $i++) {
        push @closures, sub { print $i, "\n"; };
    }
    return @closures;
}

my @closures = create_closures();
$closures[0]->();  # 5
$closures[1]->();  # 5
$closures[2]->();  # 5
$closures[3]->();  # 5
$closures[4]->();  # 5


期待してた結果は「$closures[2]->();」は「2」という結果を返してくれることです。
しかし、すべて「5」になりました。
forのループの中で随時「sub { print 1, "\n"; };」 という感じで決まっていくのかと思ったのですが違うのですね。
スコープを抜けた時点で$iの値が決まるという事でしょうか。


下記のコードでは、期待通りの結果が得られました。

use strict;
use warnings;

sub create_closures {
    my @closures;
    for (my $i = 0; $i < 5; $i++) {
        push @closures, create_function($i);
    }
    return @closures;
}


sub create_function {
    my ($i) = @_;
    return sub { print $i, "\n"; };
}


my @closures = create_closures();
$closures[0]->();  # 0
$closures[1]->();  # 1
$closures[2]->();  # 2
$closures[3]->();  # 3
$closures[4]->();  # 4