【问题标题】:Non-defined return of sub子的未定义返回
【发布时间】:2013-08-30 04:51:40
【问题描述】:

如何避免变量被子函数的返回所触及。我写了以下代码sn-p

#!/usr/bin/perl
use strict;
use warnings;

my $a = "init";

sub funct{
    my $var;
    #$var = 1;
    return $var if defined $var;
}

my $tmp = funct;

$a = $tmp if defined $tmp;
print "$a\n";

如果$var 未在子函数中定义,我不希望$a 的值从它的初始init 更改。

错误在哪里或者有更好的方法来解决这个问题?

问候

【问题讨论】:

    标签: perl function return return-value


    【解决方案1】:

    return $foo if $bar 等同于$bar and return $foo。因此,当$bar 为假时,该语句的计算结果为$bar 的值。由于子例程返回最后执行的语句的值,您的子程序返回 $var(如果已定义)或 false 值(如果未定义)。

    您可以走显式路线,然后在此处放置另一个返回:

    # all branches covered
    return $var if defined $var;
    return;
    

    这很傻,相当于return $var

    现在您的子程序返回的 false 值实际上已定义,因此将其分配给 $a。您可以改为测试真相:

    $a = $tmp if $tmp;
    

    ……但这又打开了一罐蠕虫。


    返回值在告诉我们它们是想要的返回值还是错误指示方面非常糟糕。有两种干净的方法:

    1. 返回第二个值,告诉我们函数是否正常退出:

      sub func {
        my $var;
        return (1, $var) if defined $var;
        return (0);  # not strictly needed
      }
      
      my ($ok, $tmp) = func();
      $a = $tmp if $ok;
      

      (基本上是在 Golang 中看到的 comma-ok 成语)

    2. 返回一个必须被解构以获得实际返回值的容器。一种可能性是在错误时返回 undef(或某些错误),或者在存在此类值时返回对该值的标量引用:

      sub func {
        my $var;
        return \$var if defined $var;
        return undef;  # actually not needed
      }
      
      my $tmp = func();
      $a = $$tmp if defined $tmp;
      

      (基本上是在 Haskell 中看到的 Maybe 类型)

      这是一种在没有明显临时变量的情况下使用它的方法:

      ($a) = map { $_ ? $$_ : () } func(), \$a;
      

    【讨论】:

    • 谢谢!阅读如此详细的答案总是很棒。由于我是一个新手,由于我获得的洞察力,这值得接受的答案。我喜欢第一个定义,因为它直观易懂。
    • 我比($a) = map { $_ ? $$_ : () } func(), \$a;($a) = map { defined $_ ? $_ : () } func(), $a;更喜欢OP
    • @mpapec map 表达式不一定要使用。相反,它展示了如何推广这一点。 (实际上,它有点 Haskell 势利,强调了 Maybe monad 和 Perl 的 scalarrefs 之间的相似之处。对于一个 monad,我们需要操作 return: t→t?,它由引用运算符 \ bind: t?→(t→u?)→u? 实现,它可以表示为sub bind {my ($x, $f) = @_; $x ? $f->($$x) : undef}。由于Maybe是加法,我们也有mzeroundef)和mplus: t?→t?→t?,即sub mplus {(grep $_, @_)[0]}。...)
    • ( ...我们还可以定义一个fmap: (t→u)→t?→u?,它将函数提升到monad级别:sub fmap {my ($x, $f) = @_; $x ? \($f->($$x)) : undef},它与bind非常相似。在其中放入一些子原型,我们可以写fmap {$a = $_[0]} mplus func(), \$a。当我们替换定义(并进行一些可证明的优化)时,我们得到\($a = ${ (grep $_, func(), \$a)[0] }),它也可以在我的答案中表示为map表达式。(顺便说一句,如果我们将Perl列表视为monad,然后map 是该类型的bind)。)
    • tnx 进行解释;虽然 perl 的函数式风格可能看起来很诱人,但它也可能会引起一些注意
    【解决方案2】:

    你可以避免临时变量,

    $a = funct() // $a;
    

    【讨论】:

    • 你能解释一下吗?我用你的换了倒数第二行,但它不起作用。
    • 还将return $var if defined $var; 更改为return $var;
    【解决方案3】:

    在 Perl 调试器中运行该程序显示 $tmp 设置为空字符串,使用 defined 函数计算为 true。这就是设置$a 的条件评估为真的原因:

    $ perl -d
    
    Loading DB routines from perl5db.pl version 1.33
    Editor support available.
    
    Enter h or `h h' for help, or `perldoc perldebug' for more help.
    
    use strict;
    use warnings;
    
    my $a = "init";
    
    sub funct{
        my $var;
        #$var = 1;
        return $var if defined $var;
    }
    
    my $tmp = funct;
    
    $a = $tmp if defined $tmp;
    print "$a\n";
    __END__
    main::(-:4):    my $a = "init";
    
      DB<1> x $a
    0  undef
    
      DB<2> n
    main::(-:12):   my $tmp = funct;
    
      DB<2> x $a
    0  'init'
    
      DB<3> x $tmp
    0  undef
    
      DB<4> n
    main::(-:14):   $a = $tmp if defined $tmp;
    
      DB<4> x $tmp
    0  ''
    

    要修复,只需 return $var,不带 if defined $var。这会将$tmp 设置为undef

    【讨论】:

    • 谢谢@Zaid,从未使用过调试器。以后会努力做的。
    【解决方案4】:

    您似乎正试图通过返回 undef 来检测错误情况。除了@amon 的建议,出现错误的时候可以die out of funct

    #!/usr/bin/perl
    use strict;
    use warnings;
    
    my $a = "init";
    
    sub funct{
        my $var;
        ...
        if ($something_went_wrong) {
           die 'something bad';
        }
        ...
        return $var;
    }
    
    eval {
        $a = funct;
        1;
    } or do {
        # log/handle the error in $@
    };
    
    print "$a\n";
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-11-17
      • 1970-01-01
      • 2017-08-21
      • 2016-11-18
      • 2017-10-24
      • 2021-09-22
      • 2015-12-10
      • 2019-12-24
      相关资源
      最近更新 更多