开发者

How to run an anonymous function in Perl?

开发者 https://www.devze.com 2023-04-03 05:14 出处:网络
(sub { print 1; })(); sub { print 1; }(); I 开发者_StackOverflow社区tried various ways, all are wrong...(sub { ... }) will give you the pointer to the function so you must call by reference.
(sub {
print 1;
})();



sub {
print 1;
}();

I 开发者_StackOverflow社区tried various ways, all are wrong...


(sub { ... }) will give you the pointer to the function so you must call by reference.

(sub { print "Hello world\n" })->();

The other easy method, as pointed out by Blagovest Buyukliev would be to dereference the function pointer and call that using the { } operators

&{ sub { print "Hello World" }}();


Yay, I didn't expect you folks to come up with that much possibilities. But you're right, this is perl and TIMTOWTDI: +1 for creativitiy!

But to be honest, I use hardly another form than the following:

The Basic Syntax

my $greet = sub {
    my ( $name ) = @_;
    print "Hello $name\n";
};

# ...

$greet->( 'asker' )

It's pretty straight forward: sub {} returns a reference to a sub routine, which you can store and pass around like any other scalar. You can than call it by dereferencing. There is also a second syntax to dereference: &{ $sub }( 'asker' ), but I personally prefer the arrow syntax, because I find it more readable and it pretty much aligns with dereferencing hashes $hash->{ $key } and arrays $array->[ $index ]. More information on references can be found in perldoc perlref.

I think the other given examples are a bit advanced, but why not have a look at them:

Goto

sub bar {goto $foo};
bar;

Rarely seen and much feared these days. But at least it's a goto &function, which is considered less harmful than it's crooked friends: goto LABEL or goto EXPRESSION ( they are deprecated since 5.12 and raise a warning ). There are actually some circumstances, when you want to use that form, because this is not a usual function call. The calling function ( bar in the given example ) will not appear in the callling stack. And you don't pass your parameters, but the current @_ will be used. Have a look at this:

use Carp qw( cluck );

my $cluck = sub {
    my ( $message ) = @_;
    cluck $message . "\n";
};


sub invisible {
    @_ = ( 'fake' );
    goto $cluck;
}

invisible( 'real' );

Output:

fake at bar.pl line 5
    main::__ANON__('fake') called at bar.pl line 14

And there is no hint of an invisible function in the stack trace. More info on goto in perldoc -f goto.

Method Calls

''->$foo;
# or
undef->$foo;

If you call a method on an object, the first parameter passed to that method will be the invocant ( usually an instance or the class name ). Did i already say that TIMTOWTCallAFunction?

# this is just a normal named sub
sub ask {
    my ( $name, $question ) = @_;
    print "$question, $name?\n";
};

my $ask = \&ask; # lets take a reference to that sub 

my $question = "What's up";

'asker'->ask( $question ); # 1: doesn't work

my $meth_name = 'ask';
'asker'->$meth_name( $question ); # 2: doesn't work either

'asker'->$ask( $question ); # 1: this works

In the snippet above are two calls, which won't work, because perl will try to find a method called ask in package asker ( actually it would work if that code was in the said package ). But the third one succeeds, because you already give perl the right method and it doesn't need to search for it. As always: more info in the perldoc I can't find any reason right now, to excuse this in production code.

Conclusion

Originally I didn't intend to write that much, but I think it's important to have the common solution at the beginning of an answer and some explanations to the unusual constructs. I admit to be kind of selfish here: Every one of us could end up maintaining someones code, who found this question and just copied the topmost example.


There is not much need in Perl to call an anonymous subroutine where it is defined. In general you can achieve any type of scoping you need with bare blocks. The one use case that comes to mind is to create an aliased array:

my $alias = sub {\@_}->(my ($x, $y, $z));

$x = $z = 0;
$y = 1;

print "@$alias"; # '0 1 0'

Otherwise, you would usually store an anonymous subroutine in a variable or data structure. The following calling styles work with both a variable and a sub {...} declaration:

dereference arrow:  sub {...}->(args)  or  $code->(args)

dereference sigil:  &{sub {...}}(args) or &$code(args)

if you have the coderef in a scalar, you can also use it as a method on regular and blessed values.

my $method = sub {...};

$obj->$method           # same as $method->($obj)
$obj->$method(...)      # $method->($obj, ...)

[1, 2, 3]->$method      # $method->([1, 2, 3])
[1, 2, 3]->$method(...) # $method->([1, 2, 3], ...)


I'm endlessly amused by finding ways to call anonymous functions:

$foo = sub {say 1};

sub bar {goto $foo};
bar;

''->$foo; # technically a method, along with the lovely:

undef->$foo;

() = sort $foo 1,1; # if you have only two arguments

and, of course, the obvious:

&$foo();
$foo->();


You need arrow operator:

(sub { print 1;})->();


You might not even need an anonymous function if you want to run a block of code and there is zero or one input. You can use map instead.

Just for the side effect:

map { print 1 } 1;

Transform data, take care to assign to a list:

my ($data) = map { $_ * $_ } 2;


# ------------------------------------------------------
# perl: filter array using given function
# ------------------------------------------------------
sub filter {
   my ($arr1, $func) = @_;
   my @arr2=();
   foreach ( @{$arr1} ) {
        push ( @arr2, $_ ) if $func->( $_ );
   };
   return @arr2;
}

# ------------------------------------------------------
# get files from dir
# ------------------------------------------------------
sub getFiles{
 my ($p) = @_;
 opendir my $dir, $p  or die "Cannot open directory: $!";
 my @files=readdir $dir;
 closedir $dir;
 #return files and directories that not ignored but not links
 return filter \@files, (sub { my $f = $p.(shift);return ((-f $f) || (-d $f)) && (! -l $f) } ); 
}
0

精彩评论

暂无评论...
验证码 换一张
取 消