注意:这个问题中的闭包只是一个方便的例子;我实际上正在使用的那个比这要复杂得多。 IOW,请忽略此关闭的详细信息; AFAICT 最重要的是它引用父作用域中的词法变量。
我想重新定义子 foo
下面,以便调用 List::Util::reduce
中的第一个参数替换为对嵌套闭包的引用。
use strict;
use warnings FATAL => 'all';
use List::Util;
sub foo {
my ( $x, $y ) = @_;
return List::Util::reduce { $y->[ $b ]{ $x } ? $a + ( 1 << $b ) : $a } 0, 0 .. $#$y;
}
我的第一次尝试是这样的:
sub foo {
my ( $x, $y ) = @_;
sub z {
our ( $a, $b );
return exists $y->[ $b ]{ $x } ? $a | ( 1 << $b ) : $a;
}
return List::Util::reduce \&z, 0, 0 .. $#{ $y };
}
...但这会导致警告说
Variable "$y" will not stay shared
.我以前见过这种错误,我知道的唯一方法是替换嵌套的
sub
将匿名子分配给变量的定义。因此,我尝试了这个:sub foo {
my ( $x, $y ) = @_;
my $z = sub {
our ( $a, $b );
return exists $y->[ $b ]{ $x } ? $a | ( 1 << $b ) : $a;
};
return List::Util::reduce( $z, 0, 0 .. $#{ $y } );
}
现在错误显示
Type of arg 1 to List::Util::reduce must be block or sub {} (not private variable)
.这个我真的不明白。为什么要将子引用作为第一个参数传递给
reduce
不被支持?PS:以下确实有效:
sub foo {
my ( $x, $y ) = @_;
my $z = sub {
our ( $a, $b );
return exists $y->[ $b ]{ $x } ? $a | ( 1 << $b ) : $a;
};
return List::Util::reduce { $z->( $a, $b ) } 0, 0 .. $#{ $y };
}
...但我真的很想知道 (a) 使用子引用作为
List::Util::reduce
的第一个参数有什么问题(例如,支持此变体是否存在一些技术障碍?); (b) 是否有更直接的方法(比 { $z->( $a, $b ) }
)传递给 List::Util::reduce
嵌套闭包?
最佳答案
List::Util::reduce
使用 Perl 原型(prototype),它指定对调用中传递的参数进行编译时检查。传递包含子例程引用的标量变量在编译时无法验证,因此 Perl 不允许它
这也是解决问题的途径。使用 & 字符调用子程序通常被认为是不好的做法 &
但在这种情况下,它会破坏原型(prototype)检查,这是必需的
sub foo {
my ( $x, $y ) = @_;
my $z = sub {
our ( $a, $b );
return exists $y->[ $b ]{ $x } ? $a | ( 1 << $b ) : $a;
};
return &List::Util::reduce( $z, 0, 0 .. $#{ $y } ); # Defeat prototype
}
请注意,这通常是不可能的,因为原型(prototype)可以执行诸如强制数组或散列通过引用传递,或强制表达式上的标量上下文以及使用
&
调用此类子例程之类的事情。也会禁用这些行为。但是reduce
原型(prototype)为 &@
这意味着一个 block 或sub { }
(sub
部分在这里是可选的)后跟标量列表,这是没有原型(prototype)的子程序的正常行为,因此唯一的效果是允许 $z
传递给子程序引用
关于closures - 如何使用嵌套闭包作为 List::Util::reduce 的第一个参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36237274/