Что означает «0, но факт» в Perl? - PullRequest
61 голосов
/ 25 сентября 2008

Может кто-нибудь объяснить, что именно означает "0, но true" в Perl? Насколько я понимаю, он равен нулю в целочисленном сравнении, но при использовании в качестве логического значения оценивается как true. Это правильно? Это нормальное поведение языка или это особая строка, интерпретируемая в интерпретаторе как особый случай?

Ответы [ 14 ]

56 голосов
/ 25 сентября 2008

Это нормальное поведение языка. Цитирование справочной страницы perlsyn:

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

Из-за этого должен быть способ возврата 0 из системного вызова, который ожидает возврата 0 в качестве (успешного) возвращаемого значения, и оставить способ сообщить о случае сбоя путем фактического возврата ложное значение "0 but true" служит этой цели.

51 голосов
/ 16 марта 2011

Потому что он жестко закодирован в ядре Perl, чтобы рассматривать его как число. Это хак, чтобы заставить соглашения Perl и ioctl играть вместе; от perldoc -f ioctl:

Возвращаемое значение ioctlfcntl) выглядит следующим образом:

if OS returns:      then Perl returns:

    -1              undefined value
     0              string "0 but true"
anything else       that number

Таким образом, Perl возвращает true в случае успеха и false в случае неудачи, но вы все еще может легко определить фактическое значение, возвращаемое операционная система:

$retval = ioctl(...) || -1;
printf "System returned %d\n", $retval;

Специальная строка "0 but true" освобождена от -w жалоб о неправильных числовых преобразованиях.

45 голосов
/ 25 сентября 2008

В дополнение к тому, что говорили другие, "0 but true" имеет специальный регистр, поскольку он не предупреждает в числовом контексте:

$ perl -wle 'print "0 but true" + 3'
3
$ perl -wle 'print "0 but crazy" + 3'
Argument "0 but crazy" isn't numeric in addition (+) at -e line 1.
3
32 голосов
/ 16 марта 2011

Значение 0 but true является частным случаем в Perl. Хотя для ваших простых смертных глаз это не похоже на число, мудрый и всем знающий Perl понимает, что это действительно число.

Это связано с тем, что когда подпрограмма Perl возвращает значение 0, предполагается, что подпрограмма завершилась неудачно или вернула ложное значение.

Представьте, что у меня есть подпрограмма, которая возвращает сумму двух чисел:

die "You can only add two numbers\n" if (not add(3, -2));
die "You can only add two numbers\n" if (not add("cow", "dog"));
die "You can only add two numbers\n" if (not add(3, -3));

Первое утверждение не умрет, потому что подпрограмма вернет 1. Это хорошо. Второе утверждение умрет, потому что подпрограмма не сможет добавить корова к собака .

А третье утверждение?

Хммм, я могу добавить 3 к -3. Я просто получаю 0, но моя программа умрет, даже если подпрограмма add сработает!

Чтобы обойти это, Perl считает 0 but true числом. Если моя подпрограмма add возвращает не просто 0 , но 0, но true , мое третье утверждение будет работать.

Но является ли 0, но истинным числовым нулем? Попробуйте это:

my $value = "0 but true";
print qq(Add 1,000,000 to it: ) . (1_000_000 + $value) . "\n";
print "Multiply it by 1,000,000: " . 1_000_000 * $value . "\n";

Да, это ноль!

Подпрограмма index - это очень старая часть Perl, существовавшая до концепции 0, но истинная была рядом. Предполагается вернуть позицию подстроки, расположенной в строке:

index("barfoo", "foo");   #This returns 3
index("barfoo", "bar");   #This returns 0
index("barfoo", "fu");    #This returns ...uh...

Последняя запись возвращает -1. Что означает, что если бы я сделал это:

if ($position = index($string, $substring)) {
   print "It worked!\n";
}
else {
   print "If failed!\n";
}

Как я обычно делаю со стандартными функциями, это не сработает. Если бы я использовал «barfoo» и «bar», как я делал во втором операторе, было бы выполнено предложение else, но если бы я использовал «barfoo» и «fu», как в третьем, было бы выполнено предложение if. Не то, что я хочу.

Однако, если подпрограмма index вернула 0, но true для второго оператора и undef для третьего оператора, мое предложение if / else сработало бы.

24 голосов
/ 25 сентября 2008

Вы также можете увидеть строку "0E0", используемую в коде Perl , и это означает то же самое, где 0E0 просто означает 0, записанный в экспоненциальной записи. Однако, поскольку Perl рассматривает только «0», «» или undef как false, он оценивается как true в логическом контексте.

12 голосов
/ 16 марта 2011

Он жестко запрограммирован в исходном коде Perl, в частности в Perl_grok_number_flags в numeric.c .

Читая этот код, я обнаружил, что строка "бесконечность" (без учета регистра) также проходит проверку look_like_number. Я этого не знал.

10 голосов
/ 25 сентября 2008

В целочисленном контексте он оценивается как 0 (числовая часть в начале строки) и равен нулю. В скалярном контексте это непустое значение, поэтому оно истинно.

  • if (int("0 but true")) { print "zero"; }

    (без вывода)

  • if ("0 but true") { print "true"; }

    (печатается верно)

9 голосов
/ 25 сентября 2008

0 означает false в Perl (и других языках, связанных с C). По большей части, это разумное поведение. Другие языки (например, Lua) обрабатывают 0 как true и предоставляют другой токен (часто nil или false ) для представления неверного значения.

Один из случаев, когда способ Perl не работает так хорошо, это когда вы хотите вернуть либо число, либо, если функция по какой-либо причине не работает, ложное значение. Например, если вы пишете функцию, которая читает строку из файла и возвращает количество символов в строке. Обычное использование функции может быть что-то вроде:

while($c = characters_in_line($file)){
    ...
};

Обратите внимание, что если количество символов в конкретной строке равно 0, цикл , в то время как закончится до конца файла. Таким образом, функция characters_in_line должна использовать специальный символ 0 символов и возвращать '0, но true' . Таким образом, функция будет работать так, как задумано в цикле while , но также будет возвращать правильный ответ, если она будет использоваться в качестве числа.

Обратите внимание, что это не встроенная часть языка. Скорее, он использует способность Perl интерпретировать строку как число. Поэтому вместо этого иногда используются другие укусы. DBI использует, например, "0E0" . При оценке в числовом контексте они возвращают 0 , но в логическом контексте false .

6 голосов
/ 16 марта 2011

Вещи, которые являются ложными:

  • "".
  • "0".
  • Вещи, относящиеся к тем.

"0 but true" не относится к числу таких, поэтому это не ложь.

Кроме того, Perl возвращает "0 but true", где ожидается число, чтобы сигнализировать о том, что функция завершилась успешно, даже если она вернула ноль. sysseek является примером такой функции. Поскольку ожидается, что значение будет использоваться как число, Perl кодируется, чтобы считать его числом. В результате при использовании в качестве числа предупреждения не выдаются, и looks_like_number("0 but true") возвращает значение true.

Другие "истинные нули" можно найти в http://www.perlmonks.org/?node_id=464548.

4 голосов
/ 28 сентября 2008

Я только что нашел доказательство того, что строка "0, но истина" фактически встроена в интерпретатор, как некоторые люди здесь уже ответили:

$ strings /usr/lib/perl5/5.10.0/linux/CORE/libperl.so | grep -i true
Perl_sv_true
%-p did not return a true value
0 but true
0 but true
...