Почему объект Regexp в Ruby считается "ложным"? - PullRequest
15 голосов
/ 11 октября 2019

Ruby имеет универсальную идею " истинность " и " ложь ".

Ruby имеет два специальных класса для логических объектов, TrueClass и FalseClass, причем единичные экземпляры обозначаются специальными переменными true и false соответственно.

Однако истинность и ложность не ограничиваются экземплярами этих двух классов, концепция универсальна и применяется к каждому отдельному объекту в Ruby. Каждый объект является либо правдивым , либо ложным . Правила очень просты. В частности, только два объекта являются ложными :

  • nil, единичный экземпляр NilClass и
  • false, единственный экземпляр FalseClass

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

Эти правила встроены в язык и не могут быть определены пользователем. Не существует to_bool неявного преобразования или чего-либо подобного.

Вот цитата из Спецификации языка Ruby ISO :

6.6 Булевы значения

Объект классифицируется как истинный объект или ложный объект .

Только false и nil являются ложными объектами. false - единственный экземпляр класса FalseClass (см. 15.2.6), которому оценивается ложное выражение (см. 11.5.4.8.3). nil - единственный экземпляр класса NilClass (см. 15.2.4), для которого nil-выражение оценивает (см. 11.5.4.8.2).

Объекты, отличные от false и nil , классифицируются как истинные объекты. true является единственным экземпляром класса TrueClass (см. 15.2.5), которому оценивается true-выражение (см. 11.5.4.8.3).

Кажется, что исполняемый Ruby / Spec согласен :

it "considers a non-nil and non-boolean object in expression result as true" do
  if mock('x')
    123
  else
    456
  end.should == 123
end

Согласно этим двум источникам, я бы предположил, что Regexp s также правдиво , но, согласно моим тестам, это не так:

if // then 'Regexps are truthy' else 'Regexps are falsy' end
#=> 'Regexps are falsy'

Я проверял это на YARV 2.7.0-preview1 , TruffleRuby 19.2.0.1 и JRuby 9.2.8.0 . Все три реализации согласуются друг с другом и не согласуются со Спецификацией языка Ruby ISO и моей интерпретацией Ruby / Spec.

Точнее, Regexp объектов, которые являются результатом оценки литералов Regexp являются ложными , тогда как Regexp объектами, которые являются результатом какого-либо другого выражения, являются правдивые :

r = //
if r then 'Regexps are truthy' else 'Regexps are falsy' end
#=> 'Regexps are truthy'

Является ли это ошибкой или желательноповедение?

Ответы [ 2 ]

4 голосов
/ 11 октября 2019

Это не ошибка. Происходит следующее: Ruby переписывает код так, что

if /foo/
  whatever
end

фактически становится

if /foo/ =~ $_
  whatever
end

Если вы запускаете этот код в обычном сценарии (и не используете опцию -e) тогда вы должны увидеть предупреждение:

warning: regex literal in condition

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

$ ruby -ne 'print if /foo/' filename

(Аргумент по умолчанию для print также $_.)

3 голосов
/ 11 октября 2019

Это результат (насколько я могу судить) недокументированной возможности языка ruby, которая лучше всего объясняется этой спецификацией :

it "matches against $_ (last input) in a conditional if no explicit matchee provided" do
  -> {
    eval <<-EOR
    $_ = nil
    (true if /foo/).should_not == true
    $_ = "foo"
    (true if /foo/).should == true
    EOR
  }.should complain(/regex literal in condition/)
end

Обычно вы можете$_ воспринимается как "последняя строка, прочитанная gets"

Чтобы еще больше запутать ситуацию, $_ (вместе с $-) равно , а не глобальная переменная;он имеет локальную область действия .


Когда запускается скрипт ruby, $_ == nil.

Итак, код:

// ? 'Regexps are truthy' : 'Regexps are falsey'

интерпретируетсянапример:

(// =~ nil) ? 'Regexps are truthy' : 'Regexps are falsey'

... Что возвращает фальси.

С другой стороны, для не буквального регулярного выражения (например, r = // или Regexp.new('')), это специальное толкование не применяется.

// является правдой;как и все другие объекты в ruby, кроме nil и false.


Если сценарий ruby ​​не запущен непосредственно в командной строке (т. е. с флагом -e), парсер ruby ​​будет отображатьсяпредупреждение против такого использования:

предупреждение: регулярное выражение в условии

Вы могли бы использовать это поведение в сценарии, с чем-то вроде:

puts "Do you want to play again?"
gets
# (user enters e.g. 'Yes' or 'No')
/y/i ? play_again : back_to_menu

... Но было бы более нормально назначить локальную переменную результату gets и выполнить проверку регулярного выражения по этому значению явно.

I'mне знает ни одного варианта использования для выполнения этой проверки с пустым регулярным выражением, особенно когда оно определено как буквальное значение. Выделенный вами результат действительно застал бы врасплох большинство разработчиков ruby.

...