Подтвердить что ты не робот

Почему Perl использует пустую строку для представления логического значения false?

При оценке выражения в скалярном (булевом) контексте Perl использует явное значение 1 как результат, если выражение оценивается как true и пустая строка, если выражение принимает значение false. Мне любопытно, почему Perl использует пустую строку для представления логического ложного значения, а не 0, что кажется более интуитивным.

Обратите внимание, что я не заинтересован в том, что Perl обрабатывает пустую строку как ложный в скалярном (булевом) контексте.

ИЗМЕНИТЬ

Как использовать строку, которая истинна ("false" например), как строковое представление ложных значений, изменяет смысл существующего кода? Можем ли мы сказать, что код, который изменяет семантику после такого изменения, менее надежный/правильный, чем мог бы быть? Я предполагаю, что строковый контекст настолько распространен в Perl, что единственный вариант, приводящий к разумной семантике, заключается в том, что если логическое значение сохраняет свое значение после округления до строки и из строки...

4b9b3361

Ответ 1

Различные логические операторы не возвращают пустую строку, они возвращают ложное или истинное значение во всех трех простых скалярных типах. Кажется, что он возвращает пустую строку, потому что print заставляет контекст строки в своих аргументах:

#!/usr/bin/perl

use strict;
use warnings;

use Devel::Peek;

my $t = 5 > 4;
my $f = 5 < 4;

Dump $t;
Dump $f;

Вывод:

SV = PVNV(0x100802c20) at 0x100827348
  REFCNT = 1
  FLAGS = (PADMY,IOK,NOK,POK,pIOK,pNOK,pPOK)
  IV = 1
  NV = 1
  PV = 0x100201e60 "1"\0
  CUR = 1
  LEN = 16
SV = PVNV(0x100802c40) at 0x100827360
  REFCNT = 1
  FLAGS = (PADMY,IOK,NOK,POK,pIOK,pNOK,pPOK)
  IV = 0
  NV = 0
  PV = 0x100208ca0 ""\0
  CUR = 0
  LEN = 16

Для тех, кто не знаком с внутренними элементами Perl 5, a PVNV представляет собой скалярную структуру, которая содержит все три простых скалярных типа (integer IV, double precision float NV и string PV). Флаги IOK, NOK и POK означают, что целочисленные, двойные и строковые значения находятся в синхронизации (для некоторого определения синхронизации), поэтому любой из них может быть использован (т.е. никакие преобразования не должны если вы используете его как целое, двойное или строковое).

Я предполагаю, что пустая строка была выбрана для ложной строки, потому что она меньше, а больше соответствует идее ложной строки, чем "0". Игнорируйте мое утверждение о том, что оно меньше, оба "" и "1" имеют одинаковый размер: шестнадцать символов. Это так прямо на свалке. Perl 5 добавляет дополнительное пространство для строк, чтобы они быстро расширялись.

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

Ответы на вопросы в разделе EDIT:

Как использовать строку, которая истинна (например, "false" ) в качестве строкового представления ложных значений, изменяет значение существующего кода?

Единственное, что касается PL_sv_yes и PL_sv_no (канонически истинные и ложные значения, возвращаемые операторами сравнения), - это то, что они доступны только для чтения и создаются perl не запущенной программой. Если вы измените их, это не изменит критерий правдивости, поэтому PL_sv_no, установленный на "false", будет считаться истинным. Вы даже можете сделать это самостоятельно (этот код перестает работать в какой-то момент между Perl 5.18 и последним Perl), используя недокументированные функции perl:

#!/usr/bin/perl

use strict;
use warnings;
use Scalar::Util qw/dualvar/;

BEGIN {
        # use the undocumented SvREADONLY function from Internals to
        # modify a reference to PL_sv_no readonly flag
        # note the use of & to make the compiler not use SvREADONLY's
        # prototype, yet another reason prototypes are bad and shouldn't
        # be used
        &Internals::SvREADONLY(\!!0, 0);

        # set PL_sv_no to a dualvar containing 0 and "false"
        ${\!!0} = dualvar 0, "false";
}

if (5 < 4) {
        print "oops\n";
}

выходы

opps

Это потому, что тест правности сначала смотрит на строки.

Можно ли сказать, что код, который изменяет семантику после такого изменения, менее надежный/правильный, чем он мог быть?

Он будет сломан. Даже если вы ограничиваете себя установкой на int 0 или строку "0" (оба из которых являются ложными), он сломает некоторый действительный код.

Я предполагаю, что строковый контекст настолько распространен в Perl, что единственный параметр, приводящий к разумной семантике, заключается в том, что если логическое значение сохраняет свое значение после округления в строку и из строки...

Да.

Ответ 2

Вы можете перегрузить строковую строку true, false и undef, например this:

&Internals::SvREADONLY( \ !!1, 0);    # make !!1 writable
${ \ !!1 } = 'true';                  # change the string value of true
&Internals::SvREADONLY( \ !!1, 1);    # make !!1 readonly again
print 42 == (6*7);                    # prints 'true'

&Internals::SvREADONLY( \ !!0, 0);    # make !!0 writable
${ \ !!0 } = 'false';                 # change the string value of false
&Internals::SvREADONLY( \ !!0, 1);    # make !!0 readonly again
print 42 == (6*6);                    # prints 'false'

Ответ 3

Это не просто "", что false в Perl. Что касается того, почему... это либо потому, что Perl является удивительным или ужасным - в зависимости от ваших личных предпочтений:)

Ответ 4

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

За дополнительной информацией обратитесь к Как использовать логические переменные в Perl?".

Ответ 5

Вот как я столкнулся с проблемой:

my $res = ($a eq $b) *1;

*1 преобразует логическое значение, полученное из ($a eq $b), в скаляр.