Я работал над сериализацией значений при обнаружении об этом. Ruby имеет a TrueClass
класс и a FalseClass
класс, но это имеет нет Boolean
класс. Я хотел бы знать, почему это.
Я вижу некоторые преимущества в использовании a Boolean
; например, строковый парсинг мог быть централизован на нем.
Разработчики Ruby более умны, чем я, таким образом, должно быть много серьезных оснований, что я просто не вижу. Но прямо сейчас это смотрит на меня как наличие OneClass
и a TwoClass
вместо Fixnum
.
Кажется, сам Матц ответил на этот вопрос в сообщении списка рассылки в 2004 году.
Краткая версия его ответа: "сейчас это работает нормально, добавление булевой функции не дает никаких преимуществ".
Лично я с этим не согласен; вышеупомянутый "разбор строки" - один из примеров. Другой пример - когда вы применяете различное обращение к переменной в зависимости от ее типа (например, парсер yml), иметь класс "Boolean" удобно - он убирает одно "if". Кроме того, это выглядит более корректно, но это личное мнение.
Цель класса - сгруппировать похожие объекты или объекты с похожим поведением. 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 и перенес его на мой синтаксис, и он прошел.
true и false можно было бы управлять с помощью класса Boolean, который имел бы несколько значений, но тогда объект класса должен был бы иметь внутренние значения и, следовательно, должен был бы ссылаться на них при каждом использовании.
Вместо этого, Ruby рассматривает true и false как длинные значения (0 и 1), каждое из которых соответствует типу класса объекта (FalseClass и TrueClass). Благодаря использованию двух классов вместо одного класса Boolean, каждый класс не требует никаких значений и поэтому может различаться просто по идентификатору класса (0 или 1). Я считаю, что это дает значительные преимущества в скорости работы движка Ruby, поскольку внутри Ruby можно рассматривать TrueClass и FalseClass как длинные значения, которые требуют нулевой трансляции из их идентификатора, в то время как булевский объект должен быть отнесен к другому классу, прежде чем его можно будет оценить.
В 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»
Основная причина заключается просто в том, что реализовать логические выражения в том виде, в каком они есть в настоящее время, гораздо быстрее и проще, чем с булевым классом, который подразумевал бы преобразование.
Как сказал вам Монгус Понг, когда вы пишете «если», вы просите интерпретатора оценить вещь , а затем ветвиться. Если бы у вас был логический класс, вам пришлось бы преобразовать оценку thing в логическое до ветвления (еще один шаг).
Помните, что такие ->Булинское преобразование будет доступно в виде метода Ruby в классе Boolean. Затем этот метод может быть динамически изменен, как и любой другой метод Ruby, что позволит разработчику полностью испортить вещи (что на самом деле не так серьезно), но очевидно, что это не позволит интерпретатору оптимизировать тесты так, как они должны.
Понимаете ли вы, что он заменит несколько инструкций процессора полным вызовом метода, который является дорогостоящим в Ruby (помните обработку метода «отправить»)...