【问题标题】:Nested subroutines and Scoping in PerlPerl 中的嵌套子例程和作用域
【发布时间】:2012-04-17 13:29:50
【问题描述】:

我写 Perl 已经有一段时间了,一直在发现新事物,我只是遇到了一些有趣的东西,我没有解释,也没有在网上找到它。

sub a {
   sub b {
     print "In B\n";
   }
}
b();

为什么我可以从其范围之外调用b() 并且它可以工作?

我知道这样做是一种不好的做法,但我不这样做,我在这些情况下使用封闭等,但只是看到了。

【问题讨论】:

  • 您只能使用 OOP 强制执行这种类型的作用域
  • 闭包之类的东西会帮助你;)它与你所问的不一样,而是另一个很好的子例程实现。

标签: perl scope nested subroutine


【解决方案1】:

子例程在编译时存储在全局命名空间中。在您的示例中,b();main::b(); 的简写。要将函数的可见性限制在某个范围内,您需要将匿名子例程分配给变量。

命名子例程和匿名子例程都可以形成闭包,但由于命名子例程在嵌套时只会编译一次,因此它们的行为并不像许多人期望的那样。

use warnings;
sub one {
    my $var = shift;
    sub two {
        print "var: $var\n";
    }
}
one("test");
two();
one("fail");
two();
__END__
output:
Variable "$var" will not stay shared at -e line 5.
var: test
var: test

在 Perl 中允许嵌套命名子例程,但这几乎可以肯定是代码执行错误的标志。

【讨论】:

  • 这是一个有趣的例子。你如何解释它没有第二次更新$var?奇怪的是,如果删除one("test") 行,它会抱怨未定义的变量,但随后one("fail") 允许更新$var。未定义的行为?
  • 我相信我在perlref找到了答案。
  • @TLP, sub two {}BEGIN { *two = sub {} },因此它会捕获编译时存在的 $varmy 基本上是在范围退出时发生的new,因此two$var$one 第一次运行后与one$var 不同。
  • 那么这个答案中示例代码的这种行为是否被认为是一种特性,或者只是 perl 符号表性质的副产品?因为如果我看到写在某个地方的代码,我永远不会猜到它会那样做。只是好奇。
【解决方案2】:

在 perl 中创建嵌套子例程的“官方”方法是使用 local 关键字。例如:

sub a {
    local *b = sub {
        return 123;
    };
    return b();  # Works as expected
}

b();  # Error: "Undefined subroutine &main::b called at ..."

perldoc 页面perlref 有这个例子:

sub outer {
    my $x = $_[0] + 35;
    local *inner = sub { return $x * 19 };
    return $x + inner();
}

“这具有为另一个函数创建本地函数的有趣效果,而 Perl 通常不支持这一点。”

【讨论】:

    【解决方案3】:

    以下打印123

    sub a {
        $b = 123;
    }
    
    a();
    print $b, "\n";
    

    那么你为什么对以下内容感到惊讶呢?

    sub a {
        sub b { return 123; }
    }
    
    a();
    print b(), "\n";
    

    没有任何要求 $b&b 成为词法。事实上,你不能要求&b 是词法(目前)。

    sub b { ... }
    

    基本上是

    BEGIN { *b = sub { ... }; }
    

    其中*b$b@b、...当然还有&b 的符号表条目。这意味着 subs 属于包,因此可以从包中的任何位置调用,如果使用了它们的完全限定名称 (MyPackage::b()),则可以从任何位置调用。

    【讨论】:

      【解决方案4】:

      子程序是在编译时定义的,不受作用域的影响。换句话说,它们不能真正嵌套。至少就他们自己的范围而言不是。在被定义后,它们会被有效地从源代码中删除。

      【讨论】:

      • 嗯,它们可以嵌套,只是没有词法范围。
      • @briandfoy 它似乎比我知道的要多。在perlref 中找到了一个非常神秘的解释。这种行为是故意的吗?它有什么可能的用途?
      • @briandfoy - “我假设'可以嵌套'你的意思是'可以物理放置在另一个子的代码中';而不是'可以有语义受到这种嵌套位置的影响'”,对吗?
      • @DVK 在 Ven'Tatsu 的回答中,这两个 sub 似乎是嵌套的,至少就变量而言。我用strict试了一下,代码有效。
      • @DVK 是的,“嵌套”在源代码中的“{}”块内。
      猜你喜欢
      • 2011-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-05
      • 2012-08-09
      • 2015-09-24
      相关资源
      最近更新 更多