[ruby-on-rails] 루비가 참조 또는 값으로 전달됩니까?

@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변수를.

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



답변

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

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


답변

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


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


답변

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

(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 foo(bar)
  bar = 'reference'
end

baz = 'value'

foo(baz)

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


답변

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

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

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

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

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


답변

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

[3.8.1부터 : 객체 참조]

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

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

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


답변

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

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

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

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}"

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