O que o código a seguir significa em Ruby?
||=
Tem algum significado ou razão para a sintaxe?
Essa questão foi discutida com tanta frequência nas listas de discussão e blogs Ruby que agora existem até mesmo tópicos na lista de discussão Ruby cujo único objetivo é coletar links para todos os outros tópicos na lista de discussão Ruby que discutem esse problema. .
Aqui está um: A lista definitiva de || = (OU iguais) threads e páginas
Se você realmente quer saber o que está acontecendo, consulte a Seção 11.4.2.3 "Atribuições abreviadas" da Especificação de Rascunho no Ruby Language .
Como primeira aproximação,
a ||= b
é equivalente a
a || a = b
e não equivalente a
a = a || b
No entanto, essa é apenas uma primeira aproximação, especialmente se a
for indefinida. A semântica também difere dependendo se é uma atribuição simples de variável, atribuição de método ou indexação:
a ||= b
a.c ||= b
a[c] ||= b
todos são tratados de forma diferente.
a = false; a ||= true
se não fazer o que sua resposta diz que faz uma "nuance".a ||= b
é um operador de atribuição condicional . Significa sea
é indefinido ou falsey , então avalieb
e definaa
o resultado . De maneira equivalente, sea
for definido e avaliado como verdadeiro,b
não será avaliado e nenhuma atribuição ocorrerá. Por exemplo:De maneira confusa, ele se parece com outros operadores de atribuição (como
+=
), mas se comporta de maneira diferente.a += b
traduz paraa = a + b
a ||= b
traduz aproximadamente paraa || a = b
É quase um atalho para
a || a = b
. A diferença é que, quandoa
é indefinido,a || a = b
aumentariaNameError
, enquantoa ||= b
definidoa
comob
. Essa distinção não é importante sea
eb
são ambas variáveis locais, mas é significativa se for um método getter / setter de uma classe.Leitura adicional:
fonte
h = Hash.new(0); h[1] ||= 2
. Agora, considere os dois possíveis expansõesh[1] = h[1] || 2
vsh[1] || h[1] = 2
. Ambas as expressões são avaliadas,0
mas a primeira aumenta desnecessariamente o tamanho do hash. Talvez por isso Matz tenha optado por se||=
comportar mais como a segunda expansão. (Eu baseei este em um exemplo de um dos tópicos ligados a em outra resposta.)a || a = b
gera umNameError
sea
é indefinido.a ||= b
não, mas o inicializaa
e define comob
. Essa é a única distinção entre os dois, tanto quanto eu sei. Da mesma forma, a única diferença entrea = a || b
ea ||= b
que estou ciente é que, sea=
for um método, ele será chamado independentemente do quea
retornar. Além disso, a única diferença entrea = b unless a
ea ||= b
que estou ciente é que essa declaração é avaliada emnil
vez dea
sea
é verdadeira. Lotes de aproximações, mas nada equivalente ...Resposta concisa e completa
avalia da mesma maneira que cada uma das seguintes linhas
-
Por outro lado,
avalia da mesma maneira que cada uma das seguintes linhas
-
Edit: Como AJedi32 apontou nos comentários, isso só é válido se: 1. a é uma variável definida. 2. Avaliar uma vez e duas vezes não resulta em uma diferença no estado do programa ou do sistema.
fonte
a
for falso / zero / indefinido, será avaliado duas vezes. (Mas eu não sei Ruby, então eu não sei se lvalues pode ser 'avaliada' exatamente ...)a || a = b
,a ? a : a = b
,if a then a else a = b end
, Eif a then a = a else a = b end
irá lançar um erro sea
não estiver definida, enquanto quea ||= b
ea = a || b
não vai. Além disso,a || a = b
,a ? a : a = b
,if a then a else a = b end
,a = a ? a : b
, eif a then a = a else a = b end
avaliara
duas vezes quandoa
é truthy, enquantoa ||= b
ea = a || b
não.a || a = b
não avaliaa
duas vezes quandoa
é verdade.the end state will be equivalent after the whole line has been evaluated
Isso não é necessariamente verdade. E sea
for um método? Métodos podem ter efeitos colaterais. Por exemplopublic; def a=n; @a=n; end; def a; @a+=1; end; self.a = 5
, Withself.a ||= b
retornará 6, masself.a ? self.a : self.a = b
retornará 7. #Em resumo,
a||=b
significa: Sea
estiverundefined, nil or false
, atribuab
aa
. Caso contrário, mantenha-sea
intacto.fonte
x ||= y
significase
x
tiver algum valor, deixe em branco e não altere o valor; caso contrário, definax
comoy
fonte
Significa ou é igual a. Ele verifica se o valor à esquerda está definido e use-o. Caso contrário, use o valor à direita. Você pode usá-lo no Rails para armazenar em cache variáveis de instância em modelos.
Um exemplo rápido do Rails, em que criamos uma função para buscar o usuário conectado no momento:
Ele verifica se a variável de instância @current_user está configurada. Se for, ele retornará, salvando uma chamada ao banco de dados. Se não estiver definido, no entanto, fazemos a chamada e definimos a variável @current_user para isso. É uma técnica de cache realmente simples, mas é ótima para quando você está buscando a mesma variável de instância no aplicativo várias vezes.
fonte
undefined
, mas tambémfalse
enil
, o que pode não ser relevante paracurrent_user
, mas especialmente ofalse
pode ser unexpectecd em outros casosé
"se x é falso ou indefinido, então x aponta para y"
fonte
Para ser mais preciso,
a ||= b
meios "sea
for indefinido ou Falsas (false
ounil
), definidaa
parab
e avaliar a (ou seja, o retorno)b
, de outro modo para avaliara
".Outros geralmente tentam ilustrar isso dizendo que
a ||= b
é equivalente aa || a = b
oua = a || b
. Essas equivalências podem ser úteis para entender o conceito, mas lembre-se de que elas não são precisas em todas as condições. Permita-me explicar:a ||= b
⇔a || a = b
?O comportamento dessas instruções difere quando
a
é uma variável local indefinida. Nesse caso,a ||= b
será definidoa
parab
(e avaliar ab
), ao passo quea || a = b
elevar a vontadeNameError: undefined local variable or method 'a' for main:Object
.a ||= b
⇔a = a || b
?A equivalência destas declarações são frequentemente assumido, desde a equivalência semelhante é verdadeiro para outras de atribuição abreviado operadores (ou seja
+=
,-=
,*=
,/=
,%=
,**=
,&=
,|=
,^=
,<<=
, e>>=
). No entanto,||=
o comportamento dessas instruções pode diferir quandoa=
é um método em um objeto ea
é verdade. Nesse caso,a ||= b
não fará nada (excepto avaliar aa
), enquanto quea = a || b
irá chamara=(a)
ema
's receptor. Como outros já apontaram, isso pode fazer a diferença ao chamara=a
efeitos colaterais, como adicionar chaves a um hash.a ||= b
⇔a = b unless a
??O comportamento dessas declarações difere apenas no que elas avaliam quando
a
é verdade. Nesse caso,a = b unless a
avaliará comonil
(emboraa
ainda não esteja definido como esperado), enquantoa ||= b
avaliará comoa
.a ||= b
⇔defined?(a) ? (a || a = b) : (a = b)
????Ainda não. Essas instruções podem diferir quando
method_missing
existe um método que retorna um valor verdadeiro paraa
. Neste caso,a ||= b
irá avaliar a tudo o quemethod_missing
retorna, e não tentar conjuntoa
, enquanto quedefined?(a) ? (a || a = b) : (a = b)
irá definira
parab
e avaliar ab
.Ok, ok, então o que é
a ||= b
equivalente? Existe uma maneira de expressar isso em Ruby?Bem, assumindo que eu não estou ignorando nada, acredito que
a ||= b
seja funcionalmente equivalente a ... ( drumroll )Aguente! Esse não é apenas o primeiro exemplo com um noop antes? Bem, não exatamente. Lembra como eu disse antes que isso
a ||= b
não é apenas equivalente aa || a = b
quandoa
é uma variável local indefinida? Bem,a = nil if false
garante que issoa
nunca seja indefinido, mesmo que essa linha nunca seja executada. Variáveis locais no Ruby têm escopo lexicamente.fonte
(a=b unless a) or a
a
for um método, ele será chamado duas vezes em vez de uma vez (se retornar um valor verdadeiro na primeira vez). Isso pode causar comportamentos diferentes se, por exemplo,a
demorar muito para retornar ou tiver efeitos colaterais.b
aa
, o rhs ainda atribui ao lhs, ou em outras palavras, o lhs ainda não atribui seu valor ao rhs?a ||= b
resposta que encontrei na Internet. Obrigado.unless x x = y end
a menos que x tenha um valor (não é nulo ou falso), defina-o igual a y
é equivalente a
x ||= y
fonte
Suponha
a = 2
eb = 3
ENTÃO,
a ||= b
resultará noa
valor de ie2
.Como quando um avalia para algum valor que não resultou em
false
ounil
.. É por isso quell
não avaliab
o valor de.Agora Suponha
a = nil
eb = 3
.Então
a ||= b
resultará no valor do3
ieb
.Como ele primeiro tenta avaliar o valor de a que resultou em
nil
... então ele avalioub
o valor de.O melhor exemplo usado no aplicativo ror é:
Onde,
User.find_by_id(session[:user_id])
é acionado se e somente se@current_user
não tiver sido inicializado anteriormente.fonte
a || = b
Significa se algum valor está presente em 'a' e você não deseja alterá-lo, continue usando esse valor; caso contrário, se 'a' não tiver nenhum valor, use o valor de 'b'.
Palavras simples, se o lado esquerdo não for nulo, aponte para o valor existente, caso contrário, aponte para o valor no lado direito.
fonte
é equivalente a
e não
devido à situação em que você define um hash com um padrão (o hash retornará o padrão para quaisquer chaves indefinidas)
se você usar:
a ainda é:
mas quando você escreve assim:
a se torna:
porque você atribuiu o valor de si na chave
10
, que é o padrão true, agora o hash está definido para a chave10
, em vez de nunca executar a atribuição em primeiro lugar.fonte
É como instanciação preguiçosa. Se a variável já estiver definida, ele assumirá esse valor em vez de criar o valor novamente.
fonte
Lembre-se também de que
||=
não é uma operação atômica e, portanto, não é segura para threads. Como regra geral, não a use para métodos de classe.fonte
Esta é a notação de atribuição padrão
por exemplo: x || = 1
isso verificará se x é nulo ou não. Se x for realmente nulo, ele atribuirá esse novo valor (1 em nosso exemplo)
mais explícito:
se x == nil
x = 1
final
fonte
nil
ou outrofalse
, não apenasnil
|| = é um operador de atribuição condicional
é equivalente a
ou alternativamente
fonte
Se
X
NÃO tiver um valor, será atribuído o valor deY
. Senão, preservará seu valor original, 5 neste exemplo:fonte
Como um equívoco comum,
a ||= b
não é equivalente aa = a || b
, mas se comporta comoa || a = b
.Mas aqui vem um caso complicado. Se
a
não estiver definido,a || a = 42
geraNameError
, enquantoa ||= 42
retorna42
. Portanto, eles não parecem ser expressões equivalentes.fonte
||=
atribui valor à direita somente se for esquerdo == nil (ou for indefinido ou falso).fonte
Essa sintaxe ruby-lang. A resposta correta é verificar a documentação do ruby-lang. Todas as outras explicações ofuscam .
Google
"ruby-lang docs Abreviação de Tarefa".
Documentos em Ruby-Lang
https://docs.ruby-lang.org/en/2.4.0/syntax/assignment_rdoc.html#label-Abbreviated+Assignment
fonte
Porque
a
já estava definido como1
Porque
a
foinil
fonte
Isso se traduz em:
qual será
então finalmente
Agora, se você ligar novamente:
Agora, se você ligar novamente:
Se você observar, o
b
valor não será atribuído aa
.a
ainda terá5
.É um padrão de memorização que está sendo usado no Ruby para acelerar os acessadores.
Isso basicamente se traduz em:
Portanto, você fará uma chamada ao banco de dados pela primeira vez que chamar esse método.
Chamadas futuras para esse método retornarão apenas o valor da
@users
variável de instância.fonte
||=
é chamado de operador de atribuição condicional.Basicamente, funciona como
=
mas com a exceção de que se uma variável já tiver sido atribuída, ela não fará nada.Primeiro exemplo:
Segundo exemplo:
No primeiro exemplo,
x
agora é igual a 10. No entanto, no segundo exemplox
já está definido como 20. Portanto, o operador condicional não tem efeito.x
ainda é 20 depois de executarx ||= 10
.fonte
a ||= b
é o mesmo que dizera = b if a.nil?
oua = b unless a
Mas todas as três opções mostram o mesmo desempenho? Com o Ruby 2.5.1, isso
leva 0,099 segundos no meu PC, enquanto
leva 0,062 segundos. Isso é quase 40% mais rápido.
e então também temos:
que leva 0.166 segundos.
Não que isso tenha um impacto significativo no desempenho em geral, mas se você precisar desse último bit de otimização, considere esse resultado. A propósito:
a = 1 unless a
é mais fácil ler para o novato, é auto-explicativo.Nota 1: a razão para repetir a linha de atribuição várias vezes é reduzir a sobrecarga do loop no tempo medido.
Nota 2: Os resultados são semelhantes se eu fizer
a=nil
nada antes de cada tarefa.fonte