Подтвердить что ты не робот

Почему это не работает, если в Ruby все есть объект?

Учитывая, что на языке программирования Ruby все называется объектом, я уверен, что передача аргументов методам производится по ссылке. Однако этот маленький пример ниже меня озадачивает:

$string = "String"

def changer(s)
  s = 1
end

changer($string)

puts $string.class
String
 => nil

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

4b9b3361

Ответ 1

Способ Ruby - это комбинация прохода по значению и передача по ссылке. Фактически, Ruby использует pass по значению со ссылками.

Вы можете прочитать больше в следующих разделах:

Некоторые заметные цитаты:

Абсолютно верно: Ruby использует pass by value - со ссылками.

irb(main):004:0> def foo(x) x = 10 end
=> nil
irb(main):005:0> def bar; x = 20; foo(x); x end
=> nil
irb(main):006:0> bar
=> 20
irb(main):007:0>

Нет стандартного способа (т.е. кроме привлечения eval и метапрограммирование магии), чтобы сделать переменную в области вызова другой объект. И, кстати, это не зависит от объекта, переменная относится к. Непосредственные объекты в Ruby легко интегрируются с остальные (например, POD в Java) и Ruby языковой перспективы вы не видите никакой разницы (кроме возможно, производительность). Это одна из причин, почему Ruby настолько изящна.

и

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

Разница между:

def my_method( a )
  a.gsub!( /foo/, 'ruby' )
end

str = 'foo is awesome'
my_method( str )            #=> 'ruby is awesome'
str                                    #=> 'ruby is awesome'

и

def your_method( a )
  a = a.gsub( /foo/, 'ruby' )
end

str = 'foo is awesome'
my_method( str )            #=> 'ruby is awesome'
str                                    #=> 'foo is awesome'

заключается в том, что в #my_method вы вызываете #gsub! который изменяет объект (а) на месте. Поскольку переменная 'str' (вне области метода) и переменная "a" (внутри области метода) имеет "значение", которое ссылка на тот же объект, отражение на этом объекте отражается в переменной "str" после вызова метода. В #your_method вы вызовите #gsub, который не изменяет исходный объект. Вместо этого создает новый экземпляр String, содержащий модификации. когда вы назначаете этот объект переменной 'a', вы меняете значение из 'a', чтобы быть ссылкой на этот новый экземпляр String. Однако значение 'str' все еще содержит ссылку на оригинальную (немодифицированную) Строковый объект.

Является ли метод изменением ссылки или ссылочного объекта зависит от типа класса и реализации метода.

string = "hello"

def changer(str)
  str = "hi"
end

changer(string)
puts string
# => "hello"

string не изменяется, поскольку присвоение строк заменяет ссылку, а не ссылочное значение. Я хочу изменить строку на месте, вам нужно использовать String#replace.

string = "hello"

def changer(str)
  str.replace "hi"
end

changer(string)
puts string
# => "hi"

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

str1 = "hello"
str2 = "hello"

str1.gsub("h", "H")
str2.gsub!("h", "H")

puts str1
# => "hello"
puts str2
# => "Hello"

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

$wrapper = Struct.new(:string).new
$wrapper.string = "String"

def changer(w)
  w.string = 1
end

changer($wrapper)

puts $wrapper.string
# => 1

Ответ 2

Назначение не связывает значения с объектами, оно связывает ссылки на объекты с идентификаторами. Передача аргументов работает одинаково.

Когда вы вводите тело функции, мир выглядит следующим образом:

 +---+                  +----------+
 | s |----------------->| "String" |
 +---+                  +----------+
                              ^
 +-------+                    |
 |$string|--------------------+
 +-------+

Код

 s = 1

делает мир похожим на

 +---+       +---+      +----------+
 | s |------>| 1 |      | "String" |
 +---+       +---+      +----------+
                              ^
 +-------+                    |
 |$string|--------------------+
 +-------+

Синтаксис присваивания управляет переменными, а не объектами.

Как и многие аналогичные языки (Java, С#, Python), ruby ​​является pass-by-value, где значения чаще всего ссылаются.

Чтобы управлять строковым объектом, вы можете использовать метод в строке, например s.upcase!. Подобная вещь будет отражена вне метода, поскольку она манипулирует самим объектом.

Ответ 3

Поскольку оба $string и s являются ссылками на один и тот же объект, строка "String". Однако, когда вы назначаете s в 1, вы не меняете объект "String", вы делаете ссылку на новый объект.

Ответ 4

Ruby передает значения вокруг функции, и эти значения являются ссылками на объекты. В вашей функции вы переназначаете s на другое значение, в данном случае ссылку на 1. Он не изменяет исходный объект.

Ваш метод не изменяет переданный объект, вы меняете то, что означает s.

Ответ 5

На самом деле, большинство управляемых языков программирования, таких как java, С#... almsot все не проходят по ссылке...

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

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