먼저이 동작은 배열뿐만 아니라 이후에 변경되는 모든 기본값 (예 : 해시 및 문자열)에 적용됩니다.
TL; DR : Hash.new { |h, k| h[k] = [] }가장 관용적 인 솔루션을 원하고 이유는 신경 쓰지 않는 경우 사용하십시오 .
작동하지 않는 것
Hash.new([])작동하지 않는 이유
왜 Hash.new([])작동하지 않는지 자세히 살펴 보겠습니다 .
h = Hash.new([])
h[0] << 'a' #=> ["a"]
h[1] << 'b' #=> ["a", "b"]
h[1] #=> ["a", "b"]
h[0].object_id == h[1].object_id #=> true
h #=> {}
기본 객체가 재사용되고 변경되는 것을 볼 수 있습니다 (이는 유일한 기본값으로 전달되기 때문입니다. 해시는 새 기본값을 가져올 방법이 없기 때문입니다). 그러나 키나 값이없는 이유는 무엇입니까? h[1]여전히 우리에게 가치를 주면서도 배열에서 ? 다음은 힌트입니다.
h[42] #=> ["a", "b"]
각 []호출에 의해 반환 된 배열 은 기본값 일 뿐이며, 이번에는 변경해 왔으므로 이제 새 값을 포함합니다. <<해시에 할당하지 않기 때문에 ( =현재 없이 루비에서 할당 할 수 없습니다 † ), 실제 해시에 아무것도 넣지 않았습니다. 대신 우리는 사용이 <<=(이다 <<로 +=이다 +) :
h[2] <<= 'c' #=> ["a", "b", "c"]
h #=> {2=>["a", "b", "c"]}
이것은 다음과 같습니다.
h[2] = (h[2] << 'c')
Hash.new { [] }작동하지 않는 이유
using Hash.new { [] }은 원래 기본값을 재사용하고 변경하는 문제를 해결하지만 (주어진 블록이 매번 호출되어 새 배열을 반환하므로) 할당 문제는 해결되지 않습니다.
h = Hash.new { [] }
h[0] << 'a' #=> ["a"]
h[1] <<= 'b' #=> ["b"]
h #=> {1=>["b"]}
작동하는 것
할당 방법
우리가 항상 사용하는 기억한다면 <<=, 다음 Hash.new { [] } 입니다 가능한 솔루션,하지만 조금 이상한와 (내가 본 적이 비 관용적의 <<=야생에서 사용되지 않습니다). 또한 <<실수로 사용 하면 미묘한 버그가 발생하기 쉽습니다 .
변경 가능한 방법
상태에 대한 문서Hash.new (내 자신을 강조) :
블록이 지정되면 해시 개체와 키로 호출되며 기본값을 반환해야합니다. 필요한 경우 해시에 값을 저장하는 것은 블록의 책임 입니다.
따라서 다음 <<대신 사용하려면 블록 내에서 해시에 기본값을 저장해야합니다 <<=.
h = Hash.new { |h, k| h[k] = [] }
h[0] << 'a' #=> ["a"]
h[1] << 'b' #=> ["b"]
h #=> {0=>["a"], 1=>["b"]}
이렇게하면 개별 호출 (사용 <<=)에서에 전달 된 블록으로 할당을 효과적으로 이동 Hash.new하여 <<.
이 방법과 다른 방법 사이에는 한 가지 기능적 차이가 있습니다.이 방법은 읽을 때 기본값을 할당합니다 (할당은 항상 블록 내부에서 발생하기 때문). 예를 들면 :
h1 = Hash.new { |h, k| h[k] = [] }
h1[:x]
h1 #=> {:x=>[]}
h2 = Hash.new { [] }
h2[:x]
h2 #=> {}
불변의 방법
Hash.new([])잘 작동하는 동안 왜 작동하지 않는지 궁금 할 것 Hash.new(0)입니다. 핵심은 Ruby의 Numerics는 불변이므로 자연스럽게 제자리에서 변경하지 않습니다. 기본값을 불변으로 취급하면 Hash.new([])잘 사용할 수 있습니다 .
h = Hash.new([].freeze)
h[0] += ['a'] #=> ["a"]
h[1] += ['b'] #=> ["b"]
h[2] #=> []
h #=> {0=>["a"], 1=>["b"]}
그러나 ([].freeze + [].freeze).frozen? == false. 따라서 불변성이 전체적으로 유지되도록하려면 새 개체를 다시 고정하도록주의해야합니다.
결론
모든 방법 중에서 개인적으로 "불변하는 방법"을 선호합니다. 일반적으로 불변성은 사물에 대한 추론을 훨씬 더 간단하게 만듭니다. 결국 숨겨 지거나 미묘한 예기치 않은 동작의 가능성이없는 유일한 방법입니다. 그러나 가장 일반적이고 관용적 인 방법은 "변동 가능한 방법"입니다.
마지막으로, Hash 기본값의 이러한 동작은 Ruby Koans에 기록되어 있습니다.
† 이것은 엄격히 사실이 아닙니다. instance_variable_set우회 와 같은 메소드 는 l- 값 =이 동적 일 수 없기 때문에 메타 프로그래밍을 위해 존재해야합니다 .