¿Qué significa el siguiente código en Ruby?
||=
¿Tiene algún significado o razón para la sintaxis?
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 a
no 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 ||= true
no , no hacer lo que su respuesta dice que lo hace un "matiz".a ||= b
es un operador de asignación condicional . Significa que sia
no está definido o es falso , evalúeb
y establezcaa
el resultado . De manera equivalente, sia
se define y se evalúa como verdadero,b
no se evalúa y no se realiza ninguna asignación. Por ejemplo:Confusamente, se parece a otros operadores de asignación (como
+=
), pero se comporta de manera diferente.a += b
se traduce ena = a + b
a ||= b
aproximadamente se traduce ena || a = b
Es una taquigrafía casi para
a || a = b
. La diferencia es que, cuandoa
no está definido,a || a = b
aumentaríaNameError
, mientras que sea ||= b
establecea
enb
. Esta distinción no es importante si son variables localesa
yb
ambas, pero es significativa si se trata de un método getter / setter de una clase.Otras lecturas:
fuente
h = Hash.new(0); h[1] ||= 2
. Consideremos ahora las dos posibles expansionesh[1] = h[1] || 2
vsh[1] || h[1] = 2
. Ambas expresiones evalúan0
pero 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 = b
plantea unNameError
sia
no está definido.a ||= b
no lo hace, sino que se inicializaa
y lo establece enb
. Esa es la única distinción entre los dos, que yo sepa. Del mismo modo, la única diferencia entrea = a || b
y de laa ||= b
que tengo conocimiento es que si sea=
trata de un método, se llamará independientemente de lo quea
regrese. Además, la única diferencia entrea = b unless a
ya ||= b
que conozco es que esa declaración se evalúa ennil
lugar dea
sia
es verdadera. Muchas aproximaciones, pero nada equivalente ...Respuesta concisa y completa
evalúa de la misma manera que cada una de las siguientes líneas
-
Por otra parte,
evalúa de la misma manera que cada una de las siguientes líneas
-
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.
fuente
a
es 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
, Yif a then a = a else a = b end
generará un error sia
no está definido, mientras quea ||= b
ya = a || b
lo hará no. También,a || a = b
,a ? a : a = b
,if a then a else a = b end
,a = a ? a : b
, yif a then a = a else a = b end
evaluara
dos veces cuandoa
es Truthy, mientras quea ||= b
ya = a || b
no lo hacen.a || a = b
no se evaluaráa
dos veces cuandoa
sea cierto.the end state will be equivalent after the whole line has been evaluated
Sin embargo, eso no es necesariamente cierto. ¿Qué pasa sia
es un método? Los métodos pueden tener efectos secundarios. Por ejemplopublic; def a=n; @a=n; end; def a; @a+=1; end; self.a = 5
, con ,self.a ||= b
devolverá 6, peroself.a ? self.a : self.a = b
devolverá 7.En resumen,
a||=b
significa: sia
es asíundefined, nil or false
, asignarb
aa
. De lo contrario, manténgaloa
intacto.fuente
x ||= y
mediosi
x
tiene algún valor, déjelo solo y no cambie el valor; de lo contrario, configúrelox
eny
fuente
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:
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.
fuente
undefined
, sino que también se activafalse
ynil
, lo que puede no ser relevantecurrent_user
, pero especialmentefalse
puede ser inesperado en otros casoses
"si x es falso o indefinido, entonces x apunta a y"
fuente
Para ser precisos,
a ||= b
significa "sia
no está definido o es falso (false
onil
), se establecea
enb
y se evalúa como (es decir, retorno)b
, de lo contrario se evalúa comoa
".Otros a menudo intentan ilustrar esto diciendo que
a ||= b
es equivalente aa || a = b
oa = 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
a
es una variable local indefinida. En ese caso,a ||= b
se estableceráa
enb
(y se evaluará enb
), mientrasa || a = b
que 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 cuandoa=
es un método sobre un objeto ya
es verdadero. En ese caso,a ||= b
no hará nada (excepto evaluar aa
), mientrasa = a || b
que llamaráa=(a)
ala
receptor. Como otros han señalado, esto puede marcar la diferencia cuando las llamadasa=a
tienen 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
a
es verdadero. En ese caso,a = b unless a
evaluará anil
(aunquea
todavía no se establecerá, como se esperaba), mientrasa ||= b
que evaluará aa
.a ||= b
⇔defined?(a) ? (a || a = b) : (a = b)
????Aún no. Estas declaraciones pueden diferir cuando
method_missing
existe un método que devuelve un valor verdadero paraa
. En este caso,a ||= b
se evaluará a cualquiermethod_missing
devoluciones, y no tratar de conjuntoa
, mientras quedefined?(a) ? (a || a = b) : (a = b)
estableceráa
ab
y evaluar ab
.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 ||= b
es funcionalmente equivalente a ... ( redoble de tambores )¡Espere! ¿No es ese el primer ejemplo con un noop antes? Bueno, no del todo. ¿Recuerdas cómo dije antes que eso
a ||= b
no es equivalente aa || a = b
cuándoa
es una variable local indefinida? Bueno,a = nil if false
asegura quea
nunca esté indefinido, aunque esa línea nunca se ejecute. Las variables locales en Ruby tienen un alcance léxico.fuente
(a=b unless a) or a
a
es 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,a
tarda mucho tiempo en regresar o si tiene efectos secundarios.b
aa
, no sigue asignando la rhs a la lhs, o en otras palabras, la lhs todavía no establece su valor en la rhs?a ||= b
respuesta que he encontrado en Internet. Gracias.unless x x = y end
a menos que x tenga un valor (no es nulo o falso), configúrelo igual a y
es equivalente a
x ||= y
fuente
Supongamos
a = 2
yb = 3
ENTONCES, se
a ||= b
dará como resultadoa
el valor de ie2
.Como cuando se evalúa a un valor que no resultó
false
onil
... Es por eso quell
no evalúab
el valor.Ahora suponga
a = nil
yb = 3
.Entonces se
a ||= b
dará como resultado el valor de3
ieb
.Como primero trata de evaluar el valor de a que resultó en
nil
... así evaluób
el valor de.El mejor ejemplo utilizado en la aplicación ror es:
Donde,
User.find_by_id(session[:user_id])
se dispara si y solo si@current_user
no se inicializa antes.fuente
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.
fuente
es equivalente a
y no
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)
si utiliza:
a es todavía:
pero cuando lo escribes así:
a se convierte en:
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 clave10
, en lugar de nunca realizar la asignación en primer lugar.fuente
Es como una instanciación perezosa. Si la variable ya está definida, tomará ese valor en lugar de crear el valor nuevamente.
fuente
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.fuente
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
fuente
nil
ofalse
no sólonil
|| = es un operador de asignación condicional
es equivalente a
o alternativamente
fuente
Si
X
NO tiene un valor, se le asignará el valor deY
. De lo contrario, conservará su valor original, 5 en este ejemplo:fuente
Como un error común,
a ||= b
no es equivalente aa = a || b
, pero se comporta comoa || a = b
.Pero aquí viene un caso complicado. Si
a
no se define,a || a = 42
aumentosNameError
, mientras quea ||= 42
los retornos42
. Por lo tanto, no parecen ser expresiones equivalentes.fuente
||=
asigna valor a la derecha solo si izquierda == nulo (o no está definido o es falso).fuente
Esta sintaxis de ruby-lang. La respuesta correcta es verificar la documentación de ruby-lang. Todas las demás explicaciones se ofuscan .
Google
"Ruby-lang docs Asignación abreviada".
Documentos de Ruby-Lang
https://docs.ruby-lang.org/en/2.4.0/syntax/assignment_rdoc.html#label-Abbreviated+Assignment
fuente
Porque
a
ya estaba configurado en1
Porque
a
eranil
fuente
Esto se traduce en:
Cuál podría ser
así que finalmente
Ahora, si vuelves a llamar a esto:
Ahora, si vuelves a llamar a esto:
Si observa, el
b
valor no se asignará aa
.a
aún lo tendré5
.Es un patrón de memorización que se está utilizando en Ruby para acelerar los accesores.
Esto básicamente se traduce en:
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
@users
variable de instancia.fuente
||=
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:
Segundo ejemplo
En el primer ejemplo
x
ahora es igual a 10. Sin embargo, en el segundo ejemplox
ya está definido como 20. Por lo tanto, el operador condicional no tiene ningún efecto.x
sigue siendo 20 después de correrx ||= 10
.fuente
a ||= b
es lo mismo que decira = b if a.nil?
oa = b unless a
¿Pero las 3 opciones muestran el mismo rendimiento? Con Ruby 2.5.1 esto
toma 0.099 segundos en mi PC, mientras
toma 0.062 segundos. Eso es casi un 40% más rápido.
y luego también tenemos:
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 a
es 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=nil
cero antes de cada tarea.fuente