为什么 Perl 条件运算符对于未定义的 hashref 键的行为有所不同?

tur*_*tle 1 perl

为什么三元条件运算符对于未定义的 hashref 键的行为与常规未定义的 $ 变量的行为不同?例如,如果 $form 未定义并且 $str 已定义,则以下三元组始终将 str 的值分配给 form 的值: ($str) ? $form = $str : $form = $form; 然而,如果未定义的哈希键被替换,它就不再起作用。

我制作了一个脚本来演示奇怪的行为。请注意,您将在所有测试中收到“使用未初始化值”警告。这是故意的,因为我正在测试未定义的值。提前致谢

    #!/usr/bin/env perl
    # ternary operator
    # condition ? if True : if False 
    # Why does the ternary operator behave different with a undefined hashref key
    # than a regular undefined $ variable ?
    
use strict;
use warnings;
use diagnostics;

my $i = 1;
my $form = { vc => 'customer', customer_id => ''};
my $ref = { customer_id => 12345 };

&string_test;# passes
print "-" x 80;
&mixed_test_ref; # passes
print "-" x 80;
&mixed_test; #fails
print "-" x 80;
&hashref_test; #fails
print "-" x 80;
&works_always; 
print "-" x 80;
print "\n";

sub string_test {
    my $str = '1234';
    my $form; # not defined
    print qq|
Why does the ternary operator behave different
with a undefined hashref key
than a regular undefined \$ variable ?
First with a undefined variable called form:   
   Before:
        str : '$str' \n
        form : $form
    |; # will throw undefined warning


    # ternary operator
    #condition ? if True : if False  
    ($str) ? $form = $str : 
    $form = $form;

    print qq|
       Always works:
        str : '$str' \n
        form: $form \n|;

}


sub mixed_test_ref {

    my $local;
    print qq|
    Mixed test ref
    Before:
        form->{vc} '$form->{vc}' \n
        ref->{"$form->{vc}_id"} : '$ref->{"$form->{vc}_id"}' \n
    |;


    # ternary operator
    #condition ? if True : if False  
    ($ref->{"$form->{vc}_id"}) ? $local = $ref->{"$form->{vc}_id"} : 
    $local = $local;

    print qq|
        After:
        form->{vc} '$form->{vc}' \n
        ref->{"$form->{vc}_id"} : '$ref->{"$form->{vc}_id"}' \n
         local: '$local' \n
    |;

    if ($local = $ref->{"$form->{vc}_id"}) {print "Pass\n"}
    else {print "Fail\n"}
}

sub mixed_test {
    # $form->{"$form->{vc}_id_$i"} is not defined
    # setting it to empty string makes it pass
    #$form->{"$form->{vc}_id_$i"} = '';
    delete $form->{"$form->{vc}_id_$i"};
    my $str = '1234';
    
    print qq|
    mixed test
    Before:
        str : '$str' \n
        form: '$form->{"$form->{vc}_id_$i"}' \n
    |;


    # ternary operator
    #condition ? if True : if False  
    ($str) ? $form->{"$form->{vc}_id_$i"} = $str : 
    $form->{"$form->{vc}_id_$i"} = $form->{"$form->{vc}_id_$i"};



    if ( $form->{"$form->{vc}_id_$i"} == $str ) {
    print qq|Pass\
        After:
        str : '$str' \n
        form: $form->{"$form->{vc}_id_$i"} \n|;
    }
    else {print "Fail\n"}

}

sub hashref_test {
    # $form->{"$form->{vc}_id_$i"} is not defined
    # setting it to empty string makes it pass
    #$form->{"$form->{vc}_id_$i"} = '';
    delete $form->{"$form->{vc}_id_$i"};
    print qq|
    hash ref test
        Before:
        form->{vc} '$form->{vc}' \n
        ref->{"$form->{vc}_id"} : '$ref->{"$form->{vc}_id"}' \n
        form->{"$form->{vc}_id_$i"} : $form->{"$form->{vc}_id_$i"} \n
    |;


    # ternary operator
    #condition               ? if True : if False  
    $ref->{"$form->{vc}_id"} ? $form->{"$form->{vc}_id_$i"} = $ref->{"$form->{vc}_id"} : 
    $form->{"$form->{vc}_id_$i"} = $form->{"$form->{vc}_id_$i"};

    if ($form->{"$form->{vc}_id_$i"}) {print "Passes and I am amazed\n"}
    else {
        print qq|
            Why does this not work?
            After:
            form->{vc} '$form->{vc}' \n
            ref->{"$form->{vc}_id"} : '$ref->{"$form->{vc}_id"}' \n
            form->{"$form->{vc}_id_$i"} : $form->{"$form->{vc}_id_$i"} \n|;
    }

}

sub works_always {
    if ($ref->{"$form->{vc}_id"}) { $form->{"$form->{vc}_id_$i"} = $ref->{"$form->{vc}_id"} }
    # not necessary but for completeness
    else { $form->{"$form->{vc}_id_$i"} = $form->{"$form->{vc}_id_$i" } }

    print qq|
        Always works as expected with if statement:
        form->{vc} '$form->{vc}' \n
        ref->{"$form->{vc}_id"} : '$ref->{"$form->{vc}_id"}' \n
        form->{"$form->{vc}_id_$i"} : $form->{"$form->{vc}_id_$i"} \n|;
}
Run Code Online (Sandbox Code Playgroud)

mob*_*mob 10

当我看到问题的标题时,我知道答案将是关于优先级的。

你可能会认为

($str) ? $form = $str : $form = $form;
Run Code Online (Sandbox Code Playgroud)

方法

if ($str) {
    $form = $str;
} else {
    $form = $form;
}
Run Code Online (Sandbox Code Playgroud)

但是这个命令

$ perl -MO=Deparse,-p -e '($str) ? $form = $str : $form = $form'
(($str ? ($form = $str) : $form) = $form);
Run Code Online (Sandbox Code Playgroud)

向我们表明这确实意味着

if ($str) {
    $form = $str = $form;
} else {
    $form = $form;
}
Run Code Online (Sandbox Code Playgroud)

这可以通过多种方式解决。加上更多括号:

($str) ? ($form = $str) : ($form = $form)
Run Code Online (Sandbox Code Playgroud)

或者写一个更简单的表达式:

$form = $str if $str;
$form = $str || $form;
Run Code Online (Sandbox Code Playgroud)

  • 为了清楚起见,请使用“$form = $str if $str;”或“$form = $str ||” $form;`,不是两者。 (4认同)