当前位置:  开发笔记 > 编程语言 > 正文

修改$ _的Perl函数

如何解决《修改$_的Perl函数》经验,为你挑选了2个好方法。

我正在尝试$_在我的代码中扩展隐式(全局"主题"变量)的使用.Perlmonks有关于函数的这篇(过时的?)文章,它$_在没有显式变量的情况下接受.

我遇到的问题是我不知道设置了哪些功能$_.我知道至少map,grepfor/ foreach将改变它的价值$_,但我认为必须有更多.我也不清楚与之相关的任何范围问题$_,如:

for (@array_of_array_refs)
{
  for (@$_)
  {
    print;
  }
  print;  # what does this print?
}

是否有一系列功能或一套指导原则,因此我将直观地了解如何避免遭受破坏$_



1> brian d foy..:

Steffen Ullrich的回答是误导性的,所以我必须回答这里.我可能错过了一些事情,但现在已经很晚了.而且,Learning Perl已经解释了这一切.;)

在本地经营者不按词汇的范围内工作.尽管如此,它并不局限于它所处的阻挡.人们通常在理解中遇到这个问题,因为他们没有尝试."外部"和"内部"等词语对本地人来说具有误导性和危险性.

考虑这种用法,其中有一个函数可以打印全局值$_:

$_ = 'Outside';
show_it();
inside();
$_ = 'Outside';
show_it();

sub show_it { print "\$_ is $_\n"; }

sub inside {
    local $_;

    $_ = 'Inside';
    show_it();
    }

运行它时,您会看到$_块内的set 值在块外可用:

$_ is Outside
$_ is Inside
$_ is Outside

在局部上包变量的作品.它暂时使用一个新值,直到块结束.但是,作为一个包变量,它在程序的每个地方都会发生变化,直到本地的范围结束.运营商具有词汇范围,但其效果无处不在.您正在为全局变量提供临时值,并且该全局变量仍然是全局变量.局部变量具有全局效应但是词汇生命周期.它们会更改程序中任何位置的值,直到该范围结束.

正如我之前所写,与本地人谈论"内部"和"外部"是错误的.它是"之前"和"之后".我会展示更多即将到来的东西,甚至时间也会瓦解.

在我的是完全不同的.它根本不适用于包变量.也被称为"词汇变量",这些变量在它们的范围之外根本不存在(即使像PadWalker这样的反魔术模块看它们).程序的任何其他部分都无法看到它们.它们仅在其范围内创建并在该范围内创建的子范围中可见.

Perl v5.10允许我们创建一个词汇版本$_(并修复并在v5.16中进行实验 -不要使用它.另请参阅Perl 5.10+中词汇$ _的好,坏和丑陋).我可以让我之前的例子使用:

use v5.10;

$_ = 'Outside';
show_it();
inside();
$_ = 'Outside';
show_it();

sub show_it { print "\$_ is $_\n"; }


sub inside {
    my $_;
    $_ = 'Inside';
    show_it();
    }

现在输出是不同的.词汇$_与任何其他词汇变量具有相同的效果.它不会影响其范围之外的任何内容,因为这些变量只存在于它们的词法范围内:

$_ is Outside
$_ is Outside
$_ is Outside

但是,回答原来的问题.Perlmonks post Builtin函数默认为$ _仍然很好,但我不认为它在这里是相关的.那些功能使用$_,而不是设置它.

关于Perl的重要事情是没有简短的答案.Perl做了有意义的事情,而不是使它保持一致的事情.毕竟,它是一种后现代语言.

不担心改变的$_方式不是改变$_.避免使用它.我们在Effective Perl Programming中有很多类似的建议.

的foreach

循环构造foreach及其for同义词使用本地化版本$_来引用当前主题.在循环内部,包括循环调用的任何内容,使用当前主题:

use v5.10;

$_ = 'Outside';
show_it();
sub show_it { say "\$_ is $_"; }

my @array = 'a' .. 'c';
foreach ( @array ) {
    show_it();
    $_++
    }

say "array: @array";

注意foreach循环后的数组.即使foreach本地化$_,Perl 也会对值进行别名而不是复制它.即使该值位于外部词法范围内,更改控制变量也会更改原始值:

$_ is Outside
$_ is a
$_ is b
$_ is c
array: b c d

不要$_用作控制变量.我只在真正的短程序中使用默认值,主要是因为我希望控件变量在大程序中具有有意义的名称.

地图和grep

foreach,mapgrep使用$_控制变量.您不能为这些使用不同的变量.您仍然可以通过我在上一节中展示的性能增强别名来影响范围之外的变量.

同样,这意味着存在一些范围泄漏.如果更改$_块内部并且$_是输入列表中的项目之一,则外部$_更改:

use v5.10;
$_ = 'Outside';
my @transformed = map { $_ = 'From map' } ( $_ );
say $_;

对于中等复杂的内联块,我分配$_一个词法变量:

my @output = map { my $s = $_; ... } @input;

如果你真的很紧张$_,不要做map内心的邪恶伎俩map:

my @words = map {
    map { split } $_
    } <>;

这是一个愚蠢的例子,但我过去做过这样的事情,我需要将主题变成一个列表.

而(<>)

Perl有一个方便的小成语,从文件句柄分配下一行$_.这意味着代替:

while( defined( $_ = <> ) )

你可以得到完全相同的东西:

while( <> )

但是,无论什么价值最终$_留在$_.

$_ = "Outside\n";
show_it();
sub show_it { print "\$_ is $_" }

while(  ) {
    show_it();
    }

show_it();

__DATA__
first line
second line
third line

输出看起来有点奇怪,因为最后一行没有值,但这是分配给的最后一个值$_:在defined测试停止循环之前行输入操作符分配的undef :

$_ is Outside
$_ is first line
$_ is second line
$_ is third line
$_ is

放在last那里,输出会改变

$_ = "Outside\n";
show_it();
sub show_it { print "\$_ is $_" }

while(  ) {
    show_it();
    last;
    }

show_it();

__DATA__
first line
second line
third line

现在分配的最后一个值是第一行:

$_ is Outside
$_ is first line
$_ is first line

如果你不喜欢这个,不要使用这个成语:

while( defined( my $line = <> ) )

模式匹配

替换运算符默认s///绑定$_并可以更改它(这就是重点).但是,随着v5.14,您可以使用该/r标志,这让原本独自返回修改后的版本.

匹配运算符m//也可以更改$_.它不会更改值,但可以设置位置标志.这就是Perl如何在标量上下文中进行全局匹配:

use v5.10;

$_ = 'Outside';
show_it();
sub show_it { say "\$_ is $_ with pos ", pos(); }

foreach my $time ( 1 .. 5 ) {
    my $scalar = m/./g;
    show_it();
    }

show_it();

$_即使值相同,某些标量设置也会发生变化:

$_ is Outside with pos
$_ is Outside with pos 1
$_ is Outside with pos 2
$_ is Outside with pos 3
$_ is Outside with pos 4
$_ is Outside with pos 5
$_ is Outside with pos 5

你可能不会有这个问题.您可以通过不成功的匹配重置位置$_.也就是说,除非你使用/c旗帜.即使标量值没有改变,其部分记账也发生了变化.这是词汇$_的问题之一.

匹配时会发生另一件奇怪的事情.每个匹配变量是动态范围的.它们不会更改外部范围中的值:

use v5.10;

my $string = 'The quick brown fox';

OUTER: {
    $string =~ /\A(\w+)/;
    say  "\$1 is $1";

    INNER: {
        $string =~ /(\w{5})/;
        say  "\$1 is $1";
        }

    say  "\$1 is $1";
    }

的值$1OUTER范围并不替换为$1INNER:

$1 is The
$1 is quick
$1 is The

如果这会伤害你的头脑,请不要使用每个匹配变量.马上分配(只有当你成功匹配时):

my $string = 'The quick brown fox';

OUTER: {
    my( @captures ) = $string =~ /\A(\w)/;

    INNER: {
        my $second_word;
        if( $string =~ /(\w{5})/ ) {
            $second_word = $1
            }
        }
    }



2> Steffen Ullr..:

请参阅Brian的答案以获得更详细的解释.但是我留下这个答案是因为问题上下文中的一些问题可能很难理解,除了Brian的答案以更好地理解问题之外,这个答案和评论中的不同描述可能会有所帮助.

阅读维基百科页面的"范围"以了解各种范围,尤其是词汇和动态范围,也可能是有用的.


map,grep,for/foreach等"localize" $_.这意味着它们将新变量$_绑定到,并且$_只有在离开词法范围时才会绑定原始变量.有关此"本地化"的更详细说明,请参阅答案的最后部分.例如:

for(qw(1 2)) {
    for(qw(a b)) {
        print map { uc($_) } ($_,'x');
        print $_
    }
    print $_
}

将给出AXaBXb1AXaBXb2哪些显示每个使用for/map绑定$_到另一个变量并在离开块后将其绑定回前一个变量.

并且对于$_作为默认参数的函数:除了预期(即替换s///)之外,它们没有任何副作用,并且当函数或操作将$_用作默认参数时,它在perldoc中记录.

但是,你必须注意$_自己是否使用自己,并希望确保它不会影响以前的含义.在这种情况下,本地化$_自己有助于防止意外更改上一个$_:

sub myfunction {
    local $_;
    # from now on until the functions gets left changes to $_ 
    # will not affect the previous $_
    ...
}

这也可以用块

{
    local $_;
    # from now on until the block gets left changes to $_
    # will not affect the previous $_
    ...
}

但请注意,经常使用的while (<>)不会本地化$_:

$_ = 'foo';
say $_;
while (<>) {
    say $_;
}
say $_;

在这种情况下,say $_循环之后不会显示循环('foo')之前的值,而是循环()中的最后一个隐式赋值undef.


究竟是什么本地化?大多数用于词法作用域,可以用Perl中的"my"来完成.但是"本地化"变量是不同的,无论是否使用显式local或隐式内部,map ...

主要思想是通过本地化一个全局符号,比如$_绑定到另一个变量,原始变量只在词法范围结束后恢复.因此,与词法范围相反,这种新的约束甚至影响从这个词法范围内调用的函数,即

sub foo { say $_}

$_ = 1;
foo(); # 1

{
    local $_;  # bind symbol $_ to new variable
    $_ = 2;
    foo();     # 2 - because $_ inside foo() is the same as in this block
}

foo(); # 1     # leaving the block restored original binding of $_


而()和s ///也是.此外,不建议在grep和map中更改$ _,因为它是别名的列表成员,就像foreach一样.
推荐阅读
手机用户2402851155
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有