R: Todas las variables y constantes referencian (apuntan) a algún objeto. (La excepción son las variables sin inicializar, que no referencian a nada. Si se usan generan una excepción NameError). Al realizar una asignación a una variable, o inicializar una constante, se define el objeto al que la variable o constante referencia.
Por lo tanto la asignación por si misma no genera una nueva copia del objeto.
Existe una pequeña diferencia en algunos casos especiales. Instancias de las clases Fixnum, NilClass, TrueClass y FalseClass se guardan directamente en variables o constantes - no hay referencias involucradas. Una variable que contiene el numero 42 o la constante true, contiene ese valor y no una referencia. Por lo tanto la asignación produce una copia física de los objetos de estos tipos. Se tratará con detalle en la sección Objetos Inmediatos y Referencias.
R: Se introduce un nuevo ámbito para una variable local en (1) el nivel superior (main), (2) una clase (o módulo) o (3) la definición de un método.
i = 1 # (1) Class Demo i = 2 # (2) def meth i = 3 # (3) print "En el método, i = ", i, "\n" end print "En la clase, i = ", i, "\n" end print "Al nivel superior, i = ", i, "\n" Demo.new.meth |
En la clase, i = 2 Al nivel superior, i = 1 En el método, i = 3 |
Un bloque ("{" .. "}" o do ... end) casí introduce un nuevo ámbito ;-). Las variables locales creadas dentro de un bloque no son accesibles fuera de él. Sin embargo, si una variable local dentro del bloque tiene el mismo nombre que una variable local que existe en el ámbito del llamante, entonces no se crea una nueva, y se puede acceder posteriormente a esa variable fuera del bloque.
a = 0 1.upto(3) do |i| a += i b = i*i end a # -> 6 # b no está definida aquí. |
Esto es importante cuando se crean hilo de ejecución. Cada hilo recibe su propia copia de variables locales al bloque del hilo.
threads = [] for name in ['one', 'two'] do threads << Thread.new { localName = name a = 0 3.times do |i| Thread.pass a += i print localName, ": ", a, "\n" end } end threads.each { |t| t.join } |
onetwo: : 00 onetwo: : 11 onetwo: : 33 |
R: Realmente la pregunta habría que transformarla en: "¿En qué punto decide Ruby que algo es una variable?". El problema surge porque una expresión simple como "a" podría ser una variable o la llamada a un método sin parámetros. Para tomar la decición, Ruby examina las sentencias de asignación. Si en algún punto del código fuente anterior al uso de "a" existe una asignación a ella, entonces "a" es una variable, sino se la trata como un método. Una sitiación patológica de este caso se puede ver en el siguiente fragmento de código, presentado por Clemens Hintze.
def a print "Función 'a' llamada\n" 99 end for i in 1..2 if i == 2 print "a=", a, "\n" else a = 1 print "a=", a, "\n" end end |
a=1 Función 'a' llamada a=99 |
Durante el análisis del código, Ruby examina el uso de "a" en la primera sentencia print y como no tiene ninguna asignación previa supone que se trata de un método y llama al método a. Posteriormente se encuentra con la segunda sentencia print, pero como previamente a encontrado una asignación trata "a" como una variable.
Obsérvese que la asignación no tiene porque ejecutarse - Ruby sólo tiene que verla. El siguiente programa no genera un error.
a = 1 if false; a # -> nil |
R: Una constante definida en una clase/módulo puede accederse directamente en la definición de esa clase o módulo.
Se pueden acceder directamente a las constantes en clases o módulos externos en clases y módulos anidados.
También se puede acceder directamente a las constantes de las superclases y de los modulos que se incluyen.
Aparte de estos casos, se pueden acceder a las constantes de las clases y módulos utilizando el operador :: - NOMBREDEMODULO::CONSTANTE o NOMBRESDECLASE::CONSTANTE.
R: Los argumentos reales se asignan al argumento formal al invocar al método. (Ver asignaciones (seccion 4.1) donde se describe la semçántica de la asignación).
def addOne(n) n += 1 end a = 1 addOne(a) # -> 2 a # -> 1 |
Si se pasan referencias a objetos, es posible que el método modifique el contenido del objeto mutable pasado.
def downer(string) string.downcase! end a = "HOLA" # -> "HOLA" downer(a) # -> "hola" a # -> "hola" |
R: El argumento formal es una variable local. Dentro de un método, la asignación a un argumento formal simplemente hace que el argumento referencie a otro objeto.
R: Todas las variables de Ruby (incluídos los argumentos de un método) actúan como referencias a objetos. Se pueden llamar a los métodos de estos objetospara obtener o modificar su estado y conseguir que el objeto realice algo. Se puede lograr esto con los objetos pasados a los métodos. Hay que ser muy cuidadoso, todo esta clase de efectos colaterales hace muy difícil de seguir el programa.
R: Al utilizarlo como parte de los argumentos formales, un asterisco permite pasar un número arbitrario de argumentos a un método recogiendose en un array y asignando ese array al parámetro con el asterisco.
def foo(prefix, *all) for e in all print prefix, e , " " end end for("val=", 1, 2, 3) |
val = 1 val = 2 val = 3 |
a = [1, 2, 3] foo(*a) |
A la izquierda de una asignación múltiple.
A la derecha de una asignación múltiple.
A la definicón de los argumentos formales de un método.
A los argumentos reales en la llamada a un método.
En la claúsula when de una sentencia case.
x, *y = [7, 8, 9] x # -> 7 y # -> [8, 9] x, = [7, 8, 9] x # -> 7 x = [7, 8, 9] x # -> [7, 8, 9] |
R: Si el último argumento formal de un método está precedido por un ampersand, un bloque que siga a la llamada al método se convertirá en un objeto de tipo Proc y se asignará al parámetro formal.
Si el último parámetro formal en la llamada a un método es un objeto de tipo Proc, se puede preceder el nombre con un ampersand para convertirlo en un bloque. A continuación el método puede utilizar yield para ejecutarlo.
square = proc {|i| i*i} def meth1 (&b) print b.call(9), "\n" end; meth1 {|i| i + i} def meth2 print yield(8), "\n" end meth2 {|i| i+1} meth2 &square |
18 16 64 |
R:
def greet(p1="hello",p2="world") print "#{p1} #{p2}\n" end greet greet "hi" greet "morning","mom" |
hello world hi world morning mom |
R: Los parámetros formales de un bloque aparecen entre barras vericales al comienzo del mismo.
proc {|a, b| a <==> b} |
Normalmente, los argumentos se pasan al bloque utilizado yield (o un iterador que llama a yield) o utilizando el método Proc.call.
R:
A = a = b = "abc" b.concat("d") # -> "abcd" a # -> "abcd" A # -> "abcd" |
Al llamar a b.concat("d"), se invoca al método concat de ese objeto, que modifica "abc" a "abcd". Ya que a y A referencian al mismo objeto, sus aparentes valores también han cambiado.
Esto no es un problema en la práctica como puede parecer.
A partir de Ruby 1.5.2 todos los objetos se pueden congelar, protegiéndolos del cambio.
R: Una constante es una variable cuyo nombre empieza por mayúsculas. En implementaciones antiguas de Ruby, si se asignaba un valor nuevo a una constante, se generaba un aviso. En versiones más modernas, las constantes no se pueden cambiar dentro de los métodos de instancia, pero sí desde cualquier otro lugar.