如何使用嵌套闭包作为List :: Util :: reduce的第一个参数?

kjo*_*kjo 5 perl closures perl5

注意:这个问题中的闭包只是一个方便的例子; 我实际使用的那个比这复杂得多.IOW,请忽略这个关闭的细节; 重要的是AFAICT,它指的是父范围内的词汇变量.


我想重新定义foo下面的sub ,以便调用中的第一个参数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;
}
Run Code Online (Sandbox Code Playgroud)

我的第一次尝试是这样的:

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 };
}
Run Code Online (Sandbox Code Playgroud)

......但这会导致警告说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 } );
}
Run Code Online (Sandbox Code Playgroud)

现在错误说Type of arg 1 to List::Util::reduce must be block or sub {} (not private variable).

这个我真的不明白.为什么将subref作为第一个参数传递给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 };
}
Run Code Online (Sandbox Code Playgroud)

...但我真的想知道(a)使用subref作为第一个参数List::Util::reduce有什么问题(例如,是否存在支持这种变体的技术障碍?); (b)是否有更直接的方法(而不是{ $z->( $a, $b ) })传递给List::Util::reduce嵌套的封闭?

ike*_*ami 3

reduce有一个原型&@。这意味着调用必须使用以下语法之一:

reduce BLOCK LIST
reduce sub BLOCK, LIST  # The first form is short for this.
reduce \&NAME, LIST
reduce \&$NAME, LIST    # Same as third form, but via reference (short form)
reduce \&BLOCK, LIST    # Same as third form, but via reference (long form)
Run Code Online (Sandbox Code Playgroud)

您可以使用以下任何等效语句:

return reduce { exists $y->[ $b ]{ $x } ? $a | ( 1 << $b ) : $a } 0, 0 .. $#$y;
Run Code Online (Sandbox Code Playgroud)

# Long way of writing the first.
return reduce(sub { exists $y->[ $b ]{ $x } ? $a | ( 1 << $b ) : $a }, 0, 0 .. $#$y);
Run Code Online (Sandbox Code Playgroud)

my $z = sub { exists $y->[ $b ]{ $x } ? $a | ( 1 << $b ) : $a };
return reduce(\&$z, 0, 0 .. $#$y);
Run Code Online (Sandbox Code Playgroud)

您还可以选择覆盖原型。

my $z = sub { exists $y->[ $b ]{ $x } ? $a | ( 1 << $b ) : $a };
return &reduce($z, 0, 0 .. $#$y);
Run Code Online (Sandbox Code Playgroud)