Why does !1 give me nothing in Perl?

前端 未结 5 1634
青春惊慌失措
青春惊慌失措 2020-12-03 14:29

This is strange. The following:

$sum = !0;
print $sum;

prints out 1 as you would expect. But this

$sum = !1;
print $sum;
         


        
5条回答
  •  不思量自难忘°
    2020-12-03 14:56

    Here's an addendum to the other great answers you've already gotten.

    Not's Not Not

    Consider the following code that tests each of Perl's 'not' operators:

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    for( '!1', 'not 1', '~0' ) {
        my $value = eval;
        my $zero_plus = 0 + $value;
    
        print join "\n", 
            "\nExpression: $_",
            "Value:     '$value'",
            "Defined:   " . defined $value,
            "Length:    " . length($value),
            "Plus:      " . +$value,
            "Plus Zero: '$zero_plus'",
            '';
    }
    
    print "\nTest addition for a literal null string:  ";
    print 0+'', "\n";
    
    use Scalar::Util qw(dualvar);
    
    {   # Test a dualvar 
        my $value = dualvar 0, '';
        my $zero_plus = 0+$value;
    
        print join "\n", 
            "\nExpression: dualvar",
            "Value:     '$value'",
            "Defined:   " . defined $value,
            "Length:    " . length($value),
            "Plus:      " . +$value,
            "Plus Zero: '$zero_plus'",
            '';
    
    }
    

    Executing it results in the following. Notice the warning message:

    Argument "" isn't numeric in addition (+) at test.pl line 21.
    
    Expression: !1
    Value:     ''
    Defined:   1
    Length:    0
    Plus:
    Plus Zero: '0'
    
    Expression: not 1
    Value:     ''
    Defined:   1
    Length:    0
    Plus:
    Plus Zero: '0'
    
    Expression: ~0
    Value:     '4294967295'
    Defined:   1
    Length:    10
    Plus:      4294967295
    Plus Zero: '4294967295'
    
    Test addition for a literal null string:  0
    
    Expression: dualvar
    Value:     ''
    Defined:   1
    Length:    0
    Plus:
    Plus Zero: '0'
    

    From this we learn several things.

    The first two items are not all that exciting:

    • !1 and not 1 behave in basically the same way.
    • Unsurpisingly, ~1 is different (it's the bitwise not).

    Now, the interesting item:

    • While we do get a warning for line 21 (0+''), there is no warning generated when we add 0+!1.

    It Takes Two to Tangle

    Something fishy is happening, and that fishiness has to do with special scalar contexts in Perl. In this case, the distinction between numeric and string contexts. And the ability to create a variable that has different values in each context, aka a dual variable.

    It looks like !1 returns a dual variable that returns 0 in numeric context and the null string in string context.

    The dualvar test at the end shows that a homemade dualvar works the same way as !1.

    But It's A Good Thing

    Like many Perl features, dual variables seem at first to defy expectations, and can be confusing. However, like those other features, used appropriately they make life much easier.

    As far as I know, a dualvar of 0 and '' is the only defined value that will return false in all scalar contexts. So it is a very sensible return value for !1. One could argue that undef is a good false result, but then an uninitialized variable is not distinguishable from a false value. Also, attempts to print or add the results of booleans would then be plagued with unnecessary warnings.

    Another famous dualvar is $! or $OS_ERROR if you use English. In numeric form, you get the error code, in string form, the error code is translated for you.

    It's Not Nothing, It's Empty, But It's Nought

    So in summary, you aren't getting nothing, you aren't getting an empty string, and you aren't getting zero.

    You are getting a variable that is both an empty string and 0 at the same time.

提交回复
热议问题