简体   繁体   English

Perl出人意料的结果

[英]Perl unexpected result

Imagine I have this Perl script 想象一下,我有这个Perl脚本

my $name = "   foo    ";
my $sn   = "     foosu";

trim($name, \$sn);

print "name: [$name]\n";
print "sn: [$sn]\n";
exit 0;


sub trim{

   my $fref_trim = sub{
                          my ($ref_input) = @_;
                          ${$ref_input} =~ s/^\s+// ;
                          ${$ref_input} =~ s/\s+$// ;
    };

   foreach my $input (@_){
       if (ref($input) eq "SCALAR"){
            $fref_trim->($input);
       } else {
            $fref_trim->(\$input);
       }
   }
}

Result: 结果:

name: [foo]
sn: [foosu]

I would expect $name to be " [ foo ] " when printing the value after calling trim , but the sub is setting $name as I would want it. 在调用trim之后打印值时,我希望$ name为“ [ foo ] ”,但是sub正在设置$name因为我想要它。 Why is this working, when it really shouldn't? 为什么这个工作,当它真的不应该?

I'm not passing $name by reference and the trim sub is not returning anything. 我没有通过引用传递$ name而trim子没有返回任何东西。 I'd expect the trim sub to create a copy of the $name value, process the copy, but then the original $name would still have the leading and trailing white spaces when printed in the main code. 我希望trim子创建$name值的副本,处理副本,但是当在主代码中打印时,原始$name仍然会有前导和尾随空格。

I assume it is because of the alias with @_ , but shouldn't the foreach my $input (@_) force the sub to copy the value and only treat the value not the alias? 我假设它是因为@_的别名,但不应该foreach my $input (@_)强制sub复制值并且只处理值而不是别名?

I know I can simplify this sub and I used it only as an example. 我知道我可以简化这个子,我只用它作为例子。

Elements of @_ are aliases to the original variables. @_元素是原始变量的别名。 What you are observing is the difference between: 您所观察到的是以下区别:

sub ltrim {
    $_[0] =~ s/^\s+//;
    return $_[0];
}

and

sub ltrim {
    my ($s) = @_;
    $s =~ s/^\s+//;
    return $s;
}

Compare your code to: 将您的代码与:

#!/usr/bin/env perl

my $name = "   foo    ";
my $sn   = "     foosu";

trim($name, \$sn);

print "name: [$name]\n";
print "sn: [$sn]\n";

sub trim {
    my @args = @_;

    my $fref_trim = sub{
        my ($ref_input) = @_;
        ${$ref_input} =~ s/^\s+//;
        ${$ref_input} =~ s/\s+\z//;
    };

   for my $input (@args) {
       if (ref($input) eq "SCALAR") {
           $fref_trim->($input);
       }
       else {
           $fref_trim->(\$input);
       }
   }
}

Output: 输出:

$ ./zz.pl
name: [   foo    ]
sn: [foosu]

Note also that the loop variable in for my $input ( @array ) does not create a new copy for each element of the array. 另请注意for my $input ( @array )中的循环变量不会为数组的每个元素创建新副本。 See perldoc perlsyn : perldoc perlsyn

The foreach loop iterates over a normal list value and sets the scalar variable VAR to be each element of the list in turn. foreach循环遍历正常列表值,并将标量变量VAR依次设置为列表的每个元素。 ... ...

... ...

the foreach loop index variable is an implicit alias for each item in the list that you're looping over. foreach循环索引变量是您循环的列表中每个项的隐式别名。

In your case, this would mean that, at each iteration $input is an alias to the corresponding element of @_ which itself is an alias to the variable that was passed in as an argument to the subroutine. 在你的情况下,这意味着,在每次迭代时, $input@_的相应元素的别名,它本身是作为子例程的参数传入的变量的别名。

Making a copy of @_ thus prevents the variables in the calling context from being modified. 因此,复制@_可以防止修改调用上下文中的变量。 Of course, you could do something like: 当然,你可以这样做:

sub trim {
    my $fref_trim = sub{
        my ($ref_input) = @_;
        ${$ref_input} =~ s/^\s+//;
        ${$ref_input} =~ s/\s+\z//;
    };

   for my $input (@_) {
       my $input_copy = $input;
       if (ref($input_copy) eq "SCALAR") {
           $fref_trim->($input_copy);
       }
       else {
           $fref_trim->(\$input_copy);
       }
   }
}

but I find making a wholesale copy of @_ once to be clearer and more efficient assuming you do not want to be selective. 但我发现,如果你不想选择性的话,那么制作一份@_的批发副本就会更清晰,更有效率。

I assume it is because of the alias with @_ , but shouldn't the foreach my $input (@_) force the sub to copy the value and only treat the value not the alias? 我假设它是因为@_的别名,但不应该foreach my $input (@_)强制sub复制值并且只处理值而不是别名?

You're right that @_ contains aliases. 你是对的,@ @_包含别名。 The part that's missing is that foreach also aliases the loop variable to the current list element. 缺少的部分是foreach还将循环变量别名化为当前列表元素。 Quoting perldoc perlsyn : 引用perldoc perlsyn

If any element of LIST is an lvalue, you can modify it by modifying VAR inside the loop. 如果LIST的任何元素是左值,您可以通过修改循环内的VAR来修改它。 Conversely, if any element of LIST is NOT an lvalue, any attempt to modify that element will fail. 相反,如果LIST的任何元素不是左值,则任何修改该元素的尝试都将失败。 In other words, the foreach loop index variable is an implicit alias for each item in the list that you're looping over. 换句话说, foreach循环索引变量是您循环的列表中每个项的隐式别名。

So ultimately $input is an alias for $_[0] , which is an alias for $name , which is why you see the changes appearing in $name . 所以最终$input$_[0]的别名,这是$name的别名,这就是为什么你看到$name出现的更改。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM