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


340

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

||=

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

Respuestas:


175

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.


2
El segundo enlace ha sufrido una putrefacción de bits (comentario de meta por stackoverflow.com/users/540162/nightfirecat ).
Andrew Grimm

330
Esa es una no respuesta muy críptica. La respuesta corta parece ser: a || = b significa que si a no está definido, asígnele el valor de b; de lo contrario, déjelo en paz. (Ok, hay matices y casos especiales, pero ese es el caso básico).
Steve Bennett

20
@SteveBennett: yo no lo llamaría el hecho de que a = false; a ||= trueno , no hacer lo que su respuesta dice que lo hace un "matiz".
Jörg W Mittag

23
Tal vez esta pregunta se ha hecho tantas veces porque la gente sigue respondiendo que esta pregunta se ha hecho tantas veces.
inocente

8
Con esta respuesta es fácil ver por qué hay múltiples hilos. Si intenta buscar una respuesta a esta pregunta con un sombrero de novato, notará que no todas las respuestas son claras. Por ejemplo, con este solo estás diciendo lo que no es. Sugiero mejorar su respuesta y dar respuestas fáciles para los novatos: a = b a menos que a
Arnold Roa

594

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 + b
  • a ||= b aproximadamente se traduce en a || a = b

Es 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:


52
Gracias por esta respuesta, tiene mucho más sentido.
Tom Hert

no he buscado lo suficiente pero aún no entiendo por qué usarías esto en lugar de a = a || si. tal vez solo mi opinión personal pero un poco ridículo de que exista tal matiz ...
dtc

2
@dtc, considere 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. (
Basé

1
Me gusta la otra respuesta sobre cuán profundo es, pero me encanta esta respuesta por su simplicidad. Para alguien que está aprendiendo Ruby, este es el tipo de respuesta que necesitamos. Si supiéramos lo que significa || =, entonces la pregunta probablemente habría sido redactada de manera diferente.
OBCENEIKON

1
Fyi, 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 ...
Ajedi32

32

Respuesta concisa y completa

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.


1
¿estas seguro? Esto implica que si 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 ...)
Steve Bennett

Ya veo lo que dices. Lo que quise decir con dos líneas equivalentes es que el estado final será equivalente después de que se haya evaluado toda la línea, lo que significa el valor de a, by lo que se devuelve. Si los intérpretes de ruby ​​usan o no diferentes estados, como varias evaluaciones de a, para llegar allí es completamente posible. ¿Hay algún intérprete de rubí experto por ahí?
the_minted

3
Esto no está del todo bien. 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.
Ajedi32

1
* corrección: a || a = bno se evaluará ados veces cuando asea ​​cierto.
Ajedi32

1
@the_minted 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.
Ajedi32

27

En resumen, a||=bsignifica: si aes así undefined, nil or false, asignar ba a. De lo contrario, manténgalo aintacto.


16
Básicamente,


x ||= y medio

si xtiene algún valor, déjelo solo y no cambie el valor; de lo contrario, configúrelo xeny


13

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.


8
Esto está mal. Lea Ruby-Forum.Com/topic/151660 y los enlaces que se proporcionan allí.
Jörg W Mittag

1
@Jo (diéresis) rg, no veo qué tiene de malo. Su enlace es una lista de otros enlaces. No hay una explicación real de por qué está mal, solo suena como un juicio de valor por tu parte.
eggmatters

esta respuesta es incorrecta, porque no solo se activa 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
dfherr

A pesar de lo incompleta que esta respuesta pueda exhibir (no funciona para nil / false), es la primera que explica por qué querría usar || =, ¡así que gracias!
Jonathan Tuzman


8

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 ||= ba || 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 ||= ba = 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 ||= ba = 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 ||= bdefined?(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.


Entonces, su tercer ejemplo extendido:(a=b unless a) or a
vol7ron

1
@ vol7ron Eso tiene un problema similar al # 2. Si 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.
Ajedi32

Además, la primera oración, ¿no debería decir asignar baa , no sigue asignando la rhs a la lhs, o en otras palabras, la lhs todavía no establece su valor en la rhs?
vol7ron

La mejor a ||= brespuesta que he encontrado en Internet. Gracias.
Eric Duminil

3

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


3

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.


3

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.


2
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.


2

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


2

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.


2

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


ya sea nilo falseno sólonil
Alex Poca

2

|| = 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

2

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

1

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

Pero aquí viene un caso complicado. Si ano se define, a || a = 42aumentos NameError, mientras que a ||= 42los retornos 42. Por lo tanto, no parecen ser expresiones equivalentes.


1

||= asigna valor a la derecha solo si izquierda == nulo (o no está definido o es falso).


Probablemente quisiste decir "asigna valor a la izquierda" en lugar de a la derecha
Maysam Torabi


0
irb(main):001:0> a = 1
=> 1
irb(main):002:0> a ||= 2
=> 1

Porque aya estaba configurado en1

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

Porque aeranil


¿Cuál es la fecha de respuesta aquí? ¿Por qué no se muestra año?
Shiv

0
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.


0

||= 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.


-2

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.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.