2009-05-03 3 views
1

Я ожидал следующего кода для печати «8», «111» и «999». Я предполагал, что каждый a, b, c и d указывают на одно и то же место памяти. Если я изменю местоположение через один из них, почему другой не изменится? Ясно, что моя логика бедна, или я что-то забыл. Вместо этого он печатает «7», «7» и «8».Несколько ссылок в Ruby

Почему?

a=b=c=d=7 
    b = 8 
    puts d 

    c = 111 
    puts a 

    d = 999 
    puts b 

[Разъяснение]

Причина моего замешательства является примером в the book (page 20). Они меняются там так же значения, но они получают результаты, которые я предложенные выше. Мы говорим об одном и том же вопросе?

+0

Мы могли бы дать вам более подробный ответ, если вы сообщите нам, что фон вы откуда. – kch

+0

Вы знаете, что Ruby настолько заразительно, что вы действительно можете заставить этот образец работать как ожидалось –

+2

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

ответ

11
a=b=c=d=7 
# a, b, c and d points to the same integer object "7" 
    b = 8 
# b now points to a new object "8" 
# "=" does not change the value of the pointer integer, 
# it assings a new reference like in the line above 
    puts d 
# obviously, d still points to "7" 

    c = 111 
# c now points to another integer object "111" 
    puts a 
# a still points to "7" 

    d = 999 
# d now points to a new integer object "999" 
    puts b 
# b still points to "8" 

в Ruby, объект Integer неизменен, так что вы не можете назначить Integer к несколько ссылке и после изменения значения.

Как предлагалось @pts, вы должны использовать массив, чтобы обернуть ссылку на Integer, поскольку массивы изменяемы, вы можете изменить значение после.

a=b=c=d=[7] 
b[0] = 8 
puts d[0] 
c[0] = 111 
puts a[0] 
d[0] = 999 
puts b[0] 

ПОЯСНЕНИЯ:

Если вы родом из фона C++, это может быть странно, потому что C++ делает 2 вещи с тем же синтаксисом, назначая ссылки и изменение значения, на который ссылается.

int a = 10; // creates an int on the stack with value 10 
int& b = a; // creates a reference to an int and references the a variable 
b = 5; // change the value referenced by b (so a) to 5 
// a and b now hold the value 5 

В Ruby ссылка является изменяемой и целые числа не являются (в точности противоположными C++). Поэтому назначение ссылки фактически изменит ссылку, а не ссылочное значение.

Другим решением было бы создать класс, который является изменяемым числом:

class MutableInteger 
    attr_writer :value 
    def initialize(value) 
    @value = value 
    end 
    def inspect 
    value 
    end 
    def to_i 
    value 
    end 
    def to_s 
    value 
    end 
end 

a = b = MutableInteger.new(10) 
a.value = 5 
puts b 
# prints 5 
+0

вы можете сделать вещь EVIL и переопределить оператор присваивания по целому ... –

+2

Назначение - это не сообщение, поскольку оно касается самой переменной, а не ее содержимого. –

1

Они не указывают на то же место памяти. Ruby doesn't pass by reference.

+0

Ну, это не так просто. Если вы передаете массив или хэш методу, вы передаете ссылку в том смысле, что вы не делаете его копию, поэтому, если метод изменяет массив, вы изменяете оригинал. Но да, вы не можете играть с указателями и указывать переменные, указывающие на то же место памяти и т. Д. – kch

+0

-1 Истинно, что Ruby не проходит по ссылке. Но так как каждая переменная на самом деле является ссылкой на объект, копирование объектов пока не будет использоваться #dup. После первой строки OP каждая переменная указывает на тот же объект «7». –

+0

В этом блоге отсутствует точка, конечно, Java и Ruby проходят по ссылке. Передача по ссылке означает, что содержимое памяти не копируется в параметры метода, а вместо ссылки. Реальное различие заключается в том, как работает аффектация: в C/C++ вы можете напрямую изменять ссылочное содержимое, но в Ruby-аффектации изменится только то, что содержит переменная (т. Е. Замените ссылку на другую). Так что я бы сказал, что C/C++ влияет на ссылку, Java/Ruby влияет на значение (переменной). –

2

Самый простой способ получить вывод, который вы ожидаете использует массив из одного элемента:

a=b=c=d=[7] 
b[0] = 8 
puts d[0] 
c[0] = 111 
puts a[0] 
d[0] = 999 
puts b[0] 

Чтобы получить, если б относятся к одному объекту, используйте a.__id__ == b.__id__.

+0

Я искал его. Большое спасибо! –

1

После первой линии, а, б, в и г все это указывает на тот же объект Fixnum (со значением 7). Однако, когда вы выполняете b = 8, b теперь указывает на новый объект Fixnum (со значением 8).

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

Если вы сравниваете с C++, это похоже на назначение указателя по значению, а не назначение по ссылке.

Смежные вопросы