Ruby에서 문자열을 만들 때 삽 연산자 (<<)가 더하기 같음 (+ =)보다 선호되는 이유는 무엇입니까?


161

Ruby Koans를 통해 작업하고 있습니다.

about_strings.rbtest_the_shovel_operator_modifies_the_original_stringKoan 에는 다음 주석이 포함되어 있습니다.

루비 프로그래머는 문자열을 만들 때 더하기 같음 연산자 (+ =)보다 삽 연산자 (<<)를 선호하는 경향이 있습니다. 왜?

내 생각 엔 속도와 관련이 있지만 삽 운영자가 더 빨라지게 만드는 후드 아래의 행동을 이해하지 못합니다.

누군가가이 기본 설정의 세부 사항을 설명해 주시겠습니까?


4
shovel 연산자는 새 String 객체를 만드는 대신 String 객체를 수정합니다 (메모리 비용). 구문이 예쁘지 않습니까? cf. Java 및 .NET에는 StringBuilder 클래스가 있습니다
Colonel Panic

답변:


265

증명:

a = 'foo'
a.object_id #=> 2154889340
a << 'bar'
a.object_id #=> 2154889340
a += 'quux'
a.object_id #=> 2154742560

따라서 <<새 문자열을 만드는 대신 원래 문자열을 변경합니다. 그 이유는 루비 에서 할당 인 ( 다른 연산자 에도 동일하게 적용되는 a += b) 구문상의 속기 이기 때문입니다 . 반면에 수신자를 제자리에서 변경 하는 별칭입니다 .a = a + b<op>=<<concat()


3
고마워, noodl! 그래서 본질적으로 <<는 새로운 객체를 생성하지 않기 때문에 더 빠릅니다.
erinbrown 2011 년

1
이 벤치 마크 는 사용하는 Array#join것보다 느립니다 <<.
Andrew Grimm

5
EdgeCase 직원 중 한 명이 성능 수치에 대한 설명을 게시했습니다. Strings에 대해 조금 더 알아보기
Cincinnati Joe

8
@CincinnatiJoe 링크 위의 여기, 깨져 보이는 새로운 하나입니다 이상의 문자열에 대해 조금이
jasoares

자바 사용자의 경우 : Ruby의 '+'연산자는 StringBuilder 객체를 통한 추가에 해당하고 '<<'는 String 객체의 연결에 해당합니다
nanosoft

82

성능 증명 :

#!/usr/bin/env ruby

require 'benchmark'

Benchmark.bmbm do |x|
  x.report('+= :') do
    s = ""
    10000.times { s += "something " }
  end
  x.report('<< :') do
    s = ""
    10000.times { s << "something " }
  end
end

# Rehearsal ----------------------------------------
# += :   0.450000   0.010000   0.460000 (  0.465936)
# << :   0.010000   0.000000   0.010000 (  0.009451)
# ------------------------------- total: 0.470000sec
# 
#            user     system      total        real
# += :   0.270000   0.010000   0.280000 (  0.277945)
# << :   0.000000   0.000000   0.000000 (  0.003043)

73

Ruby를 첫 프로그래밍 언어로 배우고있는 한 친구가 Ruby Koans 시리즈의 Strings in Ruby를 살펴보면서 동일한 질문을했습니다. 나는 다음 비유를 사용하여 그에게 설명했다.

반쯤 가득 찬 물 한 잔이 있고 잔을 다시 채워야합니다.

첫 번째 방법은 새 잔을 가져다가 수도꼭지에서 물로 반쯤 채운 다음이 반쯤 가득 찬 잔으로 음료수 잔을 다시 채우는 것입니다. 잔을 리필해야 할 때마다이 작업을 수행합니다.

두 번째 방법은 반쯤 가득 찬 유리 잔을 가져다가 수도꼭지에서 바로 물로 채우는 것입니다.

하루가 끝나면 잔을 다시 채울 때마다 새 잔을 선택하면 청소할 잔이 더 많아집니다.

삽 연산자와 더하기 같음 연산자에도 동일하게 적용됩니다. 게다가 동등한 작업자는 유리를 다시 채울 필요가있을 때마다 새로운 '유리'를 선택하는 반면 삽 작업자는 동일한 유리를 가져다가 다시 채 웁니다. 하루가 끝나면 Plus equal 연산자를위한 더 많은 '유리'컬렉션.


3
훌륭한 비유는 그것을 좋아했습니다.
GMA

5
훌륭한 비유이지만 끔찍한 결론입니다. 다른 사람이 안경을 닦는다는 점을 추가해야하므로 걱정할 필요가 없습니다.
Filip Bartuzi 2015

1
훌륭한 비유, 좋은 결론에 도달했다고 생각합니다. 나는 누가 유리를 청소해야 하는가가 아니라 사용되는 유리의 수에 관한 것보다 더 중요하다고 생각합니다. 특정 응용 프로그램이 컴퓨터의 메모리 한계를 밀어 내고 해당 컴퓨터가 한 번에 특정 수의 안경 만 청소할 수 있다고 상상할 수 있습니다.
Charlie L

11

이것은 오래된 질문이지만 방금 만났고 기존 답변에 완전히 만족하지 않습니다. 삽 <<이 연결 + =보다 빠르다는 점에 대해 많은 좋은 점이 있지만 의미 론적 고려도 있습니다.

@noodl의 대답은 <<가 기존 개체를 수정하는 반면 + =는 새 개체를 만듭니다. 따라서 문자열에 대한 모든 참조가 새 값을 반영하도록 할 것인지 아니면 기존 참조를 그대로두고 로컬에서 사용할 새 문자열 값을 만들 것인지 고려해야합니다. 업데이트 된 값을 반영하기 위해 모든 참조가 필요한 경우 <<를 사용해야합니다. 다른 참조를 그대로 두려면 + =를 사용해야합니다.

매우 일반적인 경우는 문자열에 대한 참조가 하나뿐입니다. 이 경우 의미 적 차이는 중요하지 않으며 속도 때문에 <<를 선호하는 것이 당연합니다.


10

더 빠르기 때문에 / 문자열의 복사본을 만들지 않기 때문에 <-> 가비지 수집기를 실행할 필요가 없습니다.


위의 답변이 더 자세한 정보를 제공하지만 이것은 완전한 답변을 위해 함께 모은 유일한 것입니다. 여기서 핵심은 "문자열 만들기"라는 표현의 의미로, 원래 문자열이 필요하지 않거나 필요하지 않음을 의미합니다.
Drew Verlee

이 대답은 잘못된 전제를 기반으로합니다. 단기 객체를 할당하고 해제하는 것은 중간 정도의 괜찮은 현대 GC에서 본질적으로 무료입니다. 적어도 C의 스택 할당만큼 빠르며 malloc/ 보다 훨씬 빠릅니다 free. 또한 더 현대적인 Ruby 구현은 객체 할당과 문자열 연결을 완전히 최적화 할 것입니다. OTOH, 돌연변이 객체는 GC 성능에 끔찍합니다.
Jörg W Mittag

5

답변 커버의 대부분 동안 +=이 새 복사본을 만들기 때문에 느린, 그 마음을 유지하는 것이 중요 +=하고 << 없는 상호 교환! 서로 다른 경우에 각각을 사용하려고합니다.

를 사용 <<하면을 가리키는 모든 변수도 변경됩니다 b. 여기서 우리는 a원하지 않을 때도 돌연변이를 일으 킵니다 .

2.3.1 :001 > a = "hello"
 => "hello"
2.3.1 :002 > b = a
 => "hello"
2.3.1 :003 > b << " world"
 => "hello world"
2.3.1 :004 > a
 => "hello world"

때문에 +=새 복사본을 만들어, 그것은 또한 변경이 가리키는 된 모든 변수를 떠난다.

2.3.1 :001 > a = "hello"
 => "hello"
2.3.1 :002 > b = a
 => "hello"
2.3.1 :003 > b += " world"
 => "hello world"
2.3.1 :004 > a
 => "hello"

이 구별을 이해하면 루프를 다룰 때 많은 두통을 줄일 수 있습니다!


2

귀하의 질문에 대한 직접적인 대답은 아니지만 The Fully Upturned Bin은 항상 제가 좋아하는 Ruby 기사 중 하나였습니다. 또한 가비지 수집과 관련된 문자열에 대한 정보도 포함합니다.


팁 주셔서 감사합니다, 마이클! 아직 Ruby에서 그렇게 멀어지지는 않았지만 앞으로는 확실히 유용 할 것입니다.
erinbrown 2011 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.