Проходит ли Ruby по ссылке или по стоимости?

@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.

Хотя то, что я пытаюсь сделать, было бы скорее взломом (который, похоже, не работает). Я хотел бы понять, почему переменные значения вымываются. Я понимаю, что передаю по ссылке, поэтому я хотел бы знать, как значение может храниться в этой переменной без размывания.

Ответ 1

В традиционной терминологии Ruby строго передается по значению. Но это не то, о чем вы здесь спрашиваете.

Ruby не имеет понятия чистого, не ссылочного значения, поэтому вы, конечно же, не можете передать его методу. Переменные всегда ссылаются на объекты. Чтобы получить объект, который не будет меняться из-под вас, вам необходимо выполнить дуплекс или клонировать переданный вами объект, тем самым предоставив объект, к которому никто не имеет ссылки. (Даже это не является пуленепробиваемым, однако - оба стандартных метода клонирования выполняют мелкую копию, поэтому переменные экземпляра клона все еще указывают на те же объекты, что и оригиналы. Если объекты, на которые ссылаются ivars, мутируют, это будет все еще отображаются в копии, поскольку она ссылается на одни и те же объекты.)

Ответ 2

Другие ответчики все правильны, но друг попросил меня объяснить это ему, и то, что это действительно сводится к тому, как Ruby обрабатывает переменные, поэтому я решил поделиться некоторыми простыми картинками/объяснениями, которые я написал для него (извинения для длины и, вероятно, некоторого упрощения):


Q1: Что происходит, когда вы назначаете новую переменную str значению 'foo'?

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

enter image description here

A: Создается метка с именем str, которая указывает на объект 'foo', который для состояния этого интерпретатора Ruby находится в ячейке памяти 2000.


Q2: Что произойдет, если назначить существующую переменную str новому объекту с помощью =?

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

enter image description here

A: метка str теперь указывает на другой объект.


Q3: Что происходит, когда вы назначаете новую переменную = на str?

str2 = str
str2.object_id # => 2002

enter image description here

A: Создается новый ярлык с именем str2, который указывает на тот же объект, что и str.


Q4: Что произойдет, если объект, на который ссылаются str и str2, будет изменен?

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

enter image description here

A: Обе метки по-прежнему указывают на один и тот же объект, но сам этот объект мутировал (его содержимое изменилось как нечто другое).


Как это относится к исходному вопросу?

Это в основном то же самое, что и в Q3/Q4; метод получает свою собственную частную копию переменной /label (str2), которая передается ей (str). Он не может изменить тот объект, на который указывает метка str, но может изменить содержимое объекта, для которого они ссылаются, содержать else:

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

Ответ 3

Проходит ли Ruby по ссылке или по значению?

Ruby - это пропускная способность. Всегда. Без исключений. Нет, если. Нет, но.

Вот простая программа, которая демонстрирует этот факт:

def foo(bar)
  bar = 'reference'
end

baz = 'value'

foo(baz)

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

Ответ 4

Ruby использует "pass by object reference"

(с использованием терминов Python.)

Говорить, что Ruby использует "pass by value" или "pass by reference", на самом деле недостаточно описателен, чтобы быть полезным. Я думаю, что большинство людей знают это в наши дни, эта терминология ( "значение" и "ссылка" ) происходит от С++.

В С++ "передать по значению" означает, что функция получает копию переменной, а любые изменения в копии не меняют оригинал. Это верно и для объектов. Если вы передаете объектную переменную по значению, то весь объект (включая все его члены) будет скопирован, и любые изменения членов не изменят эти элементы на исходном объекте. (Это отличается, если вы передаете указатель по значению, но 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

В С++ "передать по ссылке" означает, что функция получает доступ к исходной переменной. Он может назначить целое новое целое число буквально, и исходная переменная также будет иметь это значение.

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 использует pass by value (в смысле С++), если аргумент не является объектом. Но в Ruby все является объектом, поэтому в языке С++ в Ruby действительно нет пропуска по значению.

В Ruby используется "pass by object reference" (для использования терминологии Python):

  • Внутри функции любой член объекта может иметь новые значения, назначенные им, и эти изменения будут сохраняться после возвращения функции. *
  • Внутри функции назначение целого нового объекта переменной приводит к тому, что переменная перестает ссылаться на старый объект. Но после возвращения функции исходная переменная все равно будет ссылаться на старый объект.

Поэтому Ruby не использует "pass by reference" в смысле С++. Если это так, то присвоение нового объекта переменной внутри функции приведет к тому, что старый объект будет забыт после возвращения функции.

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, если вы хотите изменить объект внутри функции, но забудьте о тех изменениях, когда функция вернется, тогда вы должны явно сделать копию объекта перед внесением временных изменений в копию.

Ответ 5

Есть уже некоторые отличные ответы, но я хочу опубликовать определение пары властей по этому вопросу, но также надеясь, что кто-то может объяснить, что сказали властям Мац (создатель Ruby) и Дэвид Фланаган в их отличном O ' Reilly book, Язык программирования Ruby.

[из 3.8.1: Ссылки на объекты]

Когда вы передаете объект методу в Ruby, это ссылка на объект, передаваемая методу. Это не сам объект, и это не ссылка на ссылку на объект. Другой способ сказать, что аргументы метода передаются по значению, а не по ссылке, но что переданные значения являются объектными ссылками.

Поскольку ссылки на объекты передаются методам, методы могут использовать эти ссылки для изменения базового объекта. Эти изменения затем видны, когда метод возвращается.

Это все имеет смысл для меня до последнего абзаца, и особенно это последнее предложение. Это в лучшем случае вводит в заблуждение и в худшем случае путается. Как каким-либо образом могут быть внесены изменения в эту ссылку на значение по умолчанию?

Ответ 6

Проходит ли Ruby по ссылке или по значению?

Ruby является передачей по ссылке. Всегда. Без исключений. Нет, если. Нет, но.

Вот простая программа, которая демонстрирует этот факт:

def foo(bar)
  bar.object_id
end

baz = 'value'

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

= > 2279146940 Ruby является передачей по ссылке 2279146940, потому что object_id (адреса памяти) всегда одинаковы;)

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

bar(baz)

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

= > некоторые люди не понимают, что это ссылка, потому что локальное назначение может иметь приоритет, но оно явно передается по ссылке

Ответ 7

Ruby - это пропускная способность в строгом смысле слова, НО значения являются ссылками.

Это можно назвать " pass-reference-by-value". В этой статье есть лучшее объяснение, которое я прочитал: http://robertheaton.com/2014/07/22/is-ruby-pass-by-reference-or-pass-by-value/

Ссылка на ссылку по ссылке может быть кратко объяснена следующим образом:

Функция получает ссылку (и будет получать доступ) к тому же объекту в памяти, который используется вызывающим. Однако он не получает поле, в котором вызывающий объект хранит этот объект; как и в переменной pass-value-by-value, функция предоставляет свой собственный поле и создает для себя новую переменную.

Полученное поведение на самом деле представляет собой комбинацию классических определений pass-by-reference и pass-by-value.

Ответ 8

Параметры - это копия исходной ссылки. Таким образом, вы можете изменять значения, но не можете изменить исходную ссылку.

Ответ 9

Ruby интерпретируется. Переменные - это ссылки на данные, но не сами данные. Это облегчает использование одной и той же переменной для данных разных типов.

Назначение lhs = rhs затем копирует ссылку на rhs, а не данные. Это отличается на других языках, таких как C, где присваивание копирует данные в lhs из rhs.

Итак, для вызова функции переменная, переданная, скажем, x, действительно скопирована в локальную переменную в функции, но x является ссылкой. Затем будут две копии справки, причем оба они ссылаются на одни и те же данные. Один будет в вызывающем, один в функции.

Присвоение функции затем копирует новую ссылку на версию функции x. После этого версия хэндлера x остается неизменной. Это все еще ссылка на исходные данные.

Напротив, использование метода .replace в x приведет к тому, что ruby ​​сделает копию данных. Если замена используется перед любыми новыми назначениями, то в действительности вызывающий элемент также увидит изменение данных в своей версии.

Аналогично, если исходная ссылка находится в такте для переданной переменной, переменные экземпляра будут такими же, что и вызывающий. В рамках объекта переменные экземпляра всегда имеют самые последние опорные значения, независимо от того, предоставляются ли они вызывающим или заданы в функции, в которую был передан класс.

"Вызов по значению" или "вызов по ссылке" здесь путают из-за путаницы над '=' В скомпилированных языках '=' является копией данных. Здесь в этом интерпретируемом языке '=' является ссылочной копией. В примере у вас есть ссылка, переданная в сопровождении ссылочной копии, хотя "=", которая сгибает оригинал, переданный в ссылке, а затем люди, говорящие об этом, как будто "=" были копией данных.

Чтобы соответствовать определениям, мы должны придерживаться ".replace", поскольку это копия данных. С точки зрения ".replace" мы видим, что это действительно проходит по ссылке. Кроме того, если мы проходим через отладчик, мы видим, что ссылки передаются, поскольку переменные являются ссылками.

Однако, если мы должны сохранить '=' в качестве системы отсчета, то действительно мы видим, что данные передаются в данных вплоть до назначения, и после этого мы не можем видеть это после присвоения, в то время как данные вызывающего абонента остается неизменной. На уровне поведения это пропуск по значению, если мы не считаем переданный в значение составным, поскольку мы не сможем сохранить его часть при изменении другой части в одном задании (так как это присвоение изменяет ссылку, а оригинал выходит за рамки). Также будет бородавка, в этом случае переменными в объектах будут ссылки, как и все переменные. Следовательно, мы будем вынуждены говорить о передаче "ссылок по значению" и должны использовать связанные установки.

Ответ 10

Попробуйте следующее: -

1.object_id
#=> 3

2.object_id
#=> 5

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

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

идентификатор a содержит object_id 3 для объекта значения 1 и идентификатор b содержит object_id 5 для объекта значения 2.

Теперь сделайте следующее: -

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 оба содержат одинаковый object_id 5, который ссылается на объект значения 2. Итак, переменная Ruby содержит object_ids для ссылки на объекты значений.

Выполнение следующего также дает ошибку: -

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

Здесь идентификатор возвращает объект 11 значений, идентификатор объекта которого равен 23, то есть 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

arg в foo присваивается возвращаемое значение x. Он ясно показывает, что аргумент передается значением 11, а значение 11 само является объектом, имеет уникальный идентификатор объекта 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 сначала содержит object_id 23 для ссылки 11 и после внутреннего присвоения с объектом значения 12, он содержит object_id 25. Но он не изменяет значение, на которое ссылается идентификатор x, используемый в методе вызова.

Следовательно, Ruby передается по значению, а переменные Ruby не содержат значений, но содержат ссылку на объект значения.

Ответ 11

Следует отметить, что вам не нужно даже использовать метод "replace" для изменения значения исходного значения. Если вы присвоите одно из хэш-значений для хеша, вы меняете исходное значение.

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

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

Ответ 12

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