Borrowing the the term form a Javascript, what is the 'best practice' way to IIFE in perl?
My test code below is a simple loop calling anonymous sub and executing it right away to build an array of subs (which just return the loop index). It is mostly what I want, however I'm needing to use an intermediate variable ( instead of using @_, which changes on the internal function).
use warnings;
use strict;
my @funcs;
for(my $i=0;$i<3;$i++) {
sub {
my $index=shift;
$funcs[$index]=sub {$index};
}
-> ($i);
}
for (@funcs) {
print &$_()."\n";
}
#Output
0
1
2
I know I can use map
to restructure this problem. But putting that aside, is there a better way to do this?
Update
Thanks to @ikegami for highlighting some important points.
Just for future views of this question, my thoughts on this:
The 'iterator' for loop has different scoping (is it a map?) than a 'c style' for loop. That cleans up the code with out needing an IIFE at all. Sweet.
Update 2
Following code shows the differences I'm seeing. Not saying one is better than the other but good to know I think. The output I'm after is 0 1 2 but the first one only repeats the last value of $i (3 after the ++ operator).
use warnings;
use strict;
my @funcs;
print "C loop direct assignment of sub\n";
for(my $i=0;$i<3;$i++) {
$funcs[$i]= sub {$i};
}
print &$_()."\n" for @funcs;
print "C loop direct assignment of sub with variable\n";
for(my $i=0;$i<3;$i++) {
my $index=$i; #assignment/copy
$funcs[$index]= sub {$index};
}
print &$_()."\n" for @funcs;
print "For loop interator\n";
@funcs=[];
for my $i (0..2) {
$funcs[$i]=sub {$i};
}
print &$_()."\n" for @funcs;
print "C loop with IIFE assignment\n";
@funcs=[];
for (my $i=0;$i<3;$i++) {
sub {
my $index=shift;
$funcs[$index]=sub {$index};
}
-> ($i);
}
print &$_()."\n" for @funcs;
Out is:
C loop direct assignment of sub
3
3
3
C loop direct assignment of sub with variable
0
1
2
For loop interator
0
1
2
C loop with IIFE assignment
0
1
2
The Perl equivalent of
(function () {
var x = ...;
...
})();
is
sub {
my $x = ...;
...
}->();
That said, the IIFE is just a workaround that's simply not needed in Perl.
(function () {
var x = ...;
...
})();
is a workaround for
{
my $x = ...;
...
}
and
var result = (function () {
return ...;
})();
is a workaround for
my $result = do {
...
};
It looks like you are trying to translate something similar to the following:
let funcs = [];
for ( let i=0; i<3; ++i ) {
(function() {
var index = i;
funcs.push( function() { return index; } );
})();
}
for ( let func of funcs )
console.log( func() );
The following is the direct translation:
my @funcs;
for ( my $i=0; $i<3; ++$i ) {
sub {
my $index = $i;
push @funcs, sub { $index };
}->();
}
say $_->() for @funcs;
But there's simply no point in using an IIFE. One would simply use the following:
my @funcs;
for ( my $i=0; $i<3; ++$i ) {
my $index = $i;
push @funcs, sub { $index };
}
say $_->() for @funcs;
Now, one tends to avoid C-style for loops in Perl because using foreach loops are far more readable (and more efficient!). It also makes the solution even simpler because the loop variable of a foreach loop is scoped to the body of the loop rather than scoped to the loop statement.
my @funcs;
for my $i ( 0 .. 2 ) {
push @funcs, sub { $i };
}
say $_->() for @funcs;
We could also use map
.
my @funcs = map { my $i = $_; sub { $i } } 0 .. 2;
say $_->() for @funcs;