Почему! 1 ничего не дает мне в Perl? - PullRequest
18 голосов
/ 16 июля 2009

Это странно. Следующее:

$sum = !0;
print $sum;

выводит 1, как и следовало ожидать. Но это

$sum = !1;
print $sum;

ничего не печатает. Почему?

Ответы [ 5 ]

41 голосов
/ 16 июля 2009

Будьте осторожны: то, что вы написали, не делает того, о чем вы думаете. Помните, что Perl не имеет реального логического типа данных. У него есть скаляры, хэши, списки и ссылки. То, как он обрабатывает истинные / ложные значения, является контекстным. Все оценивается как "true" в perl, за исключением неопределенных переменных, пустого списка, пустой строки и числа 0.

То, что делает ваш код, принимает обратное значение, которое оценивается как «ложное», что может быть чем угодно, чего нет в списке выше. По соглашению и для простоты perl возвращает 1 (хотя вы не должны полагаться на это; он вполне может вернуть список, содержащий серию случайных чисел, потому что это также будет иметь значение «true».)

Подобное происходит, когда вы запрашиваете обратное значение, которое оценивается как "истина". На самом деле выводится не «ничего», это пустая строка (''), которая, как я уже говорил, оценивается как «ложь» в логических выражениях. Вы можете проверить это:

print "This evaluates to false\n" if( (!1) eq '');

Если вы спрашиваете, почему perl выплевывает пустую строку вместо одного из «ложных» значений, возможно, это потому, что perl создан для обработки строк, и это вполне разумная строка для возврата.

9 голосов
/ 16 июля 2009

Операторы, которые возвращают только логический результат, всегда будут возвращать 1 для истины и специальное ложное значение, которое "" в строковом контексте, но 0 в числовом контексте.

6 голосов
/ 17 июля 2009

Вот дополнение к другим замечательным ответам, которые вы уже получили.

Не есть не не

Рассмотрим следующий код, который проверяет каждый из операторов Perl «не»:

#!/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'",
        '';

}

Выполнение этого приводит к следующему. Обратите внимание на предупреждающее сообщение:

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'

Из этого мы узнаем несколько вещей.

Первые два предмета не так уж и интересны:

  • !1 и not 1 ведут себя в основном одинаково.
  • Неудивительно, что ~1 отличается (битовое нет).

Теперь интересный предмет:

  • Хотя мы получаем предупреждение для строки 21 (0+''), при добавлении 0+!1.

Требуется два, чтобы запутаться

Что-то подозрительное происходит, и эта подозрительность связана со специальным скалярным контекстом в Perl. В этом случае проводится различие между числовым и строковым контекстами. И возможность создавать переменную, которая имеет разные значения в каждом контексте, или двойную переменную.

Похоже, !1 возвращает двойную переменную, которая возвращает 0 в числовом контексте и пустую строку в строковом контексте.

Тест Dualvar в конце показывает, что самодельный dualvar работает так же, как !1.

Но это хорошо

Как и многие другие функции Perl, сначала кажется, что двойные переменные не соответствуют ожиданиям и могут вводить в заблуждение. Однако, как и другие функции, при правильном использовании они значительно облегчают жизнь.

Насколько я знаю, дуальная переменная 0 и '' является единственным определенным значением, которое будет возвращать false во всех скалярных контекстах. Так что это очень разумное возвращаемое значение для !1. Можно утверждать, что undef является хорошим ложным результатом, но тогда неинициализированная переменная не отличается от ложного значения. Кроме того, попытки распечатать или добавить результаты логических выражений будут сопровождаться ненужными предупреждениями.

Другой известный дуалвар - $! или $OS_ERROR, если вы use English. В числовой форме вы получите код ошибки, в строковой форме код ошибки переведен для вас.

Это не ничто, это пусто, но это ничто

Итак, в итоге вы ничего не получаете, вы не получаете пустую строку и не получаете ноль.

Вы получаете переменную, которая является одновременно пустой строкой и 0.

3 голосов
/ 16 июля 2009

См. perldoc perlsyn :

Правда и Ложь

Число 0, строки '0' и '', пустой список () и undef - все false в логическом контексте. Все остальные значения верны. Отрицание истинного значение по! или не возвращает специальный ложное значение Когда оценивается как строка это трактуется как '', но как число, оно рассматривается как 0.

Там, если вы напечатаете значение как число, вы получите 0 вместо пустой строки:

printf "%d\n", $_ for map { !$_ } (1, 0);

или

print 0 + $_, "\n" for map { !$_ } (1, 0);

Сравните их с

printf "%s\n", $_ for map { !$_ } (1, 0);

и

print $_, "\n" for map { !$_ } (1, 0);
3 голосов
/ 16 июля 2009

Оператор ! выполняет логические операции. "" (пустая строка) такая же, как false, как 0. 1 - это удобное true значение. ! не следует полагаться на то, чтобы делать что-либо, кроме как производить какое-либо значение true / false. Полагаться на точное значение сверх этого опасно и может меняться в разных версиях Perl.

...