尽管子例程作为块参数传递,但以下代码的第一次打印不会打印预期结果。预期打印 1,但打印 2。第一个和第二个 print 语句有什么区别?
my @arr = ("hello", "world");
print scalar(grep (sub {return "hello" eq $_}, @arr)); # return 2, not expected
print scalar(grep {"hello" eq $_} @arr); # return 1 as expected
Run Code Online (Sandbox Code Playgroud)
grep需要 aBLOCK或 an EXPR。
在您的第二个(工作)公式中,您提供了一个块,它按预期使用本地$_值进行评估。
在您的第一个表述中,您提供了一个表达式。该表达式恰好是一个匿名子例程,$_其中每个被 grep 的元素都有边界,但子例程本身不被评估。
map如果你愿意而不是grep输入列表,你可以更清楚地看到发生了什么:
> print "[", join(",", map (sub {return "hello" eq $_}, @arr)), "]\n";
[CODE(0x14d82bdc8),CODE(0x14d82bdc8)]
> print "[", join(",", map {"hello" eq $_} @arr), "]\n";
[1,]
Run Code Online (Sandbox Code Playgroud)
当然,您可以将临时子例程作为 grep 表达式的一部分进行计算,但语法很快就会变得非常漂亮:
print scalar(grep ((sub {return "hello" eq $_})->(), @arr)); # returns 1
Run Code Online (Sandbox Code Playgroud)
Perl 接受块参数的“函数”的语法有点奇怪,这是我对 Perl 的烦恼之一。有些事情很奇怪,因为 Perl 就是这样做的:
grep {...} @array; # no comma, block argument
grep $_ == 4, @array # comma, expression argument
Run Code Online (Sandbox Code Playgroud)
添加 看起来sub不像 Perl 的块参数,因为这不是 Perl 解析事物的方式:
grep sub { $_ == 4} @array # missing comma, expression argument, compilation error
grep sub { $_ == 4} @array # comma, expression argument
Run Code Online (Sandbox Code Playgroud)
但是,当您使用这种特殊的块形式时,这会起作用sub,并且 Perl 知道如何解析这些特殊情况,因为 Perl 知道如何解析这些特殊情况:
$ perl -MO=Deparse -e 'my @f = grep { $_ == 4 } @array'
my(@f) = grep({$_ == 4;} @array);
-e syntax OK
$ perl -MO=Deparse -e 'my @f = grep $_ == 4, @array'
my(@f) = grep(($_ == 4), @array);
-e syntax OK
$ perl -MO=Deparse -e 'my @f = grep sub{$_ == 4}, @array'
my(@f) = grep(sub {
$_ == 4;
}
, @array);
-e syntax OK
$ perl -MO=Deparse -e 'my @f = grep sub{$_ == 4} @array'
Array found where operator expected at -e line 1, near "} @array"
(Missing operator before @array?)
syntax error at -e line 1, near "} @array
"
-e had compilation errors.
Run Code Online (Sandbox Code Playgroud)
就是这样。我希望 Perl 对匿名函数有一个更一般的概念,这也是 Raku 解决的问题之一。我认为 Ruby 在可选块方面也做得很好。
现在,让我们f使用原型来创建我们自己的函数,并使用块参数(这通常不是最好的主意)。对于用户定义的函数来说,情况与 Perl 的内置函数略有不同(我知道这很令人抓狂):
给f个块,没问题:
$ perl -MO=Deparse -e 'sub f (&) { $_[0]->() }; print f {137}'
sub f (&) {
$_[0]->();
}
print f(sub {
137;
}
);
-e syntax OK
Run Code Online (Sandbox Code Playgroud)
给f一个匿名的sub,没问题:
$ perl -MO=Deparse -e 'sub f (&) { $_[0]->() }; print f sub {137}'
sub f (&) {
$_[0]->();
}
print f(sub {
137;
}
);
-e syntax OK
Run Code Online (Sandbox Code Playgroud)
但是,使用括号,Perl 会认为该块是匿名哈希,即使您试图欺骗 Perl 将其视为代码块:
$ perl -MO=Deparse -e 'sub f (&) { $_[0]->() }; print f({137})'
Type of arg 1 to main::f must be block or sub {} (not anonymous hash ({})) at -e line 1, near "})
"
-e had compilation errors.
sub f (&) {
$_[0]->();
}
print &f({137});
$ perl -MO=Deparse -e 'sub f (&) { $_[0]->() }; print f({137;})'
syntax error at -e line 1, near ";}"
-e had compilation errors.
sub f (&) {
$_[0]->();
}
$ perl -MO=Deparse -e 'sub f (&) { $_[0]->() }; print f({return 137})'
Type of arg 1 to main::f must be block or sub {} (not anonymous hash ({})) at -e line 1, near "})
"
-e had compilation errors.
sub f (&) {
$_[0]->();
}
print &f({(return 137)});
Run Code Online (Sandbox Code Playgroud)
有时事情就是这样。