Изменение значения рубиновых переменных / ссылок - PullRequest
4 голосов
/ 28 января 2010

Я просто наткнулся на то, что я не совсем понимаю. Я знаю, что переменные в ruby ​​являются ссылками. Так что это потрясающе возможно. Но когда я передаю переменную методу, она ведет себя странно:

my_var_a = "nothing happend to me"
my_var_b = "nothing happend to me"

def parse_set(my_var_set)
  my_var_set = "my value changed"
end

def parse_sub(my_var_sub)
  my_var_sub.sub! /(.*)/, "my value changed"
end

parse_set(my_var_a)
parse_sub(my_var_b)

my_var_a # => "nothing happend to me"
my_var_b # => "my value changed"

Можете ли вы объяснить мне, почему он работает с sub!, а = оставляет объект без изменений? Как я могу избежать использования sub!, но с тем же результатом?

Ответы [ 3 ]

12 голосов
/ 28 января 2010

my_var_a и my_var_set - это разные ссылки, но они указывают на один и тот же объект. Если вы измените объект в my_var_set, изменение отобразится в my_var_a. Однако, если вы назначите my_var_set новому объекту, это не изменит то, на что указывает my_var_a.

Редактировать: уточнение ...

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

my_var_a = "nothing happend to me"

Ruby сохраняет строку «ничего не случилось со мной» в ячейке памяти (назовем ее 1000) и сохраняет ссылку my_var_a в другую ячейку памяти (скажем, 2000). Когда ваш код использует my_var_a, интерпретатор смотрит на местоположение 2000, видит, что оно указывает на 1000, а затем получает фактическое строковое значение из 1000.

Когда вы вызываете parse_set(my_var_a), Ruby фактически создает новую ссылку с именем my_var_set и указывает ее на строку, на которую указывал my_var_a (область памяти 1000). Тем не менее, my_var_set является копией ссылки my_var_a - скажем, my_var_set был создан в ячейке памяти 3000. my_var_a и my_var_set - это две совершенно разные ссылки в памяти, они просто указывают на та же самая точная ячейка памяти, которая содержит строковое значение.

Оператор my_var_set = "my value changed" в parse_set создает новую строку в памяти и указывает my_var_set на эту новую ячейку памяти. Однако это не меняет того, на что указывают исходные my_var_a ориентиры! Теперь, когда my_var_set указывает на другое место в памяти, ничего, что вы делаете с этой переменной, не повлияет на my_var_a.

Та же самая справочная копия существует и для parse_sub. Но причина того, что parse_sub изменяет строку, заключается в том, что вы вызываете метод непосредственно по ссылке my_var_sub. Когда вы делаете это, интерпретатор получает объект, на который указывает my_var_sub, и затем изменяет его. Таким образом, это изменение будет отображаться в справочнике my_var_a, поскольку оно все еще указывает на ту же строку.

5 голосов
/ 28 января 2010
irb(main):008:0* a = 'abc'
=> "abc"
irb(main):009:0> a.replace('def')
=> "def"
irb(main):010:0> a
=> "def"

Мне приходилось использовать replace ровно ноль раз за все годы, что я программировал на Ruby. Интересно, есть ли лучший дизайн для вашего кода.

Большинство строковых методов, которые изменяют получателя, имеют суффикс! (sub !, прописные буквы !, chomp !, и т. д.) Некоторые, которые меняют получателя, не имеют суффикса! (заменить это один). Если вы вызываете метод, который изменяет получателя, любые и все ссылки на этот объект увидят это изменение. если вы вызываете метод, который не изменяет получатель, метод возвращает новую строку .

gsub не изменяет получателя, а вместо этого возвращает новый экземпляр String:

a = "foo"
b = a
p a.sub(/o/, '#')     # "f##"
p a                   # => "foo"
p b                   # => "foo"

GSUB! действительно изменяет приемник:

a = "foo"
b = a
p a.sub!(/o/, '#')    # => "f#o"
p a                   # => "f#o"
p b                   # => "f#o"
0 голосов
/ 28 января 2010

«Как я могу избежать использования sub! Но с тем же результатом?»

def parse_set()
  "my value changed"
end

a = parse_set()

Установите его как переменную экземпляра объекта (хотя это работает только в вашем основном скрипте, я рекомендую создать свой собственный класс, если вы хотите использовать метод переменных экземпляра).

@my_var_a = "nothing happend to me"

def parse_set()
  @my_var_a = "my value changed"
end
...