Perl-哪些范围/封闭/环境正在产生这种行为? [英] Perl - What scopes/closures/environments are producing this behaviour?

查看:70
本文介绍了Perl-哪些范围/封闭/环境正在产生这种行为?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给出一个根目录,我希望确定任何.svn目录和pom.xml中最浅的父目录.

Given a root directory I wish to identify the most shallow parent directory of any .svn directory and pom.xml .

为此,我定义了以下功能

To achieve this I defined the following function

use File::Find;
sub firstDirWithFileUnder {
    $needle=@_[0];
    my $result = 0;
    sub wanted {
        print "\twanted->result is '$result'\n";
        my $dir = "${File::Find::dir}";

        if ($_ eq $needle and ((not $result) or length($dir) < length($result))) {
            $result=$dir;
            print "Setting result: '$result'\n";
        }
    }
    find(\&wanted, @_[1]);
    print "Result: '$result'\n";
    return $result;
}

..并这样称呼:

    $svnDir = firstDirWithFileUnder(".svn",$projPath);
    print "\tIdentified svn dir:\n\t'$svnDir'\n";
    $pomDir = firstDirWithFileUnder("pom.xml",$projPath);
    print "\tIdentified pom.xml dir:\n\t'$pomDir'\n";

有两种情况我无法解释:

There are two situations which arise that I cannot explain:

  1. 成功搜索.svn时,嵌套子例程wanted中感知到的$result的值将保留到firstDirWithFileUnder的下一个调用中.因此,当pom搜索开始时,尽管行my $result = 0;仍然存在,但wanted子例程将其值视为最后一次firstDirWithFileUnder调用的返回值.
  2. 如果注释了my $result = 0;行,则该功能仍将正确执行.这意味着a)外部范围(firstDirWithFileUnder)仍然可以看到$result变量以能够返回它,并且b)打印显示wanted从上次仍然看到$result值,即它似乎具有形成了一个闭合,该闭合一直持续到firstDirWithFileUnder的第一次调用之后.
  1. When the search for a .svn is successful, the value of $result perceived inside the nested subroutine wanted persists into the next call of firstDirWithFileUnder. So when the pom search begins, although the line my $result = 0; still exists, the wanted subroutine sees its value as the return value from the last firstDirWithFileUnder call.
  2. If the my $result = 0; line is commented out, then the function still executes properly. This means a) outer scope (firstDirWithFileUnder) can still see the $result variable to be able to return it, and b) print shows that wanted still sees $result value from last time, i.e. it seems to have formed a closure that's persisted beyond the first call of firstDirWithFileUnder.

有人可以解释正在发生的事情,并建议我在进入外部示波器时如何将$result的值正确重置为零吗?

Can somebody explain what's happening, and suggest how I can properly reset the value of $result to zero upon entering the outer scope?

推荐答案

使用warnings然后使用diagnostics会产生此有用的信息,包括解决方案:

Using warnings and then diagnostics yields this helpful information, including a solution:

变量"$ needle"将不会在-----第12行(#1)保持共享状态

Variable "$needle" will not stay shared at ----- line 12 (#1)

(W闭包)一个内部(嵌套的)命名子例程正在引用一个 在外部命名子例程中定义的词汇变量.

(W closure) An inner (nested) named subroutine is referencing a lexical variable defined in an outer named subroutine.

当调用内部子程序时,它将看到 在 first 之前和期间,外部子例程的变量 调用外部子例程;在这种情况下,第一次调用 外部子例程已完成,内部和外部子例程将不会 不再共享该变量的公共值.换句话说, 变量将不再共享.

When the inner subroutine is called, it will see the value of the outer subroutine's variable as it was before and during the first call to the outer subroutine; in this case, after the first call to the outer subroutine is complete, the inner and outer subroutines will no longer share a common value for the variable. In other words, the variable will no longer be shared.

通常可以通过创建内部子例程来解决此问题 匿名,使用sub {}语法.当内部匿名订阅者表示 在外部子例程中创建引用变量,它们 会自动反弹到此类变量的当前值.

This problem can usually be solved by making the inner subroutine anonymous, using the sub {} syntax. When inner anonymous subs that reference variables in outer subroutines are created, they are automatically rebound to the current values of such variables.


$result具有词法范围,这意味着每次调用&firstDirWithFileUnder都会分配一个新变量. sub wanted { ... } compile-time 子例程声明,这意味着它由Perl解释器一次编译并存储在包的符号表中.由于它包含对词法范围的$result变量的引用,因此Perl保存的子例程定义将仅引用$result的第一个实例.第二次调用&firstDirWithFileUnder并声明一个新的$result变量,它将与&wanted中的$result完全不同.


$result is lexically scoped, meaning a brand new variable is allocated every time you call &firstDirWithFileUnder. sub wanted { ... } is a compile-time subroutine declaration, meaning it is compiled by the Perl interpreter one time and stored in your package's symbol table. Since it contains a reference to the lexically scoped $result variable, the subroutine definition that Perl saves will only refer to the first instance of $result. The second time you call &firstDirWithFileUnder and declare a new $result variable, this will be a completely different variable than the $result inside &wanted.

您需要将sub wanted { ... }声明更改为一个词法范围的匿名子项:

You'll want to change your sub wanted { ... } declaration to a lexically scoped, anonymous sub:

my $wanted = sub {
    print "\twanted->result is '$result'\n";
    ...
};

并以调用File::Find::find

find($wanted, $_[1])

在这里,$wanted是子例程的运行时声明,并且在每次单独调用&firstDirWithFileUnder时都使用当前引用$result对其进行重新定义.

Here, $wanted is a run-time declaration for a subroutine, and it gets redefined with the current reference to $result in every separate call to &firstDirWithFileUnder.

更新:该代码段可能具有启发性:

Update: This code snippet may prove instructive:

sub foo {
    my $foo = 0;  # lexical variable
    $bar = 0;     # global variable
    sub compiletime {
        print "compile foo is ", ++$foo, " ", \$foo, "\n";
        print "compile bar is ", ++$bar, " ", \$bar, "\n";
    }
    my $runtime = sub {
        print "runtime foo is ", ++$foo, " ", \$foo, "\n";
        print "runtime bar is ", ++$bar, " ", \$bar, "\n";
    };
    &compiletime;
    &$runtime;
    print "----------------\n";
    push @baz, \$foo;  # explained below
}
&foo for 1..3;

典型输出:

compile foo is 1 SCALAR(0xac18c0)
compile bar is 1 SCALAR(0xac1938)
runtime foo is 2 SCALAR(0xac18c0)
runtime bar is 2 SCALAR(0xac1938)
----------------
compile foo is 3 SCALAR(0xac18c0)
compile bar is 1 SCALAR(0xac1938)
runtime foo is 1 SCALAR(0xa63d18)
runtime bar is 2 SCALAR(0xac1938)
----------------
compile foo is 4 SCALAR(0xac18c0)
compile bar is 1 SCALAR(0xac1938)
runtime foo is 1 SCALAR(0xac1db8)
runtime bar is 2 SCALAR(0xac1938)
----------------

请注意,编译时间$foo始终引用相同的变量SCALAR(0xac18c0),并且这也是运行时间$foo函数第一次运行.

Note that the compile time $foo always refers to the same variable SCALAR(0xac18c0), and that this is also the run time $foo THE FIRST TIME the function is run.

此示例中包括了&foo的最后一行,push @baz,\$foo,因此,$foo不会在&foo的末尾收集垃圾.否则,第二个和第三个运行时$foo可能指向相同的地址,即使它们引用了不同的变量(每次声明该变量时也会重新分配内存).

The last line of &foo, push @baz,\$foo is included in this example so that $foo doesn't get garbage collected at the end of &foo. Otherwise, the 2nd and 3rd runtime $foo might point to the same address, even though they refer to different variables (the memory is reallocated each time the variable is declared).

这篇关于Perl-哪些范围/封闭/环境正在产生这种行为?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆