O que o código a seguir significa em Ruby?
||=
Tem algum significado ou razão para a sintaxe?
O que o código a seguir significa em Ruby?
||=
Tem algum significado ou razão para a sintaxe?
Respostas:
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 afor 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 ||= truese não fazer o que sua resposta diz que faz uma "nuance".
a ||= bé um operador de atribuição condicional . Significa se aé indefinido ou falsey , então avalie be defina ao resultado . De maneira equivalente, se afor definido e avaliado como verdadeiro, bnão será avaliado e nenhuma atribuição ocorrerá. Por exemplo:
a ||= nil # => nil
a ||= 0 # => 0
a ||= 2 # => 0
foo = false # => false
foo ||= true # => true
foo ||= false # => true
De maneira confusa, ele se parece com outros operadores de atribuição (como +=), mas se comporta de maneira diferente.
a += b traduz para a = a + ba ||= b traduz aproximadamente para a || a = bÉ quase um atalho para a || a = b. A diferença é que, quando aé indefinido, a || a = baumentaria NameError, enquanto a ||= bdefinido acomo b. Essa distinção não é importante se ae bsão ambas variáveis locais, mas é significativa se for um método getter / setter de uma classe.
Leitura adicional:
h = Hash.new(0); h[1] ||= 2. Agora, considere os dois possíveis expansões h[1] = h[1] || 2vs h[1] || h[1] = 2. Ambas as expressões são avaliadas, 0mas 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 = bgera um NameErrorse aé indefinido. a ||= bnão, mas o inicializa ae define como b. Essa é a única distinção entre os dois, tanto quanto eu sei. Da mesma forma, a única diferença entre a = a || be a ||= bque estou ciente é que, se a=for um método, ele será chamado independentemente do que aretornar. Além disso, a única diferença entre a = b unless ae a ||= bque estou ciente é que essa declaração é avaliada em nilvez de ase aé verdadeira. Lotes de aproximações, mas nada equivalente ...
a ||= b
avalia da mesma maneira que cada uma das seguintes linhas
a || a = b
a ? a : a = b
if a then a else a = b end
-
Por outro lado,
a = a || b
avalia da mesma maneira que cada uma das seguintes linhas
a = a ? a : b
if a then a = a else a = b end
-
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.
afor 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, E if a then a = a else a = b endirá lançar um erro se anão estiver definida, enquanto que a ||= be a = a || bnão vai. Além disso, a || a = b, a ? a : a = b, if a then a else a = b end, a = a ? a : b, e if a then a = a else a = b endavaliar aduas vezes quando aé truthy, enquanto a ||= be a = a || bnão.
a || a = bnão avalia aduas vezes quando aé verdade.
the end state will be equivalent after the whole line has been evaluatedIsso não é necessariamente verdade. E se afor um método? Métodos podem ter efeitos colaterais. Por exemplo public; def a=n; @a=n; end; def a; @a+=1; end; self.a = 5, With self.a ||= bretornará 6, mas self.a ? self.a : self.a = bretornará 7. #
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:
class User > ActiveRecord::Base
def current_user
@current_user ||= User.find_by_id(session[:user_id])
end
end
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.
undefined, mas também falsee nil, o que pode não ser relevante para current_user, mas especialmente o falsepode ser unexpectecd em outros casos
x ||= y
é
x || x = y
"se x é falso ou indefinido, então x aponta para y"
Para ser mais preciso, a ||= bmeios "se afor indefinido ou Falsas ( falseou nil), definida apara be avaliar a (ou seja, o retorno) b, de outro modo para avaliar a".
Outros geralmente tentam ilustrar isso dizendo que a ||= bé equivalente a a || a = bou a = 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 ||= bserá definido apara b(e avaliar a b), ao passo que a || a = belevar a vontade NameError: 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 quando a=é um método em um objeto e aé verdade. Nesse caso, a ||= bnão fará nada (excepto avaliar a a), enquanto que a = a || birá chamar a=(a)em a's receptor. Como outros já apontaram, isso pode fazer a diferença ao chamar a=aefeitos 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 aavaliará como nil(embora aainda não esteja definido como esperado), enquanto a ||= bavaliará como a.
a ||= b⇔defined?(a) ? (a || a = b) : (a = b) ????
Ainda não. Essas instruções podem diferir quando method_missingexiste um método que retorna um valor verdadeiro para a. Neste caso, a ||= birá avaliar a tudo o que method_missingretorna, e não tentar conjunto a, enquanto que defined?(a) ? (a || a = b) : (a = b)irá definir apara be avaliar a b.
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 ||= bseja funcionalmente equivalente a ... ( drumroll )
begin
a = nil if false
a || a = b
end
Aguente! Esse não é apenas o primeiro exemplo com um noop antes? Bem, não exatamente. Lembra como eu disse antes que isso a ||= bnão é apenas equivalente a a || a = bquando aé uma variável local indefinida? Bem, a = nil if falsegarante que isso anunca seja indefinido, mesmo que essa linha nunca seja executada. Variáveis locais no Ruby têm escopo lexicamente.
(a=b unless a) or a
afor 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, ademorar muito para retornar ou tiver efeitos colaterais.
baa , o rhs ainda atribui ao lhs, ou em outras palavras, o lhs ainda não atribui seu valor ao rhs?
a ||= bresposta que encontrei na Internet. Obrigado.
Suponha a = 2eb = 3
ENTÃO, a ||= b resultará no avalor de ie 2.
Como quando um avalia para algum valor que não resultou em falseou nil.. É por isso que llnão avalia bo valor de.
Agora Suponha a = nile b = 3.
Então a ||= bresultará no valor do 3ie b.
Como ele primeiro tenta avaliar o valor de a que resultou em nil... então ele avaliou bo valor de.
O melhor exemplo usado no aplicativo ror é:
#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
Onde, User.find_by_id(session[:user_id])é acionado se e somente se @current_usernão tiver sido inicializado anteriormente.
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.
a ||= b
é equivalente a
a || a = b
e não
a = a || b
devido à situação em que você define um hash com um padrão (o hash retornará o padrão para quaisquer chaves indefinidas)
a = Hash.new(true) #Which is: {}
se você usar:
a[10] ||= 10 #same as a[10] || a[10] = 10
a ainda é:
{}
mas quando você escreve assim:
a[10] = a[10] || 10
a se torna:
{10 => true}
porque você atribuiu o valor de si na chave 10, que é o padrão true, agora o hash está definido para a chave 10, em vez de nunca executar a atribuição em primeiro lugar.
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.
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
nilou outro false, não apenasnil
||= atribui valor à direita somente se for esquerdo == nil (ou for indefinido ou falso).
Essa sintaxe ruby-lang. A resposta correta é verificar a documentação do ruby-lang. Todas as outras explicações ofuscam .
"ruby-lang docs Abreviação de Tarefa".
https://docs.ruby-lang.org/en/2.4.0/syntax/assignment_rdoc.html#label-Abbreviated+Assignment
b = 5
a ||= b
Isso se traduz em:
a = a || b
qual será
a = nil || 5
então finalmente
a = 5
Agora, se você ligar novamente:
a ||= b
a = a || b
a = 5 || 5
a = 5
b = 6
Agora, se você ligar novamente:
a ||= b
a = a || b
a = 5 || 6
a = 5
Se você observar, o bvalor não será atribuído a a. aainda terá 5.
É um padrão de memorização que está sendo usado no Ruby para acelerar os acessadores.
def users
@users ||= User.all
end
Isso basicamente se traduz em:
@users = @users || User.all
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 @usersvariável de instância.
||= é 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:
x ||= 10
Segundo exemplo:
x = 20
x ||= 10
No primeiro exemplo, xagora é igual a 10. No entanto, no segundo exemplo xjá está definido como 20. Portanto, o operador condicional não tem efeito. xainda é 20 depois de executarx ||= 10 .
a ||= b é o mesmo que dizer a = 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
1000000.times do
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
end
leva 0,099 segundos no meu PC, enquanto
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
leva 0,062 segundos. Isso é quase 40% mais rápido.
e então também temos:
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 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=nilnada antes de cada tarefa.