perl 正在获取变量“@xml_files”将不会在...行保持共享

enxuqcxy  于 2022-11-15  发布在  Perl
关注(0)|答案(3)|浏览(104)

下面是一个例子:

sub merge_xml {

  foreach my $repository ('repo1', 'repo2') {
    my @xml_files;

    sub match_xml {
      my $filename = $File::Find::dir . '/' . $_;
      if ($filename =~ m%/(main|test)\.xml$%) {
        push(@xml_files, $filename);
      }
    }

    find(\&match_xml, $repository);

    print Dumper(\@xml_files);
  }
}

我得到警告:

Variable "@xml_files" will not stay shared at ./script/src/repair.pl line 87.

如何解决这个问题?
PS查找与File::Find相同

bq8i3lrv

bq8i3lrv1#

“嵌套的”命名子例程实际上不是--它们被编译为单独的子例程,因此将它们写为“嵌套的”只会产生误导。
此外,这会产生一个问题,因为“inner”子例程应该关闭它所使用的变量@xml_files,该变量在每次新调用时都会被重新定义,成为词法闭包。但是,在编译时构建的sub子例程不是词法闭包,它只在第一次调用时保留对该值的引用,因此它只在第一次调用外部sub子例程(这里是merge_xml)时才起作用。
不过我们确实收到了警告。使用use diagnostics;(或者在perldiag中查看)
变量“$x”将不会在-e行1(#1)中共享
(W闭包)内部(嵌套)命名子例程正在引用外部命名子例程中定义的词法变量。
当调用内部子例程时,它将看到外部子例程的变量的值,就像它在 * 第一次 * 调用外部子例程之前和期间一样;在这种情况下,在第一次调用外部子程序完成后,内部和外部子程序将不再共享变量的公共值。2换句话说,变量将不再被共享。
这个问题通常可以通过使用sub {}语法使内部子例程匿名来解决。当引用外部子例程中的变量的内部匿名子例程被创建时,它们会自动重新绑定到这些变量的当前值。
因此,取出“内部”子函数(match_xml),并正常使用“外部”子函数(merge_xml)。或者由于在这种情况下不能传递到X1 M6 N1 X的X1 M7 N1 X,因此可以使数组处于这样的范围内,以便被视为是需要的。
或者,由于match_xml的目的是作为find的“wanted”函数,因此可以使用匿名sub来实现该目的,这样就不需要单独的命名sub

find( sub { ... }, @dirs );

或者将coderef存储在变量中,如Ed Heal's answer所示

my $wanted_coderef = sub { ... };
find( $wanted_coderef, @dirs );
uidvcgyl

uidvcgyl2#

zdim的帮助下,我想出了:

sub merge_xml {

  foreach my $repository ('repo1', 'repo2') {
    my @xml_files;

    my match_xml = sub {
      my $filename = $File::Find::dir . '/' . $_;
      if ($filename =~ m%/(main|test)\.xml$%) {
        push(@xml_files, $filename);
      }
    };

    find($match_xml, $repository);

    print Dumper(\@xml_files);
  }
}
csga3l58

csga3l583#

我可以建议另一种选择吗?通过使用工厂函数,您可以消除每次手工编写find子例程的需要。
工厂是一个生成另一个函数的函数(在这里是子例程)。你给它一些参数,它就用这些参数创建一个自定义的子例程。我的例子使用了一个闭包,但是如果闭包由于某种原因成本很高,你也可以用字符串eval来构建它。

sub match_factory {
  my ($filespec, $array) = @_;

  # Validate arguments
  die qq{File spec must be a regex, not "$filespec"\n}
    unless ref $filespec eq "Regexp";
  die qq{Second argument must be an array reference, not "$array"\n} 
    unless ref $array eq "ARRAY";

  # Generate an anonymous sub to perform the match check
  # that creates a closure on $filespec and $array
  my $matcher = sub {
    # Automatically compare against $_
    m/$filespec/ and
    push(@$array, $File::Find::name);
  };

  return $matcher;
}

sub merge_xml {
  my @repos = @_ # or qw/foo bar .../;

  foreach my $repository (@repos) {
    my @matched_files;
    find(
      match_factory( qr/\/(main|test)\.xml$/, \@matched_files ),
      $repository
    );
    print Dumper(\@matched_files);
  }

}

高温

相关问题