La sintaxis del lenguaje de programación Ruby es muy similar a la de Perl y Python . Las definiciones de clases y métodos se señalan mediante palabras clave, mientras que los bloques de código se pueden definir mediante palabras clave o llaves. A diferencia de Perl, las variables no se anteponen obligatoriamente con un símbolo . Cuando se utiliza, el símbolo cambia la semántica del alcance de la variable. A efectos prácticos, no hay distinción entre expresiones y sentencias . [ 1 ] [ 2 ] Los saltos de línea son significativos y se consideran el final de una sentencia; se puede usar un punto y coma de forma equivalente. A diferencia de Python, la indentación no es significativa.
Una de las diferencias con Python y Perl es que Ruby mantiene todas sus variables de instancia completamente privadas para la clase y solo las expone a través de métodos de acceso ( attr_writer, attr_reader, etc.). A diferencia de los métodos "getter" y "setter" de otros lenguajes como C++ o Java , los métodos de acceso en Ruby se pueden crear con una sola línea de código mediante metaprogramación ; sin embargo, también se pueden crear de la forma tradicional de C++ y Java. Como la invocación de estos métodos no requiere el uso de paréntesis, es trivial convertir una variable de instancia en una función completa sin modificar una sola línea de código de llamada ni tener que realizar ninguna refactorización , logrando una funcionalidad similar a la de los miembros de propiedad de C# y VB.NET .
Los descriptores de propiedades de Python son similares, pero implican una desventaja en el proceso de desarrollo. Si en Python se empieza utilizando una variable de instancia pública y posteriormente se modifica la implementación para usar una variable de instancia privada expuesta mediante un descriptor de propiedad, puede ser necesario ajustar el código interno de la clase para usar la variable privada en lugar de la pública. El diseño de Ruby obliga a que todas las variables de instancia sean privadas, pero también proporciona una forma sencilla de declarar setmétodos get. Esto concuerda con la idea de que en Ruby nunca se accede directamente a los miembros internos de una clase desde fuera de ella; en cambio, se envía un mensaje a la clase y se recibe una respuesta.
Sesiones interactivas
Los siguientes ejemplos se pueden ejecutar en un intérprete de comandos de Ruby, como Interactive Ruby Shell , o guardarlos en un archivo y ejecutarlos desde la línea de comandos escribiendo .ruby <filename>
Ejemplo clásico de "Hola mundo" :
pone '¡Hola mundo!'Código Ruby básico:
>> # Todo, incluyendo un literal, es un objeto, así que esto funciona: >> - 199 . abs => 199 >> 'ice is nice' . length => 11 >> 'ruby is cool.' . index ( 'u' ) => 1 >> "Nice Day Isn't It?" . downcase . split ( '' ) . uniq . sort . join => " '?acdeinsty"Aporte:
print 'Por favor, escriba su nombre >' nombre = gets . chomp puts "Hola #{ nombre } ."Conversiones:
puts 'Dame un número' number = gets . chomp puts number . to_i output_number = number . to_i + 1 puts output_number . to_s + ' es un número mayor.'Instrumentos de cuerda
En Ruby existen diversas formas de definir cadenas de caracteres.
Las siguientes tareas son equivalentes:
a = " \n Esta es una cadena entre comillas dobles \n " a = %Q{ \n Esta es una cadena entre comillas dobles \n } a = %{ \n Esta es una cadena entre comillas dobles \n } a = %/\nEsta es una cadena entre comillas dobles\n/ a = <<- BLOQUEEste es un bloque de cadena entre comillas doblesLas cadenas admiten interpolación variable :
var = 3 . 14159 "pi es #{ var } " => "pi es 3,14159"Las siguientes asignaciones son equivalentes y producen cadenas de caracteres sin procesar :
a = 'Esta es una cadena entre comillas simples' a = %q{Esta es una cadena entre comillas simples}Colecciones
Construcción y uso de un array :
>> a = [ 3 , 'hola' , 14 . 5 , 1 , 2 , [ 6 , 15 ]] => [3, "hola", 14.5, 1, 2, [6, 15]] >> a [ 2 ] => 14.5 >> a . [] ( 2 ) => 14.5 >> a . reverse => [[6, 15], 2, 1, 14.5, "hola", 3] >> a . flatten . rotate => ['hola', 14.5, 1, 2, 6, 15, 3]Construcción y uso de un array asociativo (en Ruby, llamado hash ):
>> hash = Hash.new # equivalente a hash = {} >> hash = { water : 'wet' , fire : 'hot' } # hace que la línea anterior sea redundante ya que ahora estamos >> # asignando hash a un nuevo objeto hash separado >> puts hash [ :fire ] hot => nil >> ?> hash.each_pair do | key , value | # o: hash.each do |key, value| ? > puts " #{ key } is #{ value } " >> end water is wet fire is hot => {water: "wet", fire: "hot"} >> >> hash.delete : water # elimina el par :water => 'wet' = > "wet" >> hash.delete_if { | key , value | value == 'hot' } # elimina el par :fire => 'hot' = > {}Estructuras de control
Si la instrucción:
# Genera un número aleatorio e imprime si es par o impar. if rand ( 100 ) .even ? puts "Es par" else puts "Es impar" endBloques e iteradores
Las dos sintaxis para crear un bloque de código:
{ puts '¡Hola, mundo!' } # fíjate en las llaves # o: haz puts '¡Hola, mundo!' endUn bloque de código se puede pasar a un método como argumento de bloque opcional. Muchos métodos integrados tienen este tipo de argumentos:
Archivo.open ( 'file.txt' , 'w' ) do | archivo | # 'w' indica "modo escritura" archivo.puts ' Se escribió algo de texto.' end # El archivo se cierra automáticamente aquíArchivo . leer líneas ( 'archivo.txt' ) . cada hacer | línea | poner línea fin # => Escribió algo de texto.Pasar parámetros a un bloque para que sea un cierre :
>> # En una variable de instancia de objeto (denotada con '@'), recuerda un bloque. ?> def remember ( & a_block ) ?> @block = a_block >> end >> => :remember >> >> # Invoca el método anterior, pasándole un bloque que toma un nombre. >> remember { | name | puts "Hola, #{ name } !" } => #<Proc:0x00007ff0b9823460 (irb):8> >> >> # Llama al cierre (ten en cuenta que esto no cierra ninguna variable libre): >> @block . call ( 'Jon' ) Hola, Jon! => nilCreación de una función anónima :
proc { | arg | puts arg } Proc . new { | arg | puts arg } lambda { | arg | puts arg } -> ( arg ) { puts arg } # introducido en Ruby 1.9Devolución de cierres desde un método:
?> def create_set_and_get ( initial_value = 0 ) # tenga en cuenta el valor predeterminado de 0 ?> closure_value = initial_value ?> [ Proc . new { | x | closure_value = x }, Proc . new { closure_value } ] >> end >> >> setter , getter = create_set_and_get # devuelve dos valores >> setter . call ( 21 ) >> getter . call => 21# Las variables de parámetros también se pueden usar como enlace para el cierre, # por lo que lo anterior se puede reescribir como:def create_set_and_get ( closure_value = 0 ) [ proc { | x | closure_value = x } , proc { closure_value } ] endCeder el control del programa a un bloque que se proporcionó en el momento de la llamada:
?> def use_hello ?> yield "hello" >> end >> >> # Invoca el método anterior, pasándole un bloque. >> use_hello { | string | puts string } hello => nilIterar sobre enumeraciones y matrices usando bloques:
>> array = [ 1 , 'hola' , 3 . 14 ] >> array . each { | item | puts item } 1 hola 3.14 => [1, "hola", 3.14] >> >> array . each_index { | index | puts " #{ index } : #{ array [ index ] } " } 0: 1 1: hola 2: 3.14 => [1, "hola", 3.14] >> >> # Lo siguiente utiliza un rango (a..b) >> ( 3 .. 6 ) . each { | num | puts num } 3 4 5 6Un método como este injectpuede aceptar tanto un parámetro como un bloque. El injectmétodo itera sobre cada elemento de una lista, realizando alguna función sobre él mientras conserva un valor agregado. Esto es análogo a la foldlfunción en los lenguajes de programación funcional . Por ejemplo:
>> [ 1 , 3 , 5 ]. inject ( 10 ) { | suma , elemento | suma + elemento } => 19En la primera pasada, el bloque recibe 10 (el argumento a inyectar) como sum, y 1 (el primer elemento del array) como element. Esto devuelve 11, que luego se convierte sumen en la siguiente pasada. Se suma a 3 para obtener 14, que luego se suma a 5 en la tercera pasada, para finalmente devolver 19.
Utilizando una enumeración y un bloque para elevar al cuadrado los números del 1 al 10 (usando un rango ):
>> ( 1 .. 10 ) . collect { | x | x * x } => [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]O bien, invocar un método en cada elemento ( mapes sinónimo de collect):
>> ( 1 .. 5 ) . map ( & :to_f ) => [1.0, 2.0, 3.0, 4.0, 5.0]Clases
El siguiente código define una clase llamada Person. Además de initialize, el constructor habitual para crear nuevos objetos, tiene dos métodos: uno para sobrescribir el <=>operador de comparación (para Array#sortpoder ordenar por edad) y el otro para sobrescribir el to_smétodo (para Kernel#putspoder formatear su salida). Aquí, attr_readeres un ejemplo de metaprogramación en Ruby: attr_accessordefine métodos getter y setter de variables de instancia, pero attr_readersolo métodos getter. La última instrucción evaluada en un método es su valor de retorno, lo que permite la omisión de una returninstrucción explícita.
clase Persona attr_reader :nombre , :edad def inicializar ( nombre , edad ) @nombre , @edad = nombre , edad end def <=> ( persona ) # el operador de comparación para ordenar @edad <=> persona . edad end def to_s " #{ @nombre } ( #{ @edad } )" end endgrupo = [ Persona . nuevo ( "Bob" , 33 ), Persona . nuevo ( "Chris" , 16 ), Persona . nuevo ( "Ash" , 23 ) ]pone grupo . ordenar . invertirEl código anterior imprime tres nombres en orden cronológico inverso:
Bob (33) Ceniza (23) Chris (16) Persones una constante y es una referencia a un Classobjeto.
Clases abiertas
En Ruby, las clases nunca se cierran: siempre se pueden añadir métodos a una clase existente. Esto se aplica a todas las clases, incluidas las clases estándar integradas. Basta con abrir la definición de una clase existente y el nuevo contenido especificado se añadirá al contenido existente. Un ejemplo sencillo de cómo añadir un nuevo método a una Timeclase de la biblioteca estándar:
>> # reabrir la clase Time de Ruby ?> class Time ?> def yesterday ?> self - 86400 ?> end >> end => :yesterday >> >> today = Time . now => 2025-08-20 10:34:33.595383429 +0900 >> yesterday = today . yesterday => 2025-08-19 10:34:33.595383429 +0900Agregar métodos a clases previamente definidas se conoce comúnmente como "monkey-patching" . Si se realiza de forma imprudente, esta práctica puede provocar conflictos de comportamiento con resultados inesperados y problemas de escalabilidad del código.
Desde Ruby 2.0 ha sido posible utilizar refinamientos para reducir las consecuencias potencialmente negativas del monkey patching, limitando el alcance del parche a áreas particulares del código base.
# reabrir el módulo de clase Time de Ruby RelativeTimeExtensions refine Time do def half_a_day_ago self - 43200 end end endMódulo MyModule Clase MyClass # Permite que se utilice el refinamiento mediante RelativeTimeExtensionsdef ventana Tiempo . ahora . hace_medio_día fin fin finExcepciones
Se produce una excepción con una raisellamada:
aumentarSe puede añadir un mensaje opcional a la excepción:
levantar "Este es un mensaje"El programador también puede especificar excepciones:
Generar ArgumentError , "¡Argumentos ilegales!"Alternativamente, se puede pasar una instancia de excepción al raisemétodo:
generar ArgumentError.new ( " ¡ Argumentos ilegales!" )Esta última construcción resulta útil al generar una instancia de una clase de excepción personalizada que incluye un constructor que acepta más de un argumento:
clase ParseError < Exception def initialize ( input , line , pos ) super "No se pudo analizar ' #{ input } ' en la línea #{ line } , posición #{ pos } " end endgenerar ParseError.new ( " Foo " , 3 , 9 )Las excepciones se manejan mediante la rescuecláusula. Dicha cláusula puede capturar excepciones que heredan de StandardError. Otras palabras clave de control de flujo que se pueden usar al manejar excepciones son elsey ensure:
begin # hacer algo rescue # manejar excepción else # hacer esto si no se produjo ninguna excepción ensure # hacer esto independientemente de si se produjo una excepción endEs un error común intentar capturar todas las excepciones con una simple cláusula de rescate. Para capturar todas las excepciones se debe escribir:
begin # hacer algo rescue Exception # Código de manejo de excepciones aquí. # No escriba solo "rescue"; eso solo captura StandardError, una subclase de Exception. endO capturar excepciones específicas:
inicio # hacer algo rescate RuntimeError # manejar solo RuntimeError y sus subclases finTambién es posible especificar que el objeto de excepción esté disponible para la cláusula del manejador:
begin # hacer algo rescue RuntimeError => e # manejo, posiblemente involucrando e, como "puts e.to_s" endAlternativamente, la excepción más reciente se almacena en la variable global mágica $!.
También se pueden detectar varias excepciones:
comenzar # hacer algo rescate RuntimeError , Timeout :: Error => e # manejo, posiblemente involucrando e finMetaprogramación
El código Ruby puede modificar programáticamente, en tiempo de ejecución , aspectos de su propia estructura que serían fijos en lenguajes más rígidos, como las definiciones de clases y métodos. Este tipo de metaprogramación se puede utilizar para escribir código más conciso y extender el lenguaje de manera efectiva.
Por ejemplo, el siguiente código Ruby genera nuevos métodos para la clase integrada String, a partir de una lista de colores. Estos métodos envuelven el contenido de la cadena con una etiqueta HTML con el color correspondiente.
COLORES = { negro : "000" , rojo : "f00" , verde : "0f0" , amarillo : "ff0" , azul : "00f" , magenta : "f0f" , cian : "0ff" , blanco : "fff" }clase String COLORS.each do | color , code | define_method "in_ #{ color } " do " <span style= \" color: # #{ code } \" > # { self } </span>" end end endLos métodos generados podrían utilizarse de la siguiente manera:
>> "¡Hola, mundo!" . in_blue => "<span style=\"color: #00f\">¡Hola, mundo!</span>"Para implementar el equivalente en muchos otros lenguajes, el programador tendría que escribir cada método ( in_black, in_red, in_green, etc.) por separado.
Otros posibles usos de la metaprogramación en Ruby incluyen:
- interceptar y modificar llamadas a métodos
- Implementación de nuevos modelos de herencia
- Generación dinámica de clases a partir de parámetros
- serialización automática de objetos
- Ayuda interactiva y depuración
Referencias
- ↑ " [ ruby-talk:01120 ] Re: El valor de while..."
En la sintaxis de Ruby, statement es solo un caso especial de una expresión que no puede aparecer como argumento (por ejemplo, asignación múltiple).
- ↑ " [ ruby-talk:02460 ] Re: Pregunta sobre precedencia" .
La instrucción [...] no puede formar parte de una expresión a menos que esté agrupada entre paréntesis.
- sintaxis del lenguaje de programación