operadores - ¿Qué significa ||=(o igual) en Ruby?
operadores logico ruby (20)
¿Qué significa el siguiente código en Ruby?
||=
¿Tiene algún significado o razón para la sintaxis?
Respuesta concisa y completa
a ||= b
Se 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
Se 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
-
Edición: como lo señaló AJedi32 en los comentarios, esto solo es válido si: 1. a es una variable definida. 2. La evaluación de una vez y dos veces no produce una diferencia en el estado del programa o del sistema.
En resumen, a||=b
significa: si a
undefined, nil or false
está undefined, nil or false
, asigne b
a a
. De lo contrario, mantenga intacto.
Es como una instanciación perezosa. Si la variable ya está definida, tomará ese valor en lugar de volver a crear el valor.
Esta es la notación de asignación predeterminada
por ejemplo: x || = 1
Esto verificará si x es nula o no. Si x es de hecho nulo, entonces le asignará ese nuevo valor (1 en nuestro ejemplo)
más explícito:
si x == nil
x = 1
fin
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 demás hilos de la lista de correo de Ruby que tratan este tema. .
Aquí hay uno: la lista definitiva de hilos y páginas || = (O Igual)
Si realmente quiere saber qué está pasando, eche un vistazo a la Sección 11.4.2.3 "Asignaciones abreviadas" de la Especificación de borrador de Ruby Language .
Como primera aproximación,
a ||= b
es equivalente a
a || a = b
y no equivalente a
a = a || b
Sin embargo, eso es solo una primera aproximación, especialmente si a
no está definido. La semántica también difiere dependiendo de si se trata de 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
Para ser precisos, a ||= b
significa "si a
no está definido o es false
( false
o nil
), establezca a en b
y evalúe a (es decir, devuelva) b
, de lo contrario evalúe a a
".
Otros a menudo intentan ilustrar esto diciendo que a ||= b
es equivalente a a || a = b
a || a = b
o a = a || b
a = a || b
. Estas equivalencias pueden ser útiles para comprender el concepto, pero tenga en cuenta que no son precisas en todas las condiciones. Permítame explicarle:
a ||= b
⇔a || a = b
a || a = b
?El comportamiento de estas declaraciones difiere cuando
a
es una variable local no definida. En ese caso,a ||= b
estableceráa
ab
(y evaluará ab
), mientras quea || a = b
a || a = b
generaráNameError: undefined local variable or method ''a'' for main:Object
.a ||= b
⇔a = a || b
a = a || b
?La equivalencia de estas declaraciones a menudo se asume, ya que una equivalencia similar es verdadera para otros operadores de asignación abreviados (es decir,
+=
,-=
*=
,/=
,%=
,**=
,&=
,|=
,^=
,<<=
, y>>=
). Sin embargo, para||=
el comportamiento de estas declaraciones puede diferir cuandoa=
es un método en un objeto ya
es verdadero. En ese caso,a ||= b
no hará nada (excepto evaluar aa
), mientras quea = a || b
a = a || b
llamaráa=(a)
en el receptor de a. Como han señalado others , esto puede marcar la diferencia cuando llamara=a
tiene efectos secundarios, como agregar claves a un hash.a ||= b
⇔a = b unless a
??El comportamiento de estas afirmaciones difiere solo en lo que evalúan cuando
a
es verdadero. En ese caso,a = b unless a
se evalúe comonil
(aunquea
todavía no se configurará, como se esperaba), mientras quea ||= b
se evaluará como a.a ||= b
⇔defined?(a) ? (a || a = b) : (a = b)
defined?(a) ? (a || a = b) : (a = b)
????Aún no. Estas declaraciones pueden diferir cuando existe un método
method_missing
que devuelve un valor verdadero paraa
. En este caso,a ||= b
evaluará a cualquier devolución demethod_missing
y falta de tiempo, y no intentará establecera
, mientras que sedefined?(a) ? (a || a = b) : (a = b)
defined?(a) ? (a || a = b) : (a = b)
estableceráa
ab
y evaluará ab
.
Está bien, está bien, entonces, ¿a qué es equivalente a a ||= b
? ¿Hay una manera de expresar esto en Ruby?
Bueno, asumiendo que no estoy pasando por alto nada, creo que a ||= b
es funcionalmente equivalente a ... ( drumroll )
begin
a = nil if false
a || a = b
end
¡Espere! ¿No es este el primer ejemplo con un noop antes? Bueno, no del todo. Recuerda cómo dije antes que a ||= b
no es equivalente a a || a = b
a || a = b
cuando a
es una variable local indefinida? Bueno, a = nil if false
garantiza que a
nunca esté indefinido, aunque esa línea nunca se ejecute. Las variables locales en Ruby tienen un alcance léxico.
Recuerde también que ||=
no es una operación atómica y, por lo tanto, no es seguro para subprocesos. Como regla de oro, no lo use para métodos de clase.
Significa or-es igual a. Verifica si el valor de la izquierda está definido, luego use eso. Si no lo es, usa el valor de la derecha. Puede usarlo en Rails para almacenar en caché las variables de instancia en los modelos.
Un ejemplo rápido basado en Rails, donde creamos una función para obtener el usuario que ha iniciado sesión actualmente:
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á establecida. Si lo es, lo devolverá, con lo que se guardará una llamada de 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 para cuando se está recuperando la misma variable de instancia en la aplicación varias veces.
Supongamos que a = 2
y b = 3
ENTONCES, a ||= b
se convertirá en a
valor de a, es decir 2
.
Como cuando a se evalúa a un valor que no es false
o nil
. Por eso no evaluará el valor de b
.
Supongamos ahora a = nil
y b = 3
.
Entonces a ||= b
resultará en 3
es decir, el valor de b
.
Como primero intenta evaluar el valor de a que resultó en nil
... así se evaluó el valor de b
.
El mejor ejemplo usado 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 @current_user
si y solo si @current_user
no se inicializó antes.
como un error común, a || = b no es equivalente a a = a || b, pero lo es, pero se comporta como un || a = b
Pero aquí viene un caso difícil.
Si a no está definido, a || a = 42 genera NameError, mientras que a || = 42 devuelve 42. Por lo tanto, no parecen ser expresiones equivalentes.
a ||= b
es lo mismo que decir a = b if a.nil?
o a = 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 que
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 lleva 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, entonces considere este resultado. Por cierto: a = 1 unless a
sea más fácil de leer para el principiante, 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
nil antes de cada asignación.
a ||= b
es un "operador de asignación condicional". Es a || a = b
abreviatura de tipo-pero-no-bastante (*) para a || a = b
a || a = b
.
Significa " si a
no está definido o falsey ( false
o nil
), entonces evalúe b
y establezca a al resultado ".
Por ejemplo:
> a ||= nil
=> nil
> a ||= 0;
=> 0
> a ||= 2;
=> 0
> foo = false;
=> false
> foo ||= true;
=> true
> foo ||= false;
=> true
La evaluación de cortocircuito de Ruby significa que si a
está definido y se evalúa a la verdad, entonces no se evalúa el lado derecho del operador y no se realiza ninguna asignación. Esta distinción no es importante si a
y b
son ambas variables locales, pero es significativa si cualquiera de los dos es un método get / set de una clase.
Confusamente, se ve similar a otros operadores de asignación (como +=
) pero se comporta de manera diferente.
a += b
traduce en a a = a + b
a ||= b
se traduce aproximadamente a * a || a = b
a || a = b
* Excepto que, cuando a
no está definido, a || a = b
a || a = b
sería NameError, mientras que a ||= b
establece a
a b
.
Otras lecturas:
unless x x = y end
a menos que x tenga un valor (no es nulo o falso), establézcalo igual a y
es equivalente a
x ||= y
||=
se llama un 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, x
ahora es igual a 10. Sin embargo, en el segundo ejemplo, x
ya está definido como 20. Por lo tanto, el operador condicional no tiene ningún efecto. x
sigue siendo 20 después de ejecutar x ||= 10
.
|| = es un operador de asignación condicional
x ||= y
es equivalente a
x = x || y
o alternativamente
if defined?(x) and x
x = x
else
x = y
end
a ||= b
es equivalente a
a || a = b
y no
a = a || b
debido a la situación en la que se define un hash con un valor predeterminado (el hash devolverá el valor predeterminado para cualquier clave no definida)
a = Hash.new(true) #Which is: {}
si utiliza:
a[10] ||= 10 #same as a[10] || a[10] = 10
a sigue siendo
{}
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 se define para la clave 10
, en lugar de nunca realizar la asignación en primer lugar.
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 valor b
no se asignará a a
. Todavía tendrá un 5
.
Es un patrón de memorización que se utiliza en Ruby para acelerar los accesores.
def users
@users ||= User.all
end
Esto básicamente se traduce en:
@users = @users || User.all
Así que harás una llamada a la base de datos la primera vez que llames a este método.
Las llamadas futuras a este método solo devolverán el valor de la variable de instancia @users
.
irb(main):001:0> a = 1
=> 1
irb(main):002:0> a ||= 2
=> 1
Porque a
ya estaba configurado en 1
irb(main):003:0> a = nil
=> nil
irb(main):004:0> a ||= 2
=> 2
Porque era nil
x ||= y
es
x || x = y
"si x es falso o no definido, entonces x apunta a y"
x ||= y
significa
si x
tiene algún valor, déjelo solo y no cambie el valor; de lo contrario, establezca x
en y
.