2013-08-09 2 views
0

다음 코드를 사용하면 표시 한 행에서 문제가 발생합니다.<<를 사용하여 문자열을 추가하는 것이 예상대로 작동하지 않지만 +를 사용하여

arr = 'I wish I may I wish I might'.split 

dictionary = Hash.new 

arr.each_with_index do |word, index| 
    break if arr[index + 2] == nil 

    key = word << " " << arr[index + 1] #This is the problem line 
    value = arr[index + 2] 

    dictionary.merge!({ key => value }) { |key, v1, v2| [v1] << v2 } 
end 
puts dictionary 

이 코드를 실행, 나는 다음과 같은 출력 예상 : 그러나

{"I wish"=>["I", "I"], "wish I"=>["may", "might"], "I may"=>"I", "may I"=>"wish"} 

, 내가 대신

{"I wish"=>["I may", "I"], "wish I"=>["may I", "might"], "I may"=>"I wish", "may I"=>"wish I"} 

내가 찾은되어 얻을 무엇을하는 나는 문제 라인을 대체하는 경우 와 함께

key = word + " " + arr[index + 1] 

모든 것이 예상대로 작동합니다. 예기치 않은 동작을 일으킨 첫 번째 버전의 내 라인에 대해서는 무엇입니까?

답변

1

String#<< 방법은 호출 된 원래 객체를 수정합니다. 여기 word 변수에 의해 참조되는 개체는 arr 배열에있는 문자열 중 하나에 대한 다른 참조입니다. 당신은 코드와 함께이 효과를 볼 수있다 : 당신이 반복자를 통해 한 번에이 방법을 사용하면

a = 'Hello' 
b = a << ' ' << 'World' 
puts a.__id__ 
puts b.__id__ 

그래서 그것뿐만 아니라 전달 다음 에 영향을 미친다.

한편 String#+ 메서드는 결합 된 문자열 을 보유하는 새 String 객체를 만듭니다. 이 메서드를 사용하면 반복기를 한 번 통과해도 다른 패스에는 효과가 없습니다.

1

키 = 워드 < < ""< < 도착 [인덱스 + 1]

문제는 캐릭터가 사용되는 다음 시간을 수정하므로 String#<<는 현재 위치에서 동작을 수행한다는 것이다. 반면에 String#+은 새 복사본을 반환합니다.

부작용이 큰 버그 원인이기 때문에 이상한 일이 아닙니다. 매우 중요한 성능상의 이유가없는 한 functional 접근 방식을 사용하면 더 나은 코드가 생성됩니다. 예를 들어, 그게이 패싯에서 each_consmap_by를 사용하여 작성 될 수있는 방법은 다음과 같습니다

words = 'I wish I may I wish I might'.split 
dictionary = words.each_cons(3).map_by do |word1, word2, word3| 
    ["#{word1} #{word2}", word3] 
end 
관련 문제