¿Qué significa / / = (o-igual) en Ruby?


¿Qué significa el siguiente código en Ruby?

||=

¿Tiene algún significado o razón para la sintaxis?

Author: Peter O., 2009-06-15

20 answers

Esta pregunta se ha discutido tan a menudo en las listas de correo de Ruby y 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 problema.

Aquí hay uno: La lista definitiva de / / = (O Iguales) hilos y páginas

Si realmente quiere saber lo que está pasando, eche un vistazo a la Sección 11.4.2.3 "Tareas abreviadas" de la Ruby Language Draft Specification .

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

Son tratados de manera diferente.

 157
Author: Jörg W Mittag,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-25 07:26:06

a ||= b es un "operador de asignación condicional". Es una especie-de-pero-no-del todo (*) abreviatura de a || a = b.

Significa " si a es indefinido o falsey (false o nil), luego evalúe b y establezca a en el 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 se define y evalúa a truthy, entonces el lado derecho del operador no se evalúa, y no se realiza ninguna asignación. Este la distinción no es importante si a y b son ambas variables locales, pero es significativa si cualquiera de ellas es un método getter/setter de una clase.

Confusamente, se ve similar a otros operadores de asignación (como +=) pero se comporta de manera diferente.

a += b se traduce como a = a + b

a ||= b se traduce como* a || a = b

*Excepto que, cuando a no está definido, a || a = b sería NameError, mientras que a ||= b establece a a b.

Además lectura:

 496
Author: Steve Bennett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-10 10:05:17

Respuesta concisa y completa

a ||= b

Evalúa de la misma manera que cada de las siguientes líneas

a || a = b
a ? a : a = b
if a then a else a = b end

-

Por otro lado,

a = a || b

Evalúa de la misma manera que cada 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 y dos veces no da lugar a una diferencia en el estado del programa o del sistema.

 31
Author: the_minted,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-20 23:10:05

En resumen, a||=b significa: Si a es undefined, nil or false, asignar b a a. De lo contrario, mantenga a intacto.

 22
Author: vidaica,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-20 00:29:53

Basicamente,


x ||= y significa

Si x tiene algún valor déjelo solo y no cambie el valor, de lo contrario establezca x en y.

 12
Author: sony vizio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-17 21:39:27

Significa o-igual a. Comprueba si el valor de la izquierda está definido, y luego lo usa. Si no lo es, usa el valor de la derecha. Puede usarlo en Rails para almacenar en caché variables de instancia en modelos.

Un ejemplo basado en quick Rails, donde creamos una función para recuperar el usuario actualmente registrado:

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á, guardando así una llamada a la base de datos. Sin embargo, si no está configurado, hacemos la llamada y luego establezca la variable @current_user en eso. Es una técnica de almacenamiento en caché muy simple, pero es ideal para cuando se obtiene la misma variable de instancia a través de la aplicación varias veces.

 8
Author: Jamie Rumbelow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-15 11:35:58
x ||= y

Es

x || x = y

"si x es falso o indefinido, entonces x apunta a y"

 8
Author: Kiattisak Anoochitarom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-11 10:56:04

Para ser precisos, a ||= b significa "si a es indefinido o falso (false o nil), establezca a a b y evalúe (es decir, devuelva) b, de lo contrario evalúe a a".

Otros a menudo tratan de ilustrar esto diciendo que a ||= b es equivalente a a || a = b o a = a || b. Estas equivalencias pueden ser útiles para entender el concepto, pero tenga en cuenta que son no exactas bajo todas las condiciones. Permítanme explicar:

  • a ||= ba || a = b?

    El comportamiento de estas sentencias difiere cuando a es una variable local indefinida. En ese caso, a ||= b establecerá a a b (y evaluar a b), mientras que a || a = b elevará NameError: undefined local variable or method 'a' for main:Object.

  • a ||= ba = a || b?

    A menudo se asume la equivalencia de estas declaraciones, ya que una equivalencia similar es cierta para otros operadores de asignación abreviada (i. e. +=,-=,*=,/=,%=,**=,&=,|=,^=,<<=, y >>=). Sin embargo, para ||= el comportamiento de estas declaraciones puede diferir cuando a= es un método sobre un objeto y a es veraz. En ese caso, a ||= b no hará nada (aparte de evaluar a a), mientras que a = a || b llamará a a=(a) al receptor de a. Como otros han señalado, esto puede hacer una diferencia cuando llamar a a=a tiene efectos secundarios, como agregar claves a un hash.

  • a ||= ba = 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á a nil (aunque a todavía no se establecerá, como se esperaba), mientras que a ||= b evaluará a a.

  • a ||= bdefined?(a) ? (a || a = b) : (a = b)????

    Todavía no. Estas sentencias pueden diferir cuando existe un método method_missing que devuelve un valor truth para a. En este caso, a ||= b evaluará a lo que method_missing devuelve, y no intentará establecer a, mientras que defined?(a) ? (a || a = b) : (a = b) establecerá a a b y evaluará a b.

Vale, vale, entonces 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 )

begin
  a = nil if false
  a || a = b
end

Espera! ¿No es solo el primer ejemplo con un noop antes? Bueno, no del todo. ¿Recuerdas cómo dije antes que a ||= b no es equivalente a a || a = b cuando a es una variable local indefinida? Bueno, a = nil if false asegura que a nunca esté indefinido, aunque esa línea nunca se ejecute. Las variables locales en Ruby son de ámbito léxico.

 5
Author: Ajedi32,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:44

unless x x = y end

A menos que x tenga un valor (no es nil o false), póngalo igual a y

Es equivalente a

x ||= y

 3
Author: 0r4cl3,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-15 19:20:13

Supongamos a = 2 y b = 3

ENTONCES, a ||= b se dará como resultado el valor de a, es decir, 2.

Como cuando a evalúa a algún valor que no resulta en false o nil.. Es por eso que ll no evalúa el valor de b.

Ahora supongamos a = nil y b = 3.

Entonces a ||= b será el resultado de 3, es decir, el valor de b.

Ya que primero intenta evaluar el valor de a que resultó en nil.. así que evaluó el valor de b.

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_user no se inicializa antes.

 3
Author: Pankhuri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-17 12:00:45

Esta es la notación de asignación predeterminada

Por ejemplo: x / / = 1
esto comprobará si x es nil o no. Si x es nil entonces le asignará ese nuevo valor (1 en nuestro ejemplo)

Más explícito:
si x = = nil
x = 1
fin

 3
Author: Max Rogers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-13 22:15:27
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 indefinida)

a = Hash.new(true) #Which is: {}

Si usa:

a[10] ||= 10 #same as a[10] || a[10] = 10

A sigue siendo:

{}

Pero cuando lo escribes así:{[11]]}

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 true, por lo que ahora se define el hash para la clave 10, en lugar de nunca realizar la tarea en primer lugar.

 2
Author: RileyE,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-13 16:26:59

Es como una instanciación perezosa. Si la variable ya está definida, tomará ese valor en lugar de volver a crear el valor.

 2
Author: mukh007,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-29 07:52:05
irb(main):001:0> a = 1
=> 1
irb(main):002:0> a ||= 2
=> 1

Porque a ya estaba establecido en 1

irb(main):003:0> a = nil
=> nil
irb(main):004:0> a ||= 2
=> 2

Porque a era nil

 1
Author: fuzz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-15 23:42:16

Por favor, también recuerde que ||= no es una operación atómica y por lo tanto, no es seguro hilo. Como regla general, no lo use para métodos de clase.

 1
Author: Luca Guidi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-12 10:31:14
b = 5
a ||= b

Esto se traduce como:

a = a || b

Que será

a = nil || 5

Así que finalmente

a = 5

Ahora si llamas a esto de nuevo:

a ||= b
a = a || b
a = 5 || 5
a = 5

b = 6

Ahora si llamas a esto de nuevo:

a ||= b
a = a || b
a = 5 || 6
a = 5 

Si observas, b el valor no será asignado a a. a todavía tendrá 5.

Es un Patrón de Memoización que se está utilizando en Ruby para acelerar los accesores.

def users
  @users ||= User.all
end

Esto se traduce básicamente como:

@users = @users || User.all

Así que usted hará una llamada a 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 variable de instancia @users.

 1
Author: siva krishna reddy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-21 00:07:15

Como un error común a||=b no es equivalente a a = a||b, pero que es, pero se comporta como un || a = b

Pero aquí viene un caso complicado

Si a no está definido, a || a = 42 genera un error de nombre, mientras que a ||= 42 devuelve 42. Por lo tanto, no parecen ser expresiones equivalentes.

 0
Author: tessie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-25 06:14:12

||= 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 x es ahora igual a 10. Sin embargo, en el segundo ejemplo x ya se define como 20. Así que el operador condicional no tiene efecto. x sigue siendo 20 después de ejecutar x ||= 10.

 0
Author: Charlie Wood,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-03 13:48:00

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

Tarda 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

Tarda 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 tarda 0.166 Segundos.

No es que esto tenga un impacto significativo en el rendimiento en general, pero si necesita ese último poco 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 para reducir la sobrecarga del bucle en el tiempo medido.

Nota 2: Los resultados son similares si hago a=nil nada antes de cada asignación.

 0
Author: Ymox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-28 15:50:46

||= es un operador de asignación condicional

  x ||= y

Es equivalente a

  x = x || y

O bien

if defined?(x) and x
    x = x
else 
    x = y
end
 0
Author: Sunda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-01 04:51:23