¿Qué significa el siguiente código en Ruby?
||=
¿Tiene algún significado o razón para la sintaxis?
¿Qué significa el siguiente código en Ruby?
||=
¿Tiene algún significado o razón para la sintaxis?
Respuestas:
Esta pregunta se ha discutido con tanta frecuencia en las listas de correo de Ruby y en los blogs de Ruby que ahora hay incluso hilos en la lista de correo de Ruby cuyo único propósito es recopilar enlaces a todos los otros hilos en la lista de correo de Ruby que discuten este tema .
Aquí hay uno: la lista definitiva de hilos y páginas || = (O igual)
Si realmente quiere saber lo que está sucediendo, eche un vistazo a la Sección 11.4.2.3 "Asignaciones abreviadas" de la Especificación del borrador del lenguaje Ruby .
Como primera aproximación,
a ||= b
es equivalente a
a || a = b
y no equivalente a
a = a || b
Sin embargo, esa es solo una primera aproximación, especialmente si ano está definida. La semántica también difiere dependiendo de si es una asignación de variable simple, una asignación de método o una asignación de indexación:
a ||= b
a.c ||= b
a[c] ||= b
Todos son tratados de manera diferente.
a = false; a ||= trueno , no hacer lo que su respuesta dice que lo hace un "matiz".
a ||= bes un operador de asignación condicional . Significa que si ano está definido o es falso , evalúe by establezca ael resultado . De manera equivalente, si ase define y se evalúa como verdadero, bno se evalúa y no se realiza ninguna asignación. Por ejemplo:
a ||= nil # => nil
a ||= 0 # => 0
a ||= 2 # => 0
foo = false # => false
foo ||= true # => true
foo ||= false # => true
Confusamente, se parece a otros operadores de asignación (como +=), pero se comporta de manera diferente.
a += b se traduce en a = a + ba ||= b aproximadamente se traduce en a || a = bEs una taquigrafía casi para a || a = b. La diferencia es que, cuando ano está definido, a || a = baumentaría NameError, mientras que se a ||= bestablece aen b. Esta distinción no es importante si son variables locales ay bambas, pero es significativa si se trata de un método getter / setter de una clase.
Otras lecturas:
h = Hash.new(0); h[1] ||= 2. Consideremos ahora las dos posibles expansiones h[1] = h[1] || 2vs h[1] || h[1] = 2. Ambas expresiones evalúan 0pero la primera aumenta innecesariamente el tamaño del hash. Quizás es por eso que Matz decidió hacer que se ||=comportara más como la segunda expansión. (
a || a = bplantea un NameErrorsi ano está definido. a ||= bno lo hace, sino que se inicializa ay lo establece en b. Esa es la única distinción entre los dos, que yo sepa. Del mismo modo, la única diferencia entre a = a || by de la a ||= bque tengo conocimiento es que si se a=trata de un método, se llamará independientemente de lo que aregrese. Además, la única diferencia entre a = b unless ay a ||= bque conozco es que esa declaración se evalúa en nillugar de asi aes verdadera. Muchas aproximaciones, pero nada equivalente ...
a ||= b
evalúa de la misma manera que cada una de las siguientes líneas
a || a = b
a ? a : a = b
if a then a else a = b end
-
Por otra parte,
a = a || b
evalúa de la misma manera que cada una de las siguientes líneas
a = a ? a : b
if a then a = a else a = b end
-
Editar: como AJedi32 señaló en los comentarios, esto solo es cierto si: 1. a es una variable definida. 2. Evaluar una vez y dos veces no da como resultado una diferencia en el estado del programa o sistema.
aes falso / cero / indefinido, se evalúa dos veces. (Pero no conozco a Ruby, así que no sé si los valores pueden ser 'evaluados' exactamente ...)
a || a = b, a ? a : a = b, if a then a else a = b end, Y if a then a = a else a = b endgenerará un error si ano está definido, mientras que a ||= by a = a || blo hará no. También, a || a = b, a ? a : a = b, if a then a else a = b end, a = a ? a : b, y if a then a = a else a = b endevaluar ados veces cuando aes Truthy, mientras que a ||= by a = a || bno lo hacen.
a || a = bno se evaluará ados veces cuando asea cierto.
the end state will be equivalent after the whole line has been evaluatedSin embargo, eso no es necesariamente cierto. ¿Qué pasa si aes un método? Los métodos pueden tener efectos secundarios. Por ejemplo public; def a=n; @a=n; end; def a; @a+=1; end; self.a = 5, con , self.a ||= bdevolverá 6, pero self.a ? self.a : self.a = bdevolverá 7.
Significa o-es igual a. Comprueba si el valor de la izquierda está definido, luego úsalo. Si no es así, use el valor de la derecha. Puede usarlo en Rails para almacenar en caché variables de instancia en modelos.
Un ejemplo rápido basado en Rails, donde creamos una función para recuperar el usuario actualmente conectado:
class User > ActiveRecord::Base
def current_user
@current_user ||= User.find_by_id(session[:user_id])
end
end
Comprueba si la variable de instancia @current_user está configurada. Si es así, lo devolverá, guardando así una llamada a la base de datos. Sin embargo, si no está configurado, hacemos la llamada y luego configuramos la variable @current_user para eso. Es una técnica de almacenamiento en caché realmente simple, pero es excelente cuando se busca la misma variable de instancia en la aplicación varias veces.
undefined, sino que también se activa falsey nil, lo que puede no ser relevante current_user, pero especialmente falsepuede ser inesperado en otros casos
x ||= y
es
x || x = y
"si x es falso o indefinido, entonces x apunta a y"
Para ser precisos, a ||= bsignifica "si ano está definido o es falso ( falseo nil), se establece aen by se evalúa como (es decir, retorno) b, de lo contrario se evalúa como a".
Otros a menudo intentan ilustrar esto diciendo que a ||= bes equivalente a a || a = bo a = a || b. Estas equivalencias pueden ser útiles para la comprensión del concepto, pero tenga en cuenta que son no precisa en todas las condiciones. Permítame explicarle:
a ||= b⇔a || a = b ?
El comportamiento de estas declaraciones difiere cuando aes una variable local indefinida. En ese caso, a ||= bse establecerá aen b(y se evaluará en b), mientras a || a = bque aumentará NameError: undefined local variable or method 'a' for main:Object.
a ||= b⇔a = a || b ?
La equivalencia de estos estados se asume a menudo, ya que una equivalencia similares es cierto para otros asignación abreviado operadores (es decir +=, -=, *=, /=, %=, **=, &=, |=, ^=, <<=, y >>=). Sin embargo, ||=el comportamiento de estas declaraciones puede diferir cuando a=es un método sobre un objeto y aes verdadero. En ese caso, a ||= bno hará nada (excepto evaluar a a), mientras a = a || bque llamará a=(a)al areceptor. Como otros han señalado, esto puede marcar la diferencia cuando las llamadas a=atienen efectos secundarios, como agregar claves a un hash.
a ||= b⇔a = b unless a ??
El comportamiento de estas declaraciones difiere solo en lo que evalúan cuando aes verdadero. En ese caso, a = b unless aevaluará a nil(aunque atodavía no se establecerá, como se esperaba), mientras a ||= bque evaluará a a.
a ||= b⇔defined?(a) ? (a || a = b) : (a = b) ????
Aún no. Estas declaraciones pueden diferir cuando method_missingexiste un método que devuelve un valor verdadero para a. En este caso, a ||= bse evaluará a cualquier method_missingdevoluciones, y no tratar de conjunto a, mientras que defined?(a) ? (a || a = b) : (a = b)establecerá aa by evaluar a b.
Vale, vale, así que lo que es a ||= b equivalente a? ¿Hay alguna manera de expresar esto en Ruby?
Bueno, suponiendo que no estoy pasando por alto nada, creo que a ||= bes funcionalmente equivalente a ... ( redoble de tambores )
begin
a = nil if false
a || a = b
end
¡Espere! ¿No es ese el primer ejemplo con un noop antes? Bueno, no del todo. ¿Recuerdas cómo dije antes que eso a ||= bno es equivalente a a || a = bcuándo aes una variable local indefinida? Bueno, a = nil if falseasegura que anunca esté indefinido, aunque esa línea nunca se ejecute. Las variables locales en Ruby tienen un alcance léxico.
(a=b unless a) or a
aes un método, se llamará dos veces en lugar de una vez (si devuelve un valor verdadero la primera vez). Eso podría causar que los comportamientos difieran si, por ejemplo, atarda mucho tiempo en regresar o si tiene efectos secundarios.
baa , no sigue asignando la rhs a la lhs, o en otras palabras, la lhs todavía no establece su valor en la rhs?
a ||= brespuesta que he encontrado en Internet. Gracias.
Supongamos a = 2yb = 3
ENTONCES, se a ||= b dará como resultado ael valor de ie 2.
Como cuando se evalúa a un valor que no resultó falseo nil... Es por eso que llno evalúa bel valor.
Ahora suponga a = nily b = 3.
Entonces se a ||= bdará como resultado el valor de 3ie b.
Como primero trata de evaluar el valor de a que resultó en nil... así evaluó bel valor de.
El mejor ejemplo utilizado en la aplicación ror es:
#To get currently logged in iser
def current_user
@current_user ||= User.find_by_id(session[:user_id])
end
# Make current_user available in templates as a helper
helper_method :current_user
Donde, User.find_by_id(session[:user_id])se dispara si y solo si @current_userno se inicializa antes.
a || = b
Significa si algún valor está presente en 'a' y no desea modificarlo, siga usando ese valor, de lo contrario, si 'a' no tiene ningún valor, use el valor de 'b'.
Palabras simples, si el lado izquierdo si no es nulo, apuntan al valor existente, de lo contrario apuntan al valor en el lado derecho.
a ||= b
es equivalente a
a || a = b
y no
a = a || b
debido a la situación en la que define un hash con un valor predeterminado (el hash devolverá el valor predeterminado para cualquier clave indefinida)
a = Hash.new(true) #Which is: {}
si utiliza:
a[10] ||= 10 #same as a[10] || a[10] = 10
a es todavía:
{}
pero cuando lo escribes así:
a[10] = a[10] || 10
a se convierte en:
{10 => true}
porque ha asignado el valor de sí mismo en la clave 10, que por defecto es verdadero, por lo que ahora el hash está definido para la clave 10, en lugar de nunca realizar la asignación en primer lugar.
Recuerde también que ||=no es una operación atómica y, por lo tanto, no es segura para subprocesos. Como regla general, no lo use para métodos de clase.
Esta es la notación de asignación predeterminada
por ejemplo: x || = 1
esto verificará si x es nulo o no. Si x es nulo, entonces le asignará ese nuevo valor (1 en nuestro ejemplo)
más explícito:
si x == nil
x = 1
final
nilo falseno sólonil
Si XNO tiene un valor, se le asignará el valor de Y. De lo contrario, conservará su valor original, 5 en este ejemplo:
irb(main):020:0> x = 5
=> 5
irb(main):021:0> y = 10
=> 10
irb(main):022:0> x ||= y
=> 5
# Now set x to nil.
irb(main):025:0> x = nil
=> nil
irb(main):026:0> x ||= y
=> 10
||= asigna valor a la derecha solo si izquierda == nulo (o no está definido o es falso).
Esta sintaxis de ruby-lang. La respuesta correcta es verificar la documentación de ruby-lang. Todas las demás explicaciones se ofuscan .
"Ruby-lang docs Asignación abreviada".
https://docs.ruby-lang.org/en/2.4.0/syntax/assignment_rdoc.html#label-Abbreviated+Assignment
b = 5
a ||= b
Esto se traduce en:
a = a || b
Cuál podría ser
a = nil || 5
así que finalmente
a = 5
Ahora, si vuelves a llamar a esto:
a ||= b
a = a || b
a = 5 || 5
a = 5
b = 6
Ahora, si vuelves a llamar a esto:
a ||= b
a = a || b
a = 5 || 6
a = 5
Si observa, el bvalor no se asignará a a. aaún lo tendré 5.
Es un patrón de memorización que se está utilizando en Ruby para acelerar los accesores.
def users
@users ||= User.all
end
Esto básicamente se traduce en:
@users = @users || User.all
Entonces hará una llamada a la base de datos por primera vez que llame a este método.
Las futuras llamadas a este método solo devolverán el valor de la @usersvariable de instancia.
||= se llama operador de asignación condicional.
Básicamente funciona como, =pero con la excepción de que si una variable ya ha sido asignada, no hará nada.
Primer ejemplo:
x ||= 10
Segundo ejemplo
x = 20
x ||= 10
En el primer ejemplo xahora es igual a 10. Sin embargo, en el segundo ejemplo xya está definido como 20. Por lo tanto, el operador condicional no tiene ningún efecto. xsigue siendo 20 después de correr x ||= 10.
a ||= bes lo mismo que decir a = b if a.nil?oa = b unless a
¿Pero las 3 opciones muestran el mismo rendimiento? Con Ruby 2.5.1 esto
1000000.times do
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
end
toma 0.099 segundos en mi PC, mientras
1000000.times do
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
end
toma 0.062 segundos. Eso es casi un 40% más rápido.
y luego también tenemos:
1000000.times do
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
end
que toma 0.166 segundos.
No es que esto tenga un impacto significativo en el rendimiento en general, pero si necesita ese último bit de optimización, considere este resultado. Por cierto: a = 1 unless aes más fácil de leer para el novato, se explica por sí mismo.
Nota 1: la razón para repetir la línea de asignación varias veces es reducir la sobrecarga del bucle en el tiempo medido.
Nota 2: Los resultados son similares si hago a=nilcero antes de cada tarea.