jueves, 18 de agosto de 2011

La simplicidad de Ruby

Una de las cosas que me gustan de Ruby, es la simplicidad.

Hoy necesitaba generar números hexadecimales de largo 24, de forma aleatoria, así que me hice un programita...

Hay seguramente muchas formas de hacerlo, yo elegí esta:

Seudocódigo:
24 veces:
  elegir un dígito aleatorio entre 0 y F (hexadecimal)
  imprimirlo
Código Ruby:
hexaDigit = ('0'..'9').to_a + ('A'..'F').to_a
24.times do
  print hexaDigit[rand(16)]
end
Simple, ¿no? Se puede escribir más corto, pero no mucho más claro que esto...

15 comentarios:

  1. Muy bueno Marcos! va una sugerencia (nuevito en Ruby, no tengo ni unas pocas horas de mirar un par de tutoriales y ya estoy opinando sobre Ruby)

    24.times do print rand(16).to_s(16)end

    :)

    ResponderEliminar
  2. David: está bueno el tip de .to_s(16), no lo tenía.

    Igual lo que decía, es más corto de escribir, pero no necesariamente más claro. Tenés que saber que significa ese to_s(16) para entender el programa...

    Lo podés hacer incluso más corto (como 3 caracteres menos!), si cambiás el do-end por { }. Así:
    24.times { print rand(16).to_s(16) }

    ResponderEliminar
  3. si, el to_s también puede ser más claro si se hace to_s(base=16) lo del {} buen dato :)

    ResponderEliminar
  4. Dejo el código equivalente en Python, no de pesado, sino en honor al Python Day a celebrarse mañana :)

    from random import random
    print [("%X" % int(random()*15)) for x in xrange(24)]

    ResponderEliminar
  5. Ah, perdón, faltó unirlos!

    print "".join([("%X" % int(random()*15)) for x in xrange(24)])

    ResponderEliminar
  6. David: Bueno, creo que entonces tenemos un ganador... Con el to_s(base=16) sí queda bien claro.

    ResponderEliminar
  7. Varrojo: Python es un lenguaje muy interesante, y en muchos aspectos comparable a Ruby. Pero en este caso, creo que el "24.times do..." supera ampliamente la solución en Python.

    ResponderEliminar
  8. Hola,

    Interesante como se escribe en Ruby y lo fácil de leer que queda. Me di cuenta lo que hacia más rápido en Ruby que en Python. En Smalltalk es así:

    hexaDigit := ('0' completeTo: '9'), ('A' completeTo: 'F').
    random := RandomLinearCongruential newParkMiller.
    1 to: 24 do: [:index |
    Transcript show: (hexaDigit at: ((random next * 16) asInteger + 1)).
    ].

    Es bastante similar a Ruby.

    Saludos,
    Bruno

    ResponderEliminar
  9. PD: para hacerlo mas parecido a Ruby podemos implementar el siguiente método en la clase Number.

    Number
    timesRepeat: aBlock
    "Evaluate the argument receiver object (self) times"
    ^(1 to: self do: aBlock)

    Al tener el nuevo método #timesRepeat:, que tiene como argumento un bloque de código (BlockClosure) el ejemplo queda:

    hexaDigit := ('0' completeTo: '9'), ('A' completeTo: 'F').
    random := RandomLinearCongruential newParkMiller.
    24 timesRepeat: [
    Transcript show: (hexaDigit at: ((random next * 16) asInteger + 1)).
    ].

    ResponderEliminar
  10. Perdon por la 3ra entrada pero ...
    PD2:
    El método #completeTo: tampoco lo tenia disponible así que lo agregue a la clase String.
    Para poder enviar el mensaje '0' completeTo: '9' etc. Crear los métodos me tomo un par de minutos.
    Con este método también queda parecido a Ruby.

    String
    completeTo: aString
    "Answer a new concatenated collection with the reciver to argument following the Ascii Table.
    Both string must have size = 1 otherwise answer an empty string"
    | newString |

    newString := ''.
    ((self size = 1) and: [aString size = 1]) ifFalse: [^newString].
    self first asciiValue to: aString first asciiValue do: [:index | newString := newString, (Character value: index) asString].
    ^newString

    ResponderEliminar
  11. Smalltalk: Creo que tus comentarios refuerzan mi punto. Estamos hablando que en Ruby se escribe el programa (y se lee casi como en inglés) usando menos de 50 caracteres.

    En tu solución usaste más de 500... sin contar las descripciones de los métodos.

    ResponderEliminar
  12. No conozco la filosofía de Ruby, pero en Smalltalk es la legibilidad del código, y no la cantidad de caracteres lo que importa. La cantidad de caracteres se ve atenuada por el "autocompletation", habrá 500 caracteres pero habré digitado 40.
    Y la descripción de los métodos es fundamental, más cuando se muestra el código a personas que no conocen el lenguaje (ya sea Ruby/C##/Smalltalk/etc).

    Veo que en Ruby se intenta escribir igual que en ingles, este es una de las ventajas de Smalltalk debido al uso de "keyword arguments" y "keyword messages".
    Otro punto importante es poder modificar las clases base como String e Integer, no se si se podrá hacer esto en Python o Ruby (pero es para otro post no este). No es una desventaja poder o tener que agregar métodos a las clases fundamentales, sino que es una ventaja INMENSA.

    Pero los mas importante es la legibilidad no la cantidad de caracteres escritos.

    Ruby
    hexaDigit = ('0'..'9').to_a + ('A'..'F').to_a
    24.times do
    print hexaDigit[rand(16)]
    end

    Smalltalk
    hexaDigit := ('0' completeTo: '9'), ('A' completeTo: 'F').
    random := RandomLinearCongruential newParkMiller.
    24 timesRepeat: [Transcript show: (hexaDigit at: ((random next * 16) asInteger + 1)).].

    No veo mucha diferencia, te acepto en este caso que la version de Ruby es un poco mas legible en "hexaDigit at:", pero en el caso de Ruby no estas especificando que función randomica que se esta usando.

    Saludos,
    Bruno

    ResponderEliminar
  13. Bruno:

    >Otro punto importante es poder modificar las
    >clases base como String e Integer, no se si se
    >podrá hacer esto en Python o Ruby

    En Python no se. En Ruby sí se puede, igual que se puede en otros lenguajes "modernos" como Objective-C (modernos dije, no?...) o más recientemente en C# 3.0

    >No veo mucha diferencia, te acepto en este caso
    >que la version de Ruby es un poco mas legible en
    >"hexaDigit at:"

    La versión final en Ruby, gracias al aporte de David, quedó así:
    24.times do print rand(16).to_s(base=16) end
    que no usa el array hexaDigit para nada.

    Y sí, en Smalltalk se puede hacer lo mismo, definiendo una extensión de Number para implementar el to_s(base=16) :)

    ResponderEliminar
  14. Esto es mucho más que genial!!:)!!!

    ResponderEliminar
  15. Este comentario ha sido eliminado por el autor.

    ResponderEliminar