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

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

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

РЕДАКТИРОВАТЬ

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


person Piotr Dobrogost    schedule 12.10.2010    source источник
comment
Возможный дубликат Почему! 1 мне ничего не дает в Perl?   -  person Thilo    schedule 06.09.2016


Ответы (5)


Различные логические операторы не возвращают пустую строку, они возвращают ложное или истинное значение во всех трех простых скалярных типах. Просто похоже, что он возвращает пустую строку, потому что 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, PVNV - это скалярная структура, которая содержит все три простых скалярных типа (целое число IV, число с плавающей запятой двойной точности NV и строка PV). Флаги IOK, NOK и POK означают, что целые, двойные и строковые значения синхронизированы (для некоторого определения синхронизации), поэтому можно использовать любой из них (т.е. преобразование не требуется, если вы его используете. как целое, двойное или строковое).

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

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

Ответы на вопросы в разделе РЕДАКТИРОВАТЬ:

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

Единственные особенности 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's 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, что единственный вариант, ведущий к разумной семантике, - это если логическое значение сохранит свое значение после циклического перехода к строке и от нее ...

да.

person Chas. Owens    schedule 12.10.2010
comment
Я ненавижу / люблю вас обоих - из-за этого вопроса я только что отправил docpatch на p5p: perlguts ошибочно ссылается на PL_sv_no (рассматриваемый ложный скаляр) как PL_sv_false. :) - person hobbs; 12.10.2010
comment
@Piotr Dobrogost Посмотри, что ты наделал! Видите, что получается, если задавать вопросы? Вы делаете вещи в Perl 5 лучше! Как жить с собой? - person Chas. Owens; 12.10.2010
comment
@hobbs Не могли бы вы уточнить? - person Piotr Dobrogost; 14.10.2010
comment
@Piotr Dobrogost В документе perldoc perlguts неправильно указано, что существует PL_sv_false функция C, возвращающая то же значение как 1 < 0. Функция действительно называется PL_sv_no. Прочтите сообщение p5p, чтобы узнать больше Информация. - person Chas. Owens; 14.10.2010
comment
Что вы думаете о вопросах, которые я добавил в раздел РЕДАКТИРОВАНИЕ? - person Piotr Dobrogost; 27.01.2017

Вы можете перегрузить преобразование значений true, false и undef, например это:

&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'
person MkV    schedule 12.10.2010
comment
Очень приятная настройка. Я понятия не имел, что Perl позволяет такое. - person Piotr Dobrogost; 12.10.2010
comment
Пакет Internals и функции или переменные в нем не предназначены для общего пользования (отсюда и название). Глобальное изменение значений истинных и ложных значений, возвращаемых операторами, в высшей степени нежелательно. Обойти прототип SvREADONLY - это просто вишенка на торте. Тем не менее, изящно. - person Chas. Owens; 13.10.2010
comment
К сожалению, Perl 5.22, похоже, исправил это. - person Chas. Owens; 10.04.2016

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

person rfunduk    schedule 12.10.2010
comment
Perl - единственный язык, который одинаково читается до и после шифрования RSA. - person TBH; 12.10.2010
comment
@TBH Нет, APL есть. В незашифрованном виде Perl немного удобнее читать. - person Chas. Owens; 12.10.2010
comment
@Chas - Я думаю, вы хотели сказать в зашифрованном виде. - person DVK; 12.10.2010
comment
@DVK Хорошо сыграно, сэр, хорошо сыграно. - person Chas. Owens; 12.10.2010
comment
Это не ответ на вопрос. Вопрос не в том, что в Perl правда, а что ложь. - person Piotr Dobrogost; 23.11.2012

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

Дополнительные сведения см. В разделе "Как использовать логические переменные в Perl?" ".

person jsalonen    schedule 12.10.2010

Вот как я решил проблему:

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

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

person hoze    schedule 03.12.2014
comment
Собственно раньше это тоже было скалярное значение. Теперь это дополнительно числовое (IV) значение. Но использовать * 1 довольно умно ... - person Slaven Rezic; 25.02.2019