루비가 참조 또는 값으로 전달됩니까?


248
@user.update_languages(params[:language][:language1], 
                       params[:language][:language2], 
                       params[:language][:language3])
lang_errors = @user.errors
logger.debug "--------------------LANG_ERRORS----------101-------------" 
                + lang_errors.full_messages.inspect

if params[:user]
  @user.state = params[:user][:state]
  success = success & @user.save
end
logger.debug "--------------------LANG_ERRORS-------------102----------" 
                + lang_errors.full_messages.inspect

if lang_errors.full_messages.empty?

@user객체는 메소드 의 lang_errors변수에 오류를 추가합니다 update_lanugages. 나는에 선방을 수행 할 때 @user오브젝트 나는 처음에 저장된 오류 잃을 lang_errors변수를.

내가하려고하는 것은 더 많은 해킹 일 것입니다 (작동하지 않는 것 같습니다). 변수 값이 씻겨지는 이유를 이해하고 싶습니다. 나는 참조로 패스를 이해하므로 값을 씻지 않고 해당 변수에 값을 보유하는 방법을 알고 싶습니다.


또한 복제 된 객체에서 해당 값을 유지할 수 있습니다.
Sid

1
Abe Voelker의 답변을 살펴보십시오. 그러나 이것에 대한 블록을 돌고 나면 여기에 내가 어떻게 말할 것입니다. 객체 Foo를 프로 시저에 전달하면 객체에 대한 참조 사본이 전달되고, bar, 값으로 전달됩니다. Foo가 가리키는 개체는 변경할 수 없지만 가리키는 개체의 내용은 변경할 수 있습니다. 따라서 배열을 전달하면 배열의 내용을 변경할 수 있지만 참조하는 배열은 변경할 수 없습니다. Foo의 다른 종속성을 망칠 염려없이 Foo의 메소드를 사용할 수있어서 좋았습니다.
bobbdelsol

답변:


244

전통적인 용어로 루비는 엄격하게 가치를 전달합니다 . 그러나 그것은 당신이 여기서 묻는 것이 아닙니다.

루비에는 순수하고 참조가 아닌 값에 대한 개념이 없으므로 메소드에 전달할 수 없습니다. 변수는 항상 객체에 대한 참조입니다. 아래에서 변하지 않는 객체를 얻으려면 전달 된 객체를 복제하거나 복제하여 다른 사람이 참조하지 않는 객체를 제공해야합니다. (이것은 방탄하지는 않지만 표준 복제 방법은 모두 얕은 복사를 수행하므로 복제본의 인스턴스 변수는 원본과 동일한 객체를 가리 킵니다. ivars에서 참조하는 객체가 변경되면 동일한 객체를 참조하므로 사본에 여전히 표시됩니다.)


88
루비는 pass-by-value 입니다. 그렇습니다. 엉덩이는 없습니다. 예외 없음. Ruby (또는 다른 언어)가 참조 기준 인지 또는 기준 인지 여부를 알고 싶다면 다음을 시도하십시오 def foo(bar) bar = 'reference' end; baz = 'value'; foo(baz); puts "Ruby is pass-by-#{baz}".
Jörg W Mittag

95
@ JörgWMittag : 예, 그러나 OP의 혼동은 단어의 엄격한 CS 의미에서 실제로 값으로 전달되거나 참조로 전달되지 않습니다. 그가 놓친 것은 당신이 전달하는 "값" 참조라는 것입니다. 나는 단지 "가치가 지나간다"고 말하는 것이 현명한 것이며 OP를 불쾌하게한다고 생각했다. 그것이 실제로 그가 의도 한 바가 아니기 때문이다. 그러나 미래의 독자들에게 중요하기 때문에 명확하게 해주셔서 감사합니다. (저는 항상 더 많은 정보를 포함하고 혼동하지 않는 사람들 사이에서 찢어졌습니다.)
Chuck

16
@Jorg에 동의하지 않습니다. 루비는 참조로 전달되며 참조 만 변경합니다. 대신 이것을 시도하십시오 : def foo (bar) bar.replace 'reference'end; baz = '값'; foo (baz); "Ruby is pass-
by-

15
@ pguardiario : 나는 그것이 정의의 문제 일뿐이라고 생각합니다. Jörg는 전통적인 컴퓨터 과학 정의를 사용하는 동안 개인적으로 제시 한 "통과 기준"의 정의를 사용하고 있습니다. 난 그냥 어떤 용어를 설명하는 것이 중요하다고 생각 - 물론, 내 상관 할 어떻게 단어를 사용하는 방법을 말할 것입니다 일반적으로는 수단. 전통적인 용어에서 루비는 값을 전달하지만 값 자체는 참조입니다. 본인과 OP가이를 참조로 전달하는 이유를 완전히 이해합니다. 이는 용어의 전통적인 의미가 아닙니다.

7
루비의 모든 것은 객체이므로, 루비는 적어도 C ++에서 사용되는 의미에서 가치를 지니거나 참조로 전달하지 않습니다. "객체 참조를 통한 전달"은 Ruby의 기능을 설명하는 더 좋은 방법 일 수 있습니다. 결국, 가장 좋은 방법은 이러한 용어 중 하나에 너무 많은 의미를 두지 않고 실제로 발생하는 동작을 잘 이해하는 것입니다.
David Winiecki

424

다른 답변자는 모두 정확하지만 친구가 나에게 이것을 설명하도록 요청했으며 실제로 루비가 변수를 처리하는 방법은 무엇입니까? 그래서 내가 작성한 간단한 그림 / 설명을 공유 할 것이라고 생각했습니다 (길이에 대한 사과 그리고 아마도 약간 단순화 된 것) :


Q1 : 새 변수 str를 값 'foo'?에 할당하면 어떻게됩니까 ?

str = 'foo'
str.object_id # => 2000

여기에 이미지 설명을 입력하십시오

A : 이 루비 인터프리터의 상태가 메모리 위치 str에있는 객체를 가리키는 레이블 이 생성 'foo'됩니다 2000.


Q2 : ?를 str사용하여 기존 변수 를 새 개체에 할당하면 어떻게됩니까 =?

str = 'bar'.tap{|b| puts "bar: #{b.object_id}"} # bar: 2002
str.object_id # => 2002

여기에 이미지 설명을 입력하십시오

A : 레이블은 str이제 다른 개체를 가리 킵니다.


Q3 : 새 변수 =를 할당하면 어떻게됩니까 str?

str2 = str
str2.object_id # => 2002

여기에 이미지 설명을 입력하십시오

A : 와 동일한 객체str2 를 가리키는 새 레이블 이 생성 됩니다 .str


Q4 : 개체가 참조 str하고 str2변경 하면 어떻게됩니까 ?

str2.replace 'baz'
str2 # => 'baz'
str  # => 'baz'
str.object_id # => 2002
str2.object_id # => 2002

여기에 이미지 설명을 입력하십시오

A : 두 레이블 모두 여전히 동일한 개체를 가리 키지 만 해당 개체 자체는 변경되었습니다 (내용이 다른 것으로 변경됨).


이것이 원래 질문과 어떤 관련이 있습니까?

기본적으로 Q3 / Q4에서 발생하는 것과 동일합니다. 이 메소드는 str2전달 된 변수 / 레이블 ( ) 의 자체 사본 을 가져옵니다 ( str). 레이블이 str 가리키는 객체를 변경할 수는 없지만 다른 객체 를 포함하도록 참조하는 객체 의 내용을 변경할 수 있습니다 .

str = 'foo'

def mutate(str2)
  puts "str2: #{str2.object_id}"
  str2.replace 'bar'
  str2 = 'baz'
  puts "str2: #{str2.object_id}"
end

str.object_id # => 2004
mutate(str) # str2: 2004, str2: 2006
str # => "bar"
str.object_id # => 2004

1
로버트 히튼은 또한이 최근 대해 블로그 : robertheaton.com/2014/07/22/...
마이클 레너에게

48

루비는 "객체 참조로 전달"을 사용합니다

(Python 용어 사용)

루비가 "값으로 전달"또는 "참조로 전달"을 사용한다고 말하는 것은 실제로 설명하기에 충분하지 않습니다. 요즘 대부분의 사람들이 알고 있듯이 그 용어 ( "value"대 "reference")는 C ++에서 온 것입니다.

C ++에서 "pass by value"는 함수가 변수의 사본을 가져오고 사본을 변경해도 원본이 변경되지 않음을 의미합니다. 객체에도 마찬가지입니다. 값으로 객체 변수를 전달하면 전체 객체 (모든 멤버 포함)가 복사되고 멤버를 변경해도 원래 객체의 해당 멤버는 변경되지 않습니다. (값으로 포인터를 전달하지만 Ruby에 포인터가 없으면 AFAIK와 다릅니다.)

class A {
  public:
    int x;
};

void inc(A arg) {
  arg.x++;
  printf("in inc: %d\n", arg.x); // => 6
}

void inc(A* arg) {
  arg->x++;
  printf("in inc: %d\n", arg->x); // => 1
}

int main() {
  A a;
  a.x = 5;
  inc(a);
  printf("in main: %d\n", a.x); // => 5

  A* b = new A;
  b->x = 0;
  inc(b);
  printf("in main: %d\n", b->x); // => 1

  return 0;
}

산출:

in inc: 6
in main: 5
in inc: 1
in main: 1

C ++에서 "pass by reference"는 함수가 원래 변수에 액세스하는 것을 의미합니다. 완전히 새로운 리터럴 정수를 할당하면 원래 변수도 그 값을 갖습니다.

void replace(A &arg) {
  A newA;
  newA.x = 10;
  arg = newA;
  printf("in replace: %d\n", arg.x);
}

int main() {
  A a;
  a.x = 5;
  replace(a);
  printf("in main: %d\n", a.x);

  return 0;
}

산출:

in replace: 10
in main: 10

인수가 객체가 아닌 경우 Ruby는 값을 기준으로 전달합니다 (C ++ 의미). 그러나 루비에서는 모든 것이 객체이므로 루비에서는 C ++ 의미의 가치가 실제로 없습니다.

Ruby에서는 "객체 참조를 통한 전달"(Python 용어를 사용하기 위해)이 사용됩니다.

  • 함수 내에서 객체의 모든 멤버에 새 값을 할당 할 수 있으며 이러한 변경 사항은 함수가 반환 된 후에도 유지됩니다. *
  • 함수 내에서 완전히 새로운 객체를 변수에 할당하면 변수가 이전 객체 참조를 중지합니다. 그러나 함수가 반환 된 후에도 원래 변수는 여전히 이전 개체를 참조합니다.

따라서 루비는 C ++ 의미에서 "참조로 전달"을 사용하지 않습니다. 그럴 경우, 함수 내부의 변수에 새 객체를 할당하면 함수가 반환 된 후 이전 객체가 잊혀 질 수 있습니다.

class A
  attr_accessor :x
end

def inc(arg)
  arg.x += 1
  puts arg.x
end

def replace(arg)
  arg = A.new
  arg.x = 3
  puts arg.x
end

a = A.new
a.x = 1
puts a.x  # 1

inc a     # 2
puts a.x  # 2

replace a # 3
puts a.x  # 2

puts ''

def inc_var(arg)
  arg += 1
  puts arg
end

b = 1     # Even integers are objects in Ruby
puts b    # 1
inc_var b # 2
puts b    # 1

산출:

1
2
2
3
2

1
2
1

* 따라서 Ruby에서 함수 내부의 오브젝트를 수정하고 함수가 리턴 될 때 해당 변경 사항을 잊어 버리려면 임시로 사본을 변경하기 전에 오브젝트를 명시 적으로 복사해야합니다.


당신의 대답은 최고입니다. 또한 간단한 예를 게시 싶어 def ch(str) str.reverse! end; str="abc"; ch(str); puts str #=> "cba"
fangxing

이것이 정답입니다! 이것은 매우 잘 또한 여기에서 설명 : robertheaton.com/2014/07/22/...를 . 그러나 여전히 이해하지 못하는 것은 이것 def foo(bar) bar = 'reference' end; baz = 'value'; foo(baz); puts "Ruby is pass-by-#{baz}"입니다. "Ruby is pass-by-value"를 인쇄합니다. 그러나 내부 변수 foo가 다시 할당됩니다. 경우 bar배열 될 재 할당이 적용되지 것 baz. 왜?
haffla

귀하의 질문을 이해하지 못합니다. 나는 당신이 여기에 의견을 묻는 대신 완전히 새로운 질문을해야한다고 생각합니다.
David Winiecki

42

루비가 참조 또는 값으로 전달됩니까?

루비는 가치에 의해 전달됩니다. 항상. 예외 없음. 그렇습니다. 엉덩이는 없습니다.

다음은 그 사실을 보여주는 간단한 프로그램입니다.

def foo(bar)
  bar = 'reference'
end

baz = 'value'

foo(baz)

puts "Ruby is pass-by-#{baz}"
# Ruby is pass-by-value

15
즉,의 실수, 아니다 - @DavidJ : "여기서 실수는 로컬 매개 변수 (메모리에서 새로운 위치로 지적) 재 할당된다는 점이다" 정의통과에 의한 값 . 루비가 참조로 전달 된 경우, 수신자의 로컬 메소드 인수 바인딩에 재 할당하면 호출자의 로컬 변수 바인딩도 다시 지정됩니다. 그렇지 않았다. 어고, 루비는 가치를 전달합니다. 변경 가능한 값을 변경하면 값 변경이 완전히 관련이 없다는 사실이 변경 가능한 상태의 작동 방식입니다. 루비는 순수한 기능 언어가 아닙니다.
Jörg W Mittag

5
"값별 패스"의 진정한 정의를 지키는 Jörg에게 감사합니다. 루비가 항상 값을 지나치지 만 값이 실제로 참조 일 때 그것은 우리의 두뇌를 분명히 녹입니다.
Douglas

9
이것은 궤변입니다. "값에 의한 통과"와 "참조에 의한 통과"의 실질적인 구별은 의미 론적이지 구문적인 것이 아니다. C 배열이 값을 전달한다고 말할 수 있습니까? 물론 배열의 이름을 함수에 전달할 때도 불변 포인터를 전달하고 포인터가 참조하는 데이터 만 변경할 수 있습니다. 분명히 루비의 값 유형은 값으로 전달되고 참조 유형은 참조로 전달됩니다.
dodgethesteamroller

3
@dodgethesteamroller : Ruby와 C는 모두 가치가 있습니다. 항상. 예외는 없지만 엉덩이는 없습니다. 값으로 전달과 참조로 전달의 차이점은 참조가 가리키는 값을 전달하는지 또는 참조를 전달하는지입니다. C는 항상 값을 전달 하지만 참조 는 전달 하지 않습니다 . 값은 포인터 일 수도 있고 아닐 수도 있지만 , 값이 무엇인지 는 처음에 전달되는지 여부와 관련이 없습니다. 루비는 항상 값을 전달 하지만 참조는 하지 않습니다 . 이 값은 항상 포인터이지만 다시는 관련이 없습니다.
Jörg W Mittag

44
엄밀히 말하면하면서 대답은, 사실 , 매우 아니다 유용합니다 . 전달되는 값이 항상 포인터라는 사실은 관련 이 없습니다 . 배우려는 사람들에게는 혼란의 원천이며, 당신의 대답은 그 혼란에 도움이되지 않습니다.

20

루비는 엄격한 의미에서 가치에 의해 전달되지만 가치는 참조입니다.

이것을 " 값에 의한 통과 기준 "이라고 부를 수 있습니다 . 이 기사는 내가 읽은 가장 좋은 설명을 가지고 있습니다 : http://robertheaton.com/2014/07/22/is-ruby-pass-by-reference-or-pass-by-value/

값별 패스 기준은 다음과 같이 간단히 설명 할 수 있습니다.

함수는 호출자가 사용하는 것과 동일한 객체에 대한 참조를 수신하고 메모리에 액세스합니다. 그러나 호출자가이 개체를 저장하는 상자는받지 않습니다. 값별 전달에서와 같이이 함수는 자체 상자를 제공하고 자체 변수를 새로 만듭니다.

결과적인 동작은 실제로는 참조 기준 값과 값 기준의 고전적 정의의 조합입니다.


"값으로 패스 참조"는 루비의 인수 전달을 설명하는 데 사용하는 것과 같은 문구입니다. 가장 정확하고 간결한 문구라고 생각합니다.
Wayne Conrad

16

이미 훌륭한 답변이 있지만 주제에 대한 한 쌍의 권위에 대한 정의를 게시하고 싶지만 누군가가 Matz (루비의 창조자)와 David Flanagan이 우수한 O'Reilly 책에서 의미 한 것을 설명하기를 바랍니다. 루비 프로그래밍 언어 .

[3.8.1부터 : 객체 참조]

Ruby에서 객체를 메소드에 전달하면 메소드에 전달되는 객체 참조입니다. 그것은 객체 자체가 아니며 객체에 대한 참조에 대한 참조가 아닙니다. 이것을 말하는 또 다른 방법은 method 인수가 reference가 아닌 value 로 전달되지만 전달 된 값 은 객체 참조라는 것입니다.

객체 참조는 메소드에 전달되므로 메소드는 해당 참조를 사용하여 기본 객체를 수정할 수 있습니다. 그런 다음 메소드가 리턴 될 때 이러한 수정 사항을 볼 수 있습니다.

이것은 마지막 단락까지, 특히 마지막 문장 까지 나에게 의미가 있습니다. 이것은 최선의 오해의 소지가 있고 혼란스러운 상황입니다. 어떤 식 으로든 값으로 전달 된 참조를 수정하면 기본 개체를 어떻게 변경할 수 있습니까?


1
참조가 수정되지 않기 때문에; 기본 개체입니다.
dodgethesteamroller

1
객체가 변경 가능하기 때문입니다. 루비는 순전히 기능적인 언어가 아닙니다. 이는 참조 별 통과 대 값별로 완전히 직교입니다 (순전히 기능적인 언어에서는 값별 및 참조 별 기준이 항상 동일한 결과를 산출하므로 언어가 당신도 모르게 둘 중 하나 또는 둘 다를 사용하십시오).
Jörg W Mittag

함수의 변수 할당 대신 해시를 함수에 전달하고 병합하는 경우를 예로들 수 있습니다. 전달 된 해시 원래 해시는 수정되었습니다.
elc

16

루비가 참조 또는 값으로 전달됩니까?

루비는 참조로 전달됩니다. 항상. 예외 없음. 그렇습니다. 엉덩이는 없습니다.

다음은 그 사실을 보여주는 간단한 프로그램입니다.

def foo(bar)
  bar.object_id
end

baz = 'value'

puts "#{baz.object_id} Ruby is pass-by-reference #{foo(baz)} because object_id's (memory addresses) are always the same ;)"

=> 2279146940 루비는 object_id (메모리 주소)가 항상 동일하기 때문에 참조에 의한 전달 2279146940입니다.)

def bar(babar)
  babar.replace("reference")
end

bar(baz)

puts "some people don't realize it's reference because local assignment can take precedence, but it's clearly pass-by-#{baz}"

=> 일부 사람들은 로컬 할당이 우선 할 수 있기 때문에 참조를 인식하지 못하지만 분명히 참조로 전달됩니다.


이것은 유일한 정답이며 몇 가지 멋진 문제를 제시합니다. Try a = 'foobar'; b = a; b [5] = 'z', a와 b가 모두 수정됩니다.
Martijn

2
@Martijn : 당신의 주장이 완전히 유효하지는 않습니다. 진술로 코드 진술을 살펴 봅시다. a = 'foobar'는 'foobar'를 가리키는 새 참조를 만듭니다. b = a는 a와 동일한 데이터에 대한 두 번째 참조를 만듭니다. b [5] = 'z'는 b에 의해 참조되는 값의 6 번째 문자를 'z'로 변경합니다 (우연히 a에 의해 참조되는 값이 변경됨). 그렇기 때문에 "둘 다 수정"되거나 더 정확하게 "두 변수가 모두 참조하는 값이 수정"됩니다.
Lukas_Skywalker

2
당신은 당신의 bar방법 에서 참조로 아무것도하지 않습니다 . 단순히 참조 자체가 아닌 참조가 가리키는 오브젝트를 수정하고 있습니다. 루비에서 참조를 수정하는 유일한 방법은 할당입니다. 메소드는 오브젝트에서만 호출 될 수 있고 참조는 Ruby에서 오브젝트가 아니기 때문에 Ruby에서 메소드를 호출하여 참조를 수정할 수 없습니다. 코드 샘플은 Ruby가 가변 상태를 공유했음을 보여 주지만 (여기에서는 논의되지 않음), 값별 값과 참조 별 값의 구분을 밝히는 것은 없습니다.
Jörg W Mittag

1
언어가 "참조에 의한 통과"인지 묻는 사람은 일반적으로 함수에 무언가를 전달할 때 알고 싶어하며 함수가 언어를 수정하면 함수 외부에서 언어가 수정됩니다. 루비의 대답은 '예'입니다. 이 답변은 @ JörgWMittag의 답변이 매우 도움이되지 않음을 증명하는 데 도움이됩니다.
Toby 1 Kenobi

@ Toby1Kenobi : 일반적으로 널리 사용되는 정의와 다른 "값별"이라는 용어에 대한 개인 정의를 자유롭게 사용할 수 있습니다. 그러나 그렇게한다면 사람들이 혼란스러워 할 수 있도록 준비해야합니다. 특히 다른 사람들이하는 것과는 전혀 다른 개념으로, 매우 다른 것에 대해 이야기하고 있다는 사실을 공개하지 않는 경우에는 더욱 그렇습니다. 특히, "by-by-by-reference"는 전달 된 "무언가"가 수정 될 수 있는지의 여부와 관련 이 없으며 오히려 "무언가"가 무엇인지 , 특히 그것이 참조인지에 관한 것입니다.
Jörg W Mittag

8

매개 변수는 원래 참조의 사본입니다. 따라서 값을 변경할 수 있지만 원래 참조는 변경할 수 없습니다.


2

이 시도:--

1.object_id
#=> 3

2.object_id
#=> 5

a = 1
#=> 1
a.object_id
#=> 3

b = 2
#=> 2
b.object_id
#=> 5

식별자 a는 값 객체 1에 대한 object_id 3을 포함하고 식별자 b는 값 객체 2에 대한 object_id 5를 포함합니다.

이제 이렇게하십시오 :-

a.object_id = 5
#=> error

a = b
#value(object_id) at b copies itself as value(object_id) at a. value object 2 has object_id 5
#=> 2

a.object_id 
#=> 5

이제 a와 b는 모두 값 객체 2를 나타내는 동일한 object_id 5를 포함합니다. 따라서 Ruby 변수에는 값 객체를 나타내는 object_id가 포함됩니다.

다음을 수행하면 오류가 발생합니다.

c
#=> error

그러나 이렇게하면 오류가 발생하지 않습니다.

5.object_id
#=> 11

c = 5
#=> value object 5 provides return type for variable c and saves 5.object_id i.e. 11 at c
#=> 5
c.object_id
#=> 11 

a = c.object_id
#=> object_id of c as a value object changes value at a
#=> 11
11.object_id
#=> 23
a.object_id == 11.object_id
#=> true

a
#=> Value at a
#=> 11

여기서 식별자 a는 객체 ID가 23 인 값 객체 11을 반환합니다. 즉, object_id 23은 식별자 a에 있습니다. 이제 메서드를 사용하여 예제를 봅니다.

def foo(arg)
  p arg
  p arg.object_id
end
#=> nil
11.object_id
#=> 23
x = 11
#=> 11
x.object_id
#=> 23
foo(x)
#=> 11
#=> 23

foo의 arg는 x의 반환 값으로 할당됩니다. 그것은 인수가 값 11에 의해 전달되고 값 11 자체가 객체가 고유 한 객체 ID 23을 가지고 있음을 분명히 보여줍니다.

이제 이것도보십시오 :-

def foo(arg)
  p arg
  p arg.object_id
  arg = 12
  p arg
  p arg.object_id
end

#=> nil
11.object_id
#=> 23
x = 11
#=> 11
x.object_id
#=> 23
foo(x)
#=> 11
#=> 23
#=> 12
#=> 25
x
#=> 11
x.object_id
#=> 23

여기서, 식별자 arg는 먼저 11을 참조하는 object_id 23을 포함하고 value object 12를 사용한 내부 할당 후에 object_id 25를 포함합니다. 그러나 호출 메소드에 사용 된 식별자 x가 참조하는 값은 변경하지 않습니다.

따라서 Ruby는 값으로 전달되며 Ruby 변수는 값을 포함하지 않지만 값 객체에 대한 참조를 포함합니다.


1

루비는 해석됩니다. 변수는 데이터에 대한 참조이지만 데이터 자체는 아닙니다. 이를 통해 다른 유형의 데이터에 동일한 변수를 사용할 수 있습니다.

lhs = rhs의 할당은 데이터가 아니라 rhs에 대한 참조를 복사합니다. 이는 C와 같은 다른 언어에서는 할당이 rhs에서 lhs로 데이터 복사를 수행합니다.

따라서 함수 호출의 경우 x와 같이 전달 된 변수는 실제로 함수의 로컬 변수에 복사되지만 x는 참조입니다. 그런 다음 동일한 데이터를 참조하는 두 개의 참조 사본이 있습니다. 하나는 호출자에 있고 다른 하나는 함수에 있습니다.

함수에 할당하면 함수의 x 버전에 대한 새로운 참조가 복사됩니다. 이 후에도 발신자의 x 버전은 변경되지 않습니다. 여전히 원본 데이터에 대한 참조입니다.

반대로, x에서 .replace 메소드를 사용하면 루비가 데이터 복사를 수행합니다. 새 할당 전에 바꾸기를 사용하면 실제로 발신자도 해당 버전의 데이터 변경 사항을 볼 수 있습니다.

마찬가지로, 전달 된 변수에 대해 원래 참조가 그대로있는 한 인스턴스 변수는 호출자가 보는 것과 동일합니다. 객체의 프레임 워크 내에서 인스턴스 변수는 호출자가 제공하거나 클래스가 전달 된 함수에 설정되어 있는지 여부에 관계없이 항상 최신 참조 값을 갖습니다.

'='에 대한 혼동으로 인해 '값으로 호출'또는 '참조로 호출'이 여기에서 혼동됩니다. 컴파일 된 언어에서 '='는 데이터 사본입니다. 이 해석 언어에서 '='는 참조 사본입니다. 이 예에서는 참조가 전달 된 다음 참조 사본이 '='이지만 참조로 전달 된 원본을 방해하고 '='가 데이터 사본 인 것처럼 이야기합니다.

정의와 일관성을 유지하려면 데이터 복사본이므로 '.replace'를 유지해야합니다. '.replace'의 관점에서 우리는 이것이 실제로 참조로 전달된다는 것을 알 수 있습니다. 또한 디버거를 살펴보면 변수가 참조이므로 참조가 전달되는 것을 볼 수 있습니다.

그러나 '='를 참조 프레임으로 유지해야 할 경우 실제로 할당 될 때까지 전달 된 데이터를 볼 수 있으며 호출자의 데이터가 변경되지 않은 채 할당 후에는 더 이상 볼 수 없습니다. 행동 수준에서 이것은 전달 된 값을 복합적인 것으로 간주하지 않는 한 값으로 전달됩니다. 우리는 단일 할당에서 다른 부분을 변경하면서 그 부분을 유지할 수 없기 때문에 (그 할당과 같이) 참조를 변경하면 원본이 범위를 벗어납니다). 개체의 변수는 모든 변수와 마찬가지로 참조가되는 경우도 있습니다. 따라서 우리는 '가치 별 기준'을 전달하는 것에 대해 이야기하고 관련 삭감을 사용해야합니다.


1

값을 원래 값으로 변경하기 위해 "바꾸기"방법을 사용할 필요는 없습니다. 해시에 대한 해시 값 중 하나를 할당하면 원래 값이 변경됩니다.

def my_foo(a_hash)
  a_hash["test"]="reference"
end;

hash = {"test"=>"value"}
my_foo(hash)
puts "Ruby is pass-by-#{hash["test"]}"

내가 찾은 또 다른 것. 숫자 유형을 전달하는 경우 모든 숫자 유형을 변경할 수 없으므로 값으로 전달됩니다. 위의 문자열에서 작동 한 바꾸기 기능은 숫자 유형에 대해 작동하지 않습니다.
Don Carr

1
Two references refer to same object as long as there is no reassignment. 

동일한 객체의 업데이트는 여전히 동일한 메모리에 있으므로 새 메모리를 참조하지 않습니다. 다음은 몇 가지 예입니다.

    a = "first string"
    b = a



    b.upcase! 
    => FIRST STRING
    a
    => FIRST STRING

    b = "second string"


a
    => FIRST STRING
    hash = {first_sub_hash: {first_key: "first_value"}}
first_sub_hash = hash[:first_sub_hash]
first_sub_hash[:second_key] = "second_value"

    hash
    => {first_sub_hash: {first_key: "first_value", second_key: "second_value"}}

    def change(first_sub_hash)
    first_sub_hash[:third_key] = "third_value"
    end

    change(first_sub_hash)

    hash
    =>  {first_sub_hash: {first_key: "first_value", second_key: "second_value", third_key: "third_value"}}

0

네,하지만 ....

루비는 객체에 대한 참조를 전달하고 루비의 모든 것이 객체이므로 참조로 전달한다고 말할 수 있습니다.

나는 그것이 가치에 의한 통과라고 주장하는 게시물에 동의하지 않습니다. 그것은 저에게 pemantic, symantic 게임처럼 보입니다.

그러나, 루비가 대부분의 오퍼레이션을 "out of the box"(예 : 문자열 오퍼레이션)로 제공하기 때문에 사실상 동작을 "숨 깁니다":

> astringobject = "lowercase"

> bstringobject = astringobject.upcase
> # bstringobject is a new object created by String.upcase

> puts astringobject
lowercase

> puts bstringobject
LOWERCASE

즉, 루비가 "값으로 전달"되는 것처럼 보이기 때문에 원래 오브젝트가 변경되지 않은 채로 남아 있습니다.

물론 자신의 클래스를 디자인 할 때이 동작의 세부 사항을 이해하는 것은 기능적 동작, 메모리 효율성 및 성능 모두에 중요합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.