¿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 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 siano está definido o es falso , evalúeby establezcaael resultado . De manera equivalente, siase define y se evalúa como verdadero,bno 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 += bse traduce ena = a + ba ||= baproximadamente se traduce ena || a = bEs una taquigrafía casi para
a || a = b. La diferencia es que, cuandoano está definido,a || a = baumentaríaNameError, mientras que sea ||= bestableceaenb. Esta distinción no es importante si son variables localesaybambas, 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] || 2vsh[1] || h[1] = 2. Ambas expresiones evalúan0pero 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 unNameErrorsiano está definido.a ||= bno lo hace, sino que se inicializaay lo establece enb. Esa es la única distinción entre los dos, que yo sepa. Del mismo modo, la única diferencia entrea = a || by de laa ||= bque tengo conocimiento es que si sea=trata de un método, se llamará independientemente de lo quearegrese. Además, la única diferencia entrea = b unless aya ||= bque conozco es que esa declaración se evalúa ennillugar deasiaes 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
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, Yif a then a = a else a = b endgenerará un error siano está definido, mientras quea ||= bya = 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, yif a then a = a else a = b endevaluarados veces cuandoaes Truthy, mientras quea ||= bya = a || bno lo hacen.a || a = bno se evaluaráados veces cuandoasea cierto.the end state will be equivalent after the whole line has been evaluatedSin embargo, eso no es necesariamente cierto. ¿Qué pasa siaes 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 ||= bdevolverá 6, peroself.a ? self.a : self.a = bdevolverá 7.En resumen,
a||=bsignifica: siaes asíundefined, nil or false, asignarbaa. De lo contrario, manténgaloaintacto.fuente
x ||= ymediosi
xtiene algún valor, déjelo solo y no cambie el valor; de lo contrario, configúreloxenyfuente
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 activafalseynil, lo que puede no ser relevantecurrent_user, pero especialmentefalsepuede ser inesperado en otros casoses
"si x es falso o indefinido, entonces x apunta a y"
fuente
Para ser precisos,
a ||= bsignifica "siano está definido o es falso (falseonil), se estableceaenby se evalúa como (es decir, retorno)b, de lo contrario se evalúa comoa".Otros a menudo intentan ilustrar esto diciendo que
a ||= bes equivalente aa || a = boa = 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áaenb(y se evaluará enb), mientrasa || 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 cuandoa=es un método sobre un objeto yaes verdadero. En ese caso,a ||= bno hará nada (excepto evaluar aa), mientrasa = a || bque llamaráa=(a)alareceptor. Como otros han señalado, esto puede marcar la diferencia cuando las llamadasa=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á anil(aunqueatodavía no se establecerá, como se esperaba), mientrasa ||= bque evaluará aa.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 paraa. En este caso,a ||= bse evaluará a cualquiermethod_missingdevoluciones, y no tratar de conjuntoa, mientras quedefined?(a) ? (a || a = b) : (a = b)estableceráaaby evaluar ab.Vale, vale, así que lo que es
a ||= bequivalente 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 )¡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 aa || a = bcuándoaes una variable local indefinida? Bueno,a = nil if falseasegura queanunca 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 aaes 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.unless x x = y enda menos que x tenga un valor (no es nulo o falso), configúrelo igual a y
es equivalente a
x ||= yfuente
Supongamos
a = 2yb = 3ENTONCES, se
a ||= bdará como resultadoael valor de ie2.Como cuando se evalúa a un valor que no resultó
falseonil... Es por eso quellno evalúabel valor.Ahora suponga
a = nilyb = 3.Entonces se
a ||= bdará como resultado el valor de3ieb.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:
Donde,
User.find_by_id(session[:user_id])se dispara si y solo si@current_userno 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
nilofalseno sólonil|| = es un operador de asignación condicional
es equivalente a
o alternativamente
fuente
Si
XNO 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 ||= bno es equivalente aa = a || b, pero se comporta comoa || a = b.Pero aquí viene un caso complicado. Si
ano se define,a || a = 42aumentosNameError, mientras quea ||= 42los 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
aya estaba configurado en1Porque
aeranilfuente
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
bvalor no se asignará aa.aaú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
@usersvariable 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
xahora es igual a 10. Sin embargo, en el segundo ejemploxya está definido como 20. Por lo tanto, el operador condicional no tiene ningún efecto.xsigue siendo 20 después de correrx ||= 10.fuente
a ||= bes 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 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.fuente