Почему Ruby имеет TrueClass и FalseClass вместо единственного булева класса?

Я работал над сериализацией значений при обнаружении об этом. Ruby имеет a TrueClass класс и a FalseClass класс, но это имеет нет Boolean класс. Я хотел бы знать, почему это.

Я вижу некоторые преимущества в использовании a Boolean; например, строковый парсинг мог быть централизован на нем.

Разработчики Ruby более умны, чем я, таким образом, должно быть много серьезных оснований, что я просто не вижу. Но прямо сейчас это смотрит на меня как наличие OneClass и a TwoClass вместо Fixnum.

73
задан Andrew Marshall 5 June 2012 в 15:55
поделиться

5 ответов

Кажется, сам Матц ответил на этот вопрос в сообщении списка рассылки в 2004 году.

Краткая версия его ответа: "сейчас это работает нормально, добавление булевой функции не дает никаких преимуществ".

Лично я с этим не согласен; вышеупомянутый "разбор строки" - один из примеров. Другой пример - когда вы применяете различное обращение к переменной в зависимости от ее типа (например, парсер yml), иметь класс "Boolean" удобно - он убирает одно "if". Кроме того, это выглядит более корректно, но это личное мнение.

21
ответ дан 24 November 2019 в 12:24
поделиться

Цель класса - сгруппировать похожие объекты или объекты с похожим поведением. 1 и 2 очень похожи, поэтому для них вполне логично находиться в одном классе. true и false, однако, не похожи. Фактически, вся их суть в том, что они в точности противоположны друг другу и имеют противоположное поведение. Поэтому они не принадлежат к одному классу.

Можете ли вы привести пример того, какое общее поведение вы бы реализовали в классе Boolean? Я не могу ничего придумать.

Давайте просто посмотрим на поведение классов TrueClass и FalseClass: там есть ровно четыре метода. Не больше. И в каждом отдельном случае эти два метода делают прямо противоположное. Как и зачем вам помещать это в один класс?

Вот как вы реализуете все эти методы:

class TrueClass
  def &(other)
    other
  end

  def |(_)
    self
  end

  def ^(other)
    !other
  end

  def to_s
    'true'
  end
end

А теперь наоборот:

class FalseClass
  def &(_)
    self
  end

  def |(other)
    other
  end

  def ^(other)
    other
  end

  def to_s
    'false'
  end
end

Конечно, в Ruby есть много "магии", которая происходит за кулисами и которая на самом деле не обрабатывается TrueClass и FalseClass, а встраивается в интерпретатор. Такие вещи, как if, &&, || и ! . Однако в Smalltalk, из которого Ruby позаимствовал многое, включая концепцию FalseClass и TrueClass, все это реализовано в виде методов, и вы можете сделать то же самое в Ruby:

class TrueClass
  def if
    yield
  end

  def ifelse(then_branch=->{}, _=nil)
    then_branch.()
  end

  def unless
  end

  def unlesselse(_=nil, else_branch=->{})
    ifelse(else_branch, _)
  end

  def and
    yield
  end

  def or
    self
  end

  def not
    false
  end
end

И наоборот:

class FalseClass
  def if
  end

  def ifelse(_=nil, else_branch=->{})
    else_branch.()
  end

  def unless
    yield
  end

  def unlesselse(unless_branch=->{}, _=nil)
    ifelse(_, unless_branch)
  end

  def and
    self
  end

  def or
    yield
  end

  def not
    true
  end
end

Пару лет назад я написал вышеописанное просто для развлечения и даже опубликовал его. Кроме того, что синтаксис выглядит иначе, потому что Ruby использует специальные операторы, а я использую только методы, он ведет себя точно так же, как встроенные операторы Ruby. Фактически, я взял набор тестов на соответствие RubySpec и перенес его на мой синтаксис, и он прошел.

60
ответ дан 24 November 2019 в 12:24
поделиться

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

Вместо этого, Ruby рассматривает true и false как длинные значения (0 и 1), каждое из которых соответствует типу класса объекта (FalseClass и TrueClass). Благодаря использованию двух классов вместо одного класса Boolean, каждый класс не требует никаких значений и поэтому может различаться просто по идентификатору класса (0 или 1). Я считаю, что это дает значительные преимущества в скорости работы движка Ruby, поскольку внутри Ruby можно рассматривать TrueClass и FalseClass как длинные значения, которые требуют нулевой трансляции из их идентификатора, в то время как булевский объект должен быть отнесен к другому классу, прежде чем его можно будет оценить.

4
ответ дан 24 November 2019 в 12:24
поделиться

В Ruby nil и false являются ложными, а все остальное верно. Следовательно, нет необходимости в конкретном логическом классе.

Вы можете попробовать:

if 5
  puts "5 is true"
end

5 оценивается как истина

if nil
    puts "nil is true"
else
    puts "nil is false"
end

Выведет «nil is false»

1
ответ дан 24 November 2019 в 12:24
поделиться

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

Как сказал вам Монгус Понг, когда вы пишете «если», вы просите интерпретатора оценить вещь , а затем ветвиться. Если бы у вас был логический класс, вам пришлось бы преобразовать оценку thing в логическое до ветвления (еще один шаг).

Помните, что такие ->Булинское преобразование будет доступно в виде метода Ruby в классе Boolean. Затем этот метод может быть динамически изменен, как и любой другой метод Ruby, что позволит разработчику полностью испортить вещи (что на самом деле не так серьезно), но очевидно, что это не позволит интерпретатору оптимизировать тесты так, как они должны.

Понимаете ли вы, что он заменит несколько инструкций процессора полным вызовом метода, который является дорогостоящим в Ruby (помните обработку метода «отправить»)...

1
ответ дан 24 November 2019 в 12:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: