답변:
이 질문은 루비 메일 링리스트와 루비 블로그에서 자주 논의되었는데, 이제 루비 메일 링리스트에이 문제를 논의하는 루비 메일 링리스트 의 다른 모든 스레드 에 대한 링크를 수집하는 것이 유일한 목적인 스레드도 있습니다. .
하나는 다음과 같습니다. || = (또는 동일한) 스레드 및 페이지의 최종 목록
당신이 경우 정말 무슨 일이 일어나고 있는지 알고 싶어의 "단축 할당"섹션 11.4.2.3 한 번 봐 걸릴 루비 언어 초안 사양 .
첫 번째 근사치로
a ||= b
에 해당
a || a = b
와 하지 에 해당
a = a || b
그러나 특히 a
정의되지 않은 경우 첫 번째 근사치 입니다. 시맨틱은 단순 변수 할당, 메소드 할당 또는 인덱싱 할당인지에 따라 다릅니다.
a ||= b
a.c ||= b
a[c] ||= b
모두 다르게 취급됩니다.
a = false; a ||= true
하지 않는 사실을 부르지 않을 것입니다.
a ||= b
A는 조건 할당 연산자 . 이 수단 경우 a
미정되거나 falsey 후 평가 b
및 집합 a
결과로 . 마찬가지로 a
정의되고 진실로 b
평가되면 평가되지 않으며 할당이 수행되지 않습니다. 예를 들면 다음과 같습니다.
a ||= nil # => nil
a ||= 0 # => 0
a ||= 2 # => 0
foo = false # => false
foo ||= true # => true
foo ||= false # => true
혼란스럽게도 다른 할당 연산자 (예 :)와 비슷 +=
하지만 다르게 동작합니다.
a += b
로 번역 a = a + b
a ||= b
대략적으로 a || a = b
의 속기입니다 a || a = b
. 차이는 때이다 a
정의되지 a || a = b
올리는 것 NameError
반면, a ||= b
세트 a
로 b
. 이 구별은 경우 중요하지 않습니다 a
및 b
로컬 변수가 있지만 하나가 클래스의 게터 / setter 메소드의 경우 중요합니다.
더 읽을 거리 :
h = Hash.new(0); h[1] ||= 2
. 이제 두 가지 가능한 확장 h[1] = h[1] || 2
대를 고려하십시오 h[1] || h[1] = 2
. 두 표현식 모두 평가 0
되지만 첫 번째 표현식 은 해시 크기를 불필요하게 증가시킵니다. 아마도 이것이 Matz가 ||=
두 번째 확장과 같은 행동 을하도록 선택한 이유 일 것입니다 . (나는 이것을 다른 답변에 연결된 스레드 중 하나의 예를 기반으로했습니다.)
a || a = b
제기 NameError
하는 경우 a
정의되지 않습니다. a ||= b
하지 않고 대신 초기화 a
하고로 설정합니다 b
. 그것이 내가 아는 한이 둘의 유일한 차이점입니다. 마찬가지로, 사이의 유일한 차이점 a = a || b
과 a ||= b
제가 알고 그 경우이다 a=
방법, 그것은 관계없이 어떤 전화를받을 것이다 a
돌아갑니다. 또한, 사이의 유일한 차이점 a = b unless a
과 a ||= b
제가 알고 그에게 그 문을 평가하는 것입니다 nil
대신 a
하는 경우가 a
truthy입니다. 근사치가 많지만 그에 상응하는 것은 없습니다.
a ||= b
다음 각 줄과 동일한 방식으로 평가
a || a = b
a ? a : a = b
if a then a else a = b end
-
반면에
a = a || b
다음 각 줄과 동일한 방식으로 평가
a = a ? a : b
if a then a = a else a = b end
-
편집 : AJedi32가 주석에서 지적했듯이 다음과 같은 경우에만 적용됩니다. 1. a는 정의 된 변수입니다. 2. 한 번 및 두 번 평가해도 프로그램 또는 시스템 상태에는 차이가 없습니다.
a
false / zero / undefined 인 경우 두 번 평가됨을 의미합니다 . (그러나 나는 루비를 모른다. 그래서 lvalues가 정확하게 '평가'될 수 있는지 모르겠다 ...)
a || a = b
, a ? a : a = b
, if a then a else a = b end
및 if a then a = a else a = b end
경우에 오류가 발생한다 a
정의되어있는 반면 a ||= b
및 a = a || b
하지 않습니다. 또한, a || a = b
, a ? a : a = b
, if a then a else a = b end
, a = a ? a : b
, 및 if a then a = a else a = b end
평가 a
두 번 때 a
truthy이며, 반면 a ||= b
및 a = a || b
하지 않습니다.
a || a = b
평가되지 않습니다 . a
a
the end state will be equivalent after the whole line has been evaluated
그런 것은 아닙니다. a
방법이 무엇입니까 ? 방법은 부작용이있을 수 있습니다. 예로는 public; def a=n; @a=n; end; def a; @a+=1; end; self.a = 5
, self.a ||= b
6을 반환하지만 self.a ? self.a : self.a = b
7을 반환합니다
또는 같음을 의미합니다. 왼쪽의 값이 정의되어 있는지 확인한 다음 사용하십시오. 그렇지 않은 경우 오른쪽의 값을 사용하십시오. Rails에서이를 사용하여 모델에서 인스턴스 변수를 캐시 할 수 있습니다.
현재 로그인 한 사용자를 가져 오는 함수를 생성하는 빠른 Rails 기반 예제 :
class User > ActiveRecord::Base
def current_user
@current_user ||= User.find_by_id(session[:user_id])
end
end
@current_user 인스턴스 변수가 설정되어 있는지 확인합니다. 있는 경우이를 리턴하여 데이터베이스 호출을 저장합니다. 그러나 설정되어 있지 않으면 호출 한 다음 @current_user 변수를 설정합니다. 실제로 간단한 캐싱 기술이지만 응용 프로그램에서 동일한 인스턴스 변수를 여러 번 가져올 때 유용합니다.
undefined
뿐만 아니라에, false
그리고 nil
관련이되지 않을 수도있는, current_user
그러나 특히이 false
다른 경우 unexpectecd 수 있습니다
x ||= y
이다
x || x = y
"x가 거짓이거나 정의되지 않은 경우 x는 y를 가리 킵니다."
정확하게 말하면, a ||= b
" a
정의되지 않거나 허위 ( false
또는 nil
) 인 경우,로 설정 a
하여 b
평가 (즉, 반환)로 b
, 그렇지 않으면 a
"로 평가합니다 .
다른 사람들은 종종 그 말함으로써이 문제를 설명하기 위해 시도 a ||= b
에 해당 a || a = b
하거나 a = a || b
. 이러한 동등성은 개념을 이해하는 데 도움이 될 수 있지만 모든 조건에서 정확 하지는 않습니다 . 설명해주세요 :
a ||= b
⇔a || a = b
?
이 명령문의 동작은 a
정의되지 않은 로컬 변수 인 경우와 다릅니다 . 이 경우으로 a ||= b
설정 a
하고 b
(으로 평가 b
) 반면 a || a = b
을 올리십시오 NameError: undefined local variable or method 'a' for main:Object
.
a ||= b
⇔a = a || b
?
이러한 진술의 동등성은 종종 유사한 등가 다른 마찬가지입니다 때문에, 가정 단축 할당 사업자 (즉 +=
, -=
, *=
, /=
, %=
, **=
, &=
, |=
, ^=
, <<=
, 및 >>=
). 그러나 ||=
이러한 진술의 행동은 대상에 대한 방법 이며 진실 할 때 다를 수 있습니다 . 이 경우, (에 평가 이외의 아무것도 할 것입니다 반면,) 호출 에 의 수신기를. 다른 사람들 이 지적했듯이, 이것은 호출 에 해시에 키를 추가하는 것과 같은 부작용이 있을 때 차이를 만들 수 있습니다 .a=
a
a ||= b
a
a = a || b
a=(a)
a
a=a
a ||= b
⇔a = b unless a
??
이 진술의 행동은 그들이 a
진실한 때에 평가하는 것만 다릅니다 . 이 경우에는 a = b unless a
로 평가 nil
되지만 ( a
예상대로 설정되지는 않지만 )로 a ||= b
평가됩니다 a
.
a ||= b
⇔defined?(a) ? (a || a = b) : (a = b)
????
아직도 아니야. 이 문은에 method_missing
대한 값을 반환하는 메서드가 있을 때 다를 수 있습니다 a
. 이 경우, a ||= b
어떤으로 평가합니다 method_missing
반환하고, 세트에 시도하지 a
반면, defined?(a) ? (a || a = b) : (a = b)
설정됩니다 a
에 b
와로 평가 b
.
좋아, 좋아, 그럼 입니다 a ||= b
동등? 루비로 이것을 표현하는 방법이 있습니까?
글쎄, 내가 아무것도 간과하지 않는다고 가정하면, a ||= b
기능적으로는 ... ( drumroll )
begin
a = nil if false
a || a = b
end
기다려! noop가있는 첫 번째 예가 아닌가? 글쎄요 내가 전에 말한 방법 이 정의되지 않은 지역 변수 인 경우 a ||= b
와 같지 않다는 것을 기억 합니까? 글쎄, 그 행이 실행되지 않더라도 정의되지 않은 것을 보장합니다 . Ruby의 지역 변수는 어휘 범위가 있습니다.a || a = b
a
a = nil if false
a
(a=b unless a) or a
a
방법은, 그것은 대신 (그것이 truthy 값을 처음 반환하는 경우)를 한 번으로 두 번 호출됩니다. 예를 들어 a
돌아 오는 데 시간이 오래 걸리거나 부작용이있는 경우 동작이 달라질 수 있습니다 .
b
할a
좌변은 여전히 RHS에 그 값을 설정하지 않습니다, 우변은 여전히 좌에 할당하지 않거나, 다른 말로하면?
a ||= b
인터넷에서 찾은 최고의 답변입니다. 감사.
가정 a = 2
하고b = 3
그런 다음 a ||= b
에 결과 것 a
즉,의 값 2
.
어떤 값으로 평가하여하지에했을 때와 같은 false
또는 nil
그 왜 .. 그의 ll
평가하지 b
의 값입니다.
이제 a = nil
및을 가정하십시오 b = 3
.
그런 다음 a ||= b
에 결과 것 3
즉, b
의 값입니다.
처음으로 결과를 평가하려고 시도한 결과 nil
..b
.
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
어디서 초기화되지 않은 User.find_by_id(session[:user_id])
경우에만 실행됩니다 @current_user
.
a || = b
'a'에 값이 존재하고 해당 값을 사용하여 유지를 변경하지 않으려는 경우를 나타냅니다. 그렇지 않으면 'a'에 값이 없으면 'b'의 값을 사용하십시오.
왼쪽이 null이 아닌 경우 간단한 단어는 기존 값을 가리키고, 그렇지 않으면 오른쪽의 값을 가리 킵니다.
a ||= b
에 해당
a || a = b
그리고 아닙니다
a = a || b
기본값으로 해시를 정의하는 상황으로 인해 (해시는 정의되지 않은 키의 기본값을 반환합니다)
a = Hash.new(true) #Which is: {}
사용하는 경우 :
a[10] ||= 10 #same as a[10] || a[10] = 10
는 여전히 :
{}
그러나 당신이 그렇게 쓸 때 :
a[10] = a[10] || 10
a가된다 :
{10 => true}
key에 자체 값을 할당했기 때문에 10
기본값은 true이므로 이제 10
할당을 절대 수행하지 않고 키에 대해 해시가 정의 됩니다.
||=
left == nil (또는 undefined 또는 false) 인 경우에만 오른쪽에 값을 할당합니다.
이 루비 언어 문법. 정답은 ruby-lang 설명서를 확인하는 것입니다. 다른 모든 설명은 난독 화 됩니다.
"ruby-lang docs 축약 된 할당".
https://docs.ruby-lang.org/en/2.4.0/syntax/assignment_rdoc.html#label-Abbreviated+Assignment
b = 5
a ||= b
이것은 다음과 같이 번역됩니다.
a = a || b
어느 것
a = nil || 5
그래서 마침내
a = 5
이제 다시 전화하면 :
a ||= b
a = a || b
a = 5 || 5
a = 5
b = 6
이제 다시 전화하면 :
a ||= b
a = a || b
a = 5 || 6
a = 5
관찰하면 b
값이에 할당되지 않습니다 a
. a
여전히 가질 것이다5
.
루비에서 접근 자의 속도를 높이기 위해 사용되는 메모 패턴입니다.
def users
@users ||= User.all
end
이것은 기본적으로 다음과 같이 번역됩니다.
@users = @users || User.all
따라서이 메소드를 처음 호출 할 때 데이터베이스를 호출하게됩니다.
이 방법에 대한 미래의 호출은의 값이 반환됩니다 @users
인스턴스 변수를.
a ||= b
말과 동일 a = b if a.nil?
또는a = b unless a
그러나 세 가지 옵션 모두 동일한 성능을 보여줍니까? Ruby 2.5.1에서는
1000000.times do
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
end
PC에서 0.099 초 소요
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
0.062 초가 걸립니다. 거의 40 % 빠릅니다.
그리고 우리는 또한 :
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
0.166 초가 걸립니다.
이것이 일반적인 성능에 큰 영향을 미치지는 않지만 마지막 최적화가 필요한 경우이 결과를 고려하십시오. 그건 그렇고 : a = 1 unless a
초보자가 읽기 쉽기 때문에 설명이 필요 없습니다.
참고 1 : 할당 라인을 여러 번 반복하는 이유는 측정 된 시간에 루프의 오버 헤드를 줄이는 것입니다.
참고 2 : a=nil
할당 전에 nil을 입력 하면 결과가 비슷 합니다.