rails programming portable language framework characteristics ruby language-design

programming - ruby server



¿Por qué Ruby tiene TrueClass y FalseClass en lugar de una sola clase booleana? (8)

Estaba trabajando en la serialización de valores cuando me enteré de este. Ruby tiene una clase TrueClass y una clase FalseClass , pero no tiene una clase Boolean . Me gustaría saber por qué es esto.

Veo algunas ventajas al usar un Boolean ; por ejemplo, el análisis sintáctico de cadenas podría centralizarse en él.

Los desarrolladores de Ruby son más inteligentes que yo, así que debe haber muchas buenas razones que simplemente no veo. Pero en este momento me parece que tengo OneClass y una TwoClass lugar de Fixnum .


Citando el foro Matz on Ruby (2013) :

... No hay nada verdadero y falso comúnmente compartido, por lo tanto no hay clase booleana. Además de eso, en Ruby, todo se comporta como valor booleano ...


Como otros han dicho, podrías "parchar" a Ruby. Crea tu propia clase. Aquí hay algo que se me ocurrió. Los métodos en la clase booleana son un poco tontos, pero podrían ser útiles programáticamente en algún momento.

class Boolean def self.new(bool) bool end def self.true true end def self.false false end end class FalseClass def is_a?(other) other == Boolean || super end def self.===(other) other == Boolean || super end end class TrueClass def is_a?(other) other == Boolean || super end def self.===(other) other == Boolean || super end end


Dado que todo, excepto false y nil evalúa como verdadero en Ruby de forma predeterminada, solo necesitaría agregar el análisis sintáctico a String.

Algo como esto podría funcionar:

class Object ## Makes sure any other object that evaluates to false will work as intended, ## and returns just an actual boolean (like it would in any context that expect a boolean value). def trueish?; !!self; end end class String ## Parses certain strings as true; everything else as false. def trueish? # check if it''s a literal "true" string return true if self.strip.downcase == ''true'' # check if the string contains a numerical zero [:Integer, :Float, :Rational, :Complex].each do |t| begin converted_number = Kernel.send(t, self) return false if converted_number == 0 rescue ArgumentError # raises if the string could not be converted, in which case we''ll continue on end end return false end end

Cuando se usa, esto te daría:

puts false.trueish? # => false puts true.trueish? # => true puts ''false''.trueish? # => false puts ''true''.trueish? # => true puts ''0''.trueish? # => false puts ''1''.trueish? # => true puts ''0.0''.trueish? # => false puts ''1.0''.trueish? # => true

Creo que parte de la "gran idea" detrás de Ruby es hacer que el comportamiento que deseas sea inherente a tu programa (por ejemplo, análisis sintáctico booleano), en lugar de crear una clase completamente encapsulada que viva en su propio espacio de nombres (por ejemplo, BooleanParser).


El objetivo de una clase es agrupar objetos similares u objetos con un comportamiento similar. 1 y 2 son muy similares, por lo tanto, tiene mucho sentido que estén en la misma clase. true y false sin embargo, no son similares. De hecho, su punto es que son exactamente lo opuesto entre sí y tienen un comportamiento opuesto. Por lo tanto, no pertenecen a la misma clase.

¿Puedes dar un ejemplo de qué tipo de comportamiento común implementarías en una clase Boolean ? No puedo pensar en nada.

Miremos el comportamiento que tienen TrueClass y FalseClass : allí hay exactamente cuatro métodos. No más. Y en cada caso, los dos métodos hacen exactamente lo contrario . ¿Cómo y por qué lo pondrías en una sola clase?

Así es cómo implementa todos esos métodos:

class TrueClass def &(other) other end def |(_) self end def ^(other) !other end def to_s ''true'' end end

Y ahora al revés:

class FalseClass def &(_) self end def |(other) other end def ^(other) other end def to_s ''false'' end end

De acuerdo, en Ruby, hay una gran cantidad de "magia" detrás de escena que TrueClass y FalseClass no FalseClass sino que están FalseClass en el intérprete. Cosas como if , && , || y ! . Sin embargo, en Smalltalk, del cual Ruby pidió prestado mucho, incluido el concepto de FalseClass y TrueClass , todos estos se implementaron también como métodos, y usted puede hacer lo mismo en 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

Y otra vez al revés:

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

Hace un par de años, escribí lo anterior solo por diversión e incluso lo publiqué . Aparte del hecho de que la sintaxis se ve diferente porque Ruby utiliza operadores especiales mientras yo uso solo métodos, se comporta exactamente como los operadores integrados de Ruby. De hecho, en realidad tomé el equipo de pruebas de conformidad RubySpec y lo transferí a mi sintaxis y se transfiere .


En Ruby nil y falso son falsos y todo lo demás es verdadero. Por lo tanto, no hay necesidad de una clase booleana específica.

Puedes probarlo :

if 5 puts "5 is true" end

5 evalúa a verdadero

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

Se imprimirá "nil is false"


La razón principal es simplemente el hecho de que es mucho más rápido y sencillo implementar expresiones booleanas que en la actualidad con una clase booleana que implicaría una conversión.

Como le dijo Mongus Pong, cuando escribe "si", le pide al intérprete que lo evalúe y luego se bifurca. Si tuviera una clase booleana, tendría que convertir la evaluación de la cosa en una booleana antes de la bifurcación (un paso más).

Recuerde que tal conversión booleana -> estaría disponible como método Ruby en la clase booleana. Este método podría cambiarse dinámicamente como cualquier otro método de Ruby, permitiendo al desarrollador desordenar las cosas por completo (lo cual no es tan grave) pero, claramente, esto no le permitiría al intérprete optimizar las pruebas como debería.

¿Te das cuenta de que reemplazaría unas pocas instrucciones de funcionamiento de la CPU por una llamada a método completo, que es costosa en Ruby (recuerda el procesamiento del método "enviar") ...


Parece que el propio Matz respondió esta pregunta en un mensaje de lista de correo en 2004.

Versión resumida de su respuesta: "en este momento funciona bien, agregar un booleano no da ninguna ventaja".

Personalmente, no estoy de acuerdo con eso; el "análisis sintáctico de cadenas" antes mencionado es un ejemplo. Otra es que cuando se aplica un tratamiento diferente a una variable según su tipo (es decir, un analizador yml) tener una clase "booleana" es útil, se elimina uno "si". También parece más correcto, pero esa es una opinión personal.


verdadero y falso podría ser administrado por una clase booleana que contenía varios valores, pero luego el objeto de clase debería tener valores internos y, por lo tanto, debe ser referenciado con cada uso.

En cambio, Ruby trata true y false como valores largos (0 y 1), cada uno de los cuales corresponde a un tipo de clase de objeto (FalseClass y TrueClass). Al usar dos clases en lugar de una única clase booleana, cada clase no requiere ningún valor y, por lo tanto, se puede distinguir simplemente por su identificador de clase (0 o 1). Creo que esto se traduce en importantes ventajas de velocidad internas para el motor Ruby, porque internamente Ruby puede tratar TrueClass y FalseClass como valores largos que requieren cero traducción de su valor de ID, mientras que un objeto booleano debería ser desreferenciado antes de poder ser evaluado .