En Ruby todo es un objeto. Por eso es que es natual que cuando se le pasa el mensaje next a un objeto entero (Fixnum en este caso) la respuesta sea el próximo número entero, por ejemplo:

RUBY:
  1. irb(main):001:0> 2.next
  2. => 3

Por supuesto la aritmética funciona tal y cual como nuestras maestras se esforzaron en enseñarnos:

RUBY:
  1. irb(main):002:0> 2 + 2
  2. => 4

Sin embargo los enteros (Integers) son capaces de algunas cositas interesantes como:

RUBY:
  1. irb(main):002:0> 5.times { |i| printf("2^%d=%d\n",i,2**i) }
  2. 2^0=1
  3. 2^1=2
  4. 2^2=4
  5. 2^3=8
  6. 2^4=16
  7. => 5

El método times básicamente ejecuta el bloque (la construcción entre llaves en este caso) el número de veces indicado por el número entero al que se le aplica. La variable i, que es local al bloque, toma los valores desde 0 hasta número - 1 para cada iteración donde se ejecuta el bloque de código, que en este caso sencillamente imprime las potencias de 2.

Ajá, muy bonitos los ejemplos, pero ¿Y cómo sabemos que es 1, 70, o 15000 son Fixnums? Sencillo, se lo preguntamos, para eso son objetos ¿No?:

RUBY:
  1. irb(main):001:0> 1.class
  2. => Fixnum
  3. irb(main):002:0> 70.class
  4. => Fixnum
  5. irb(main):003:0> 15000.class
  6. => Fixnum
  7. irb(main):004:0> 15_000.class
  8. => Fixnum

Con 1, 70 y 15000 no tenemos problemas... Pero ¿Es 15_000 un Fixnum también? Pues sí, sólo que Ruby nos permite agregar entre los digitos underscores en forma arbitraria para mejorar la legibilidad como si fueran puntos: Azúcar Sintáctico.

Por otra parte si el entero es lo suficientemente grande, eventualmene pasará esto:

RUBY:
  1. irb(main):005:0> 9876543210.class
  2. => Bignum

Ruby no fué capaz de representar 9876543210 con un Fixnum y utilizó la clase Bignum. Sobre este punto elaboraremos más adelante.

Como ya vimos, las operaciones que comúnmente asociamos a los números están disponibes por supuesto. Pero en forma consistente con el manejo de los números en Ruby, estas operaciones están implementadas como métodos.

De esta forma podríamos redefinir el comportamiento del operador de suma "+" para siempre usar los valores absolutos de los operandos. Cosa que sin duda horrorizaría a nuestra maestras.

RUBY:
  1. puts 2 + (-3) # => -1
  2.  
  3. class Fixnum
  4.   alias sumaAnterior +
  5.   def +(otro)
  6.     self.abs.sumaAnterior(otro.abs)
  7.   end
  8. end
  9.  
  10. puts 2 + (-3) # => 5

Aquí sencilamente hemos creado un alias (copia, no referencia) al método que implementa la suma, para poder conservar ese comportamiento y recurrir a él posteriormente. Esto es posible hacerlo por que en Ruby los métodos son ¿Adivinen? Objetos, correcto.

Posteriormente cuando redefinimos la suma sencillamente calculamos el valor absoluto primero usando el método abs que Fixnum hereda de Integer, que a su vez lo hereda de Numeric. No me lo crean a mí, ustedes mismos pueden verificarlo:

RUBY:
  1. irb(main):013:0> clase = 1.class
  2. => Fixnum
  3. irb(main):014:0> clase = clase.superclass
  4. => Integer
  5. irb(main):015:0> clase = clase.superclass
  6. => Numeric
  7. irb(main):016:0> clase = Numeric.instance_methods.include?('abs')
  8. => true

Si bien el ejemplo anterior funciona, yo no les recomendaría dedicarse a ese tipo de manipulaciones ya que estarían efectivamente modificando el comportamiento de buena parte de los enteros para su aplicación de una forma más bien poco ortodoxa, por no decir demencial. Y seguramente serían castigados en el rincón, ya no digamos que por su maestra sino por su jefe.

Ahora, en lo que respecta a los número enteros sabemos que tenemos las clases Fixnum y los Bignum ¿Por qué tenemos dos clases distintas para representar los mismos comportamientos?

Bien, en cuanto a funcionalidad estas dos clases son básicamente iguales. Ruby elige en forma automática entre Fixnum y Bignum dependiendo del tamaño del literal a representar o resultado de la expresión evaluada. Los Fixnums permiten almacenar números enteros que pueden ser representados mediante el tamaño de una palabra nativa de la máquina, menos un bit.

RUBY:
  1. irb(main):031:0> x = 123456
  2. => 123456
  3. irb(main):032:0> x.class
  4. => Fixnum
  5. irb(main):033:0> x = x * 10000
  6. => 1234560000
  7. irb(main):034:0> x.class
  8. => Bignum
  9. irb(main):035:0> x = x / 20000
  10. => 61728
  11. irb(main):036:0> x.class
  12. => Fixnum

Lo bueno de todo esto, es que ha sido transparente para nosotros, ya que nunca necesitamos instanciar estos objetos en forma explícita. Y estos objetos exponen un comportamiento consistente para que nosotros podamos manipularlos sin problemas.

Fixnum sigue un patrón de Singleton. Esto se traduce en que nunca existirá más que un objeto Fixnum para cualquier entero.

RUBY:
  1. irb(main):038:0> a = 2 - 1
  2. => 1
  3. irb(main):039:0> a.object_id
  4. => 3
  5. irb(main):040:0> b = 0 + 1
  6. => 1
  7. irb(main):041:0> b.object_id
  8. => 3

En el ejemplo anterior pudimos constatar que que dos expresiones distintas, que dieron el mismo número entero como resultado, efectivamente "referencian" al mismo objeto 1 cuyo identificador único (object_id) es el mismo (3).

Al ser Singletons se gana en rendimiento ya que la instanciación de un nuevo objeto es mucho más costosa que "referenciar" a uno ya existente. El entrecomillado es a propósito, ya vamos a ver el motivo.

Si chequean la clase Fixnum en el Pickaxe, van a encontrar la siguiente oración "Los objetos Fixnum tienen un valor inmediato", mi traducción es libre ¿Inmediato? ¿Qué es eso de inmediato? (Este es el tipo de pregunta que nadie en el salón se atrevía a hacer, y donde todo el mundo ponía cara de inteligencia ;-)

Bueno, después de escarbar un poquito en comp.lang.ruby entendí. Los objetos Fixnum no existen más allá de la referencia a ellos, es decir su valor no reside en una ubicación de memoria que es referenciada por una variable o constante, sino que está almacenado en la propia referencia. Obviamente esto es una consideración realizada para mejorar el rendimiento en el manejo de estos números.

Una cosa que pareciera ser cierta y que no lo es (al menos como yo lo entiendo) es que los Fixnums son inmutables. Ya que es posible agregarle variables de instancia en el estilo de:

RUBY:
  1. class Fixnum
  2.    attr_accessor :letras
  3. end
  4.  
  5. 1.letras = "uno"
  6. 10.letras = "diez"
  7.  
  8. puts 1.letras # uno
  9. a = 1
  10. puts a.letras # uno
  11. puts (2-1).letras # uno
  12. puts 10.letras # diez

Sin embargo el intérprete no permitirá agregarle singleton methods a un Fixnum, supongo que por no penalizar la resolución de los métodos. La discusión sobre este tópico es un tanto bizarra, y suele estar aderezada con frases al estilo de "Use The Source Luke, use The Source", cosa que en general ni nos va a interesar ni es realmente necesario, ya que a efectos prácticos seguimos entre cómodos objetos.

Cuando el intérprete no puede representar una valor entero con un Fixnum, entonces apela a los Bignums, siempre en forma transparente para nosotros los programadores. Y estos objetos ni nos singletons, ni son inmediatos.

RUBY:
  1. irb(main):048:0> a = 9876543210
  2. => 9876543210
  3. irb(main):049:0> b = 9876543210
  4. => 9876543210
  5. irb(main):050:0> a.object_id
  6. => 24579420
  7. irb(main):051:0> b.object_id
  8. => 24567756

Por supuesto podemos resolver a los número reales con la representación de puntos flotante de doble precisión, basada en el tamaño de la palabra de la arquitectura nativa, con los objetos de la clase Float. A pesar del suspiro de culebra anterior, en realidad a estas alturas eso tiene poco de sorprendente.

RUBY:
  1. irb(main):010:0> ( 1.0 + 2.3 ).class
  2. => Float
  3. irb(main):014:0> 33e-1
  4. => 3.3
  5. irb(main):018:0> 1.e3
  6. NoMethodError: undefined method `e3' for 1:Fixnum
  7.         from (irb):18
  8. irb(main):019:0> 1.0e3
  9. => 1000.0

El error undefined method se genera porque se ha omitido un dígito al a derecha del punto, y Ruby intenta pasar el mensaje e3 al Fixnum 1, cosa que obviamente la clase no sabe como resolver.

En el caso de tener que manejar números decimales realmente grandes, tendremos que optar por usar la librería estándar BigDecimal, que además de las operaciones convencionales incluye algunas librerías matemáticas adicionales.

Ahora, nuestras abnegadas maestras se empeñaron en enseñarnos eso de sumar peras con manzanas no es una cosa buena. Así que de alguna forma hay que ver como hacemos para sumar números enteros y reales, con el fin de que nos nos castiguen después de clases, menos mal que los palmetazos ya no se usan.

Ruby tiene el concepto de protocolos de conversión, mediante el cual un objeto puede ser sujeto a una conversión a otra clase diferente de la propia.

RUBY:
  1. irb(main):007:0> "2" + 2
  2. TypeError: cannot convert Fixnum into String
  3.         from (irb):7:in `+'
  4.         from (irb):7
  5. irb(main):008:0> "2".to_i + 2
  6. => 4
  7. irb(main):009:0> "2".to_int + 2
  8. NoMethodError: undefined method `to_int' for "2":String
  9.         from (irb):9

En el ejemplo anterior llevamos las cosas un poco lejos, brincando fuera del mundo de los números. Sin embargo estos ejemplos son interesantes, ya que a diferencia de otros populares lenguajes interpretados, Ruby no convertirá en forma implícita un String al número equivalente a la representación entre comillas.

Por otra parte si utilizamos una conversión aproximada ("loose") invocando el método to_i sobre el String, obtenemos el resultado deseado. Sin embargo si intentamos una conversión estricta, que se obtiene mediante el método to_int, al intérprete no le gusta.

La diferencia es sutil, pero puede ser importante ya que la conversión aproximada se toma licencias como:

RUBY:
  1. irb(main):016:0> "tu maestra te puso un".to_i
  2. => 0

Claro, el caso que más nos interesa es al final es:

RUBY:
  1. irb(main):002:0> a = 2.5 + 5
  2. => 7.5
  3. irb(main):003:0> a.class
  4. => Float
  5. irb(main):004:0> a = a + 2.5
  6. => 10.0
  7. irb(main):005:0> a.class
  8. => Float

Donde se han aplicado las reglas de coerción numérica, para llevar a ambos números a una clase común donde la operación a ejecutar tenga sentido. La coerción numérica se basa en el método coerce de la clase Numeric.

Este método retorna un arreglo de dos elementos, donde ambos elementos del arreglo tienen la misma clase y sus valores son equivalentes a los valores suministrados. Cada vez que se ejecuta un operación sobre un número y el parámetro es de un clase distinta del receptor, se realiza la coersión.

En el Pickaxe van a encontrar este ejemplo muy bonito para proveer una conversión de la clase String a los descendientes de Numeric, ejemplo que me permito reproducir a continuación:

RUBY:
  1. class String
  2.   def coerce(other)
  3.     case other
  4.       when Integer
  5.         begin
  6.       return other, Integer(self)
  7.         rescue
  8.       return Float(other), Float(self)
  9.     end
  10.      when Float
  11.        return other, Float(self)
  12.      else
  13.         super
  14.      end
  15.    end
  16. end
  17.  
  18. puts 5 + "3" # => 8
  19. puts 5 - "2.5"  # => 7.5
  20. puts 1.2 + "2.1" # => 3.3
  21. puts 1.5 + "2" # => 2.5

Fíjense que si el parámetro es un entero se intenta llevar al receptor (self) a un entero, si esta operación falla con un ArgumentError: invalid value for Integer entonces se intenta con un Float para ambos.

Algo que amerita una aclaratoria son los métodos Integer, y Float que son métodos del módulo Kernel. Estos parecen clásicos constructores Java y violan la convención Ruby para los nombres métodos al tener capitalizada la primera letra. En palabras de Matz "Methods with class name constant e.g. Integer, String, etc. are converters in convention"

Bueno, para finalizar si hacen un require mathn, obtienen algo así como "Pimp My Numbers" ;-) y pueden hacer cosas como:

RUBY:
  1. irb(main):012:0> require 'mathn'
  2. => true
  3. irb(main):023:0> 1/2 + 1/2
  4. => 1
  5. irb(main):024:0> 2/6 * 2
  6. => 2/3
  7. irb(main):025:0> Math.sqrt(-1) + 1
  8. => Complex(1, 1)

No le enseñen a sus hijos a usar esta librería, o empezarán a ver que resuelven las tareas de matemáticas muy rápido.

Si llegaron hasta este punto, estoy seguro de que su maestra de primaría se sentiría orgullosos de ustedes y obtendrían una una calcomanía con estrella, un rubí o algo por el estilo. Espero que les haya gustado, y que se animen a investigar un poco sobre este facinante lenguaje de programación orientado a objetos que es Ruby.


58 Respuestas a “Todo lo que tu maestra no te dijo sobre los números en la clase de Ruby”

  1. 1 deisy

    serias tan amable dde dar una clase completa sobre numeros enteros

  2. 2 marihermis pinto

    que son los numeros naturales

  3. 3 mauricio

    ME PARECE UNA FORMA DE HACER MAS EXPLICITO EL CONOCIMINTO DE LAS MATEMATICAS.GRACIAS POR TODO ESTE TRABAJO TAN INTERESANTE. GRACIAS MUCHAS GRACIAS.

  4. 4 Evelyn

    Me parece muy interesante....pero como utilizo la libreria BigDecimal...para trabajar con numeros muy pequeños y que considere todos los decimales....???
    Gracias

  5. 5 auto

    uatbzkx

  6. 6 auto

    wshnq

  7. 7 auto

    pamdzlk

  8. 8 auto

    nleu fylpsr hpoejdf mnfh

  9. 9 auto

    yszdmao oqhl duiwg bmlzh

  10. 10 auto

    dsunh

  11. 11 auto

    zfgjh alseon bungc paleh

  12. 12 auto

    opyq txwd zmup

  13. 13 auto

    msloufk chyd cpvrt htoyr

  14. 14 auto

    rclvom utbrm

  15. 15 auto

    hwdya plvkm

  16. 16 auto

    ryahx plmeor lkob mngpirc

  17. 17 auto

    qylpeif ohtpifs

  18. 18 auto

    nwvpf hmjfao scbv

  19. 19 auto

    wlmbnoc

  20. 20 auto

    ofmlh sdgxci uarn

  21. 21 auto

    cpofyw idqnu

  22. 22 auto

    blfk

  23. 23 auto

    wiznkub mlbd

  24. 24 auto

    cfewn kvnubm pmwbqy

  25. 25 auto

    cwloby iqng wejczbu

  26. 26 auto

    zajglv oamxi ueridk vbmw

  27. 27 auto

    frbdmwq zpdq

  28. 28 auto

    jhioexb

  29. 29 auto

    rxui

  30. 30 auto

    lkhvc uwjxn wiyxq nyqte

  31. 31 auto

    wcrhatn

  32. 32 auto

    ebnf

  33. 33 auto

    zmrgv gnfyai

  34. 34 car

    hotq avko

  35. 35 car

    nrjmx valz fadszu sizcury

  36. 36 car

    jsfbrzo gustvfh lutj dfzxej

  37. 37 car

    daes

  38. 38 car

    ybum dprzk

  39. 39 bmw car stereo repair

    ofbr hposa rgixmap

  40. 40 car county junk new wayne yard york

    dves rqgcafi bpvidcu gfeaquz

  41. 41 wade ford used car

    jrpxmcy uwpnymt kbwjt

  42. 42 kuwait car dealers

    tlihv ouldabp

  43. 43 car technology timeline

    wpicxal gxabc nsel

  44. 44 glade car

    idbkzj rqlck

  45. 45 compact subwoofer car

    mgxdce jlcdhpf

  46. 46 buying car leasing new vs

    epkcg yhkdzl

  47. 47 car troubleshooting no heat

    rxoylu

  48. 48 usd car prices

    psbizn nsmef

  49. 49 buy here pay here car dealership

    nwzckh zvxqjy hfkb jxzueg

  50. 50 car parks in london w1

    wjfn

  51. 51 auto automobile boat car convertible cyc

    zspjn mbnjfkl

  52. 52 oasis car wash plano tx

    zihn efgp

  53. 53 bentley kit car

    vrde gjfw

  54. 54 hire purchase car loan

    usmzk

  55. 55 car leasing calculator australia

    tenkmr fgijvw hlgx fuwr

  56. 56 race car gifs

    zhsam pigkzoy ictv fsnwm

  57. 57 car rental 18 and over

    kuazdh ckshwd

  58. 58 acura california.com car gsr in integra

    sambiup xjghkld dlrek

Añade un Comentario





RSS feeds

Suscríbete a nuestros RSS Feeds