2011-08-24 2 views
5
>> a = 5 
=> 5 
>> b = a 
=> 5 
>> b = 4 
=> 4 
>> a 
=> 5 

come posso impostare "b" per essere effettivamente "a" in modo che nell'esempio, la variabile a diventi anche quattro. Grazie.ruby ​​variabile come stesso oggetto (puntatori?)

+2

Suoni come puntatori ... Non è un dev di Ruby, ma non penso che i puntatori siano in Ruby. – Dair

+0

Sì, ci sono dei puntatori in Ruby. Sono riferimenti alle variabili create dai loro tipi di classe. Qui b stava puntando a dove è fino a quando non ha fatto un nuovo puntatore per sé riferimento a Fixnum 4. –

+0

La risposta mostra che = l'assegnazione crea questi puntatori e quando i tipi degli stessi puntano allo stesso riferimento tutto ciò che è necessario è cambiare il riferimento oggetto e entrambi gli stessi tipi hanno uguale riferimento all'oggetto. Tieni presente perché dicono "In Ruby, tutto è un oggetto." Un sacco di verità in questo. –

risposta

5
class Ref 
    def initialize val 
    @val = val 
    end 

    attr_accessor :val 

    def to_s 
    @val.to_s 
    end 
end 

a = Ref.new(4) 
b = a 

puts a #=> 4 
puts b #=> 4 

a.val = 5 

puts a #=> 5 
puts b #=> 5 

Quando si esegue b = a, b punti per lo stesso oggetto come a (hanno lo stesso object_id) .

Quando si esegue a = some_other_thing, un punto punta a un altro oggetto, mentre b rimane invariato.

Per Fixnum, nil, true e false, non è possibile cambiare il valore senza cambiare il object_id. Tuttavia, è possibile modificare altri oggetti (stringhe, matrici, hash, ecc.) Senza modificare object_id, poiché non si utilizza il compito (=).

Esempio con le stringhe:

a = 'abcd' 
b = a 

puts a #=> abcd 
puts b #=> abcd 

a.upcase!   # changing a 

puts a #=> ABCD 
puts b #=> ABCD 

a = a.downcase  # assigning a 

puts a #=> abcd 
puts b #=> ABCD 

Esempio con gli array:

a = [1] 
b = a 

p a #=> [1] 
p b #=> [1] 

a << 2   # changing a 

p a #=> [1, 2] 
p b #=> [1, 2] 

a += [3]   # assigning a 

p a #=> [1, 2, 3] 
p b #=> [1, 2] 
2

Non è possibile. Le variabili contengono riferimenti a valori, non riferimenti ad altre variabili.

Ecco ciò che il codice di esempio sta facendo:

a = 5 # Assign the value 5 to the variable named "a". 
b = a # Assign the value in the variable "a" (5) to the variable "b". 
b = 4 # Assign the value 4 to the variable named "b". 
a # Retrieve the value stored in the variable named "a" (5). 

si veda questo articolo per una discussione più approfondita del tema: pass by reference or pass by value.

+0

Non c'è modo di fare questo? Affatto? Anche con qualche kludge? Nessun valore che può rappresentare il riferimento? Ho scoperto che posso fare ogni genere di cose pazze in ruby ​​abbastanza facilmente, sono sorpreso che questo non sia possibile. Grazie per la risposta. Voglio sostanzialmente nascondere la variabile reale, potrei adattare il metodo impostato della variabile per impostare anche l'altra variabile? Ciò comporterebbe un sovraccarico? – Orbit

+0

No, non si utilizza la sintassi nell'esempio. Potresti fare alcuni trucchi fantastici con 'eval' per scrivere funzioni che caricano/memorizzano i valori in altre variabili, ma questo è tutto. Vedi questo [discussione di Ruby passare per riferimento vs valore] (http://www.ruby-forum.com/topic/41160). – maerics

+0

Questo è sbagliato! b = un enfaticamente NON assegna 5 a b perché 5 è il valore in a. Il valore in a è un riferimento a un fixnum. b = a assegna quel riferimento a b. –

0

Non sono un esperto di Ruby. Ma per un Kluge tecnicamente pazzo ... che funzionerebbe solo se ti sei sentito come passare attraverso eval ogni volta che hai lavorato con una variabile:

>> a = 5 
=> 5 
>> b = :a 
=> :a 
>> eval "#{b} = 4" 
=> 4 
>> eval "#{a}" 
=> 4 
>> eval "#{b}" 
=> 4 

Si noti che un uso diretto di b sarà ancora vi darà :a e si può 't lo usano nelle espressioni che non sono in eval:

>> b 
=> :a 
>> b + 1 
NoMethodError: undefined method `+' for :a:Symbol 

... e ci sono sicuramente un sacco di avvertimenti. Come quella che avrebbe dovuto catturare l'binding e passarlo in giro in scenari più complessi ...

'pass parameter by reference' in Ruby?

@ Paul.s ha una risposta per se è possibile modificare il punto della dichiarazione di essere un oggetto wrapper, ma se si può controllare solo il punto di riferimento allora ecco una classe BasicReference ho provato:

class BasicReference 
    def initialize(r,b) 
     @r = r 
     @b = b 
     @val = eval "#{@r}", @b 
    end 

    def val=(rhs) 
     @val = eval "#{@r} = #{rhs}", @b 
    end 

    def val 
     @val 
    end 
end 

a = 5 

puts "Before basic reference" 
puts " the value of a is #{a}" 

b = BasicReference.new(:a, binding) 

b.val = 4 

puts "After b.val = 4" 
puts " the value of a is #{a}" 
puts " the value of b.val is #{b.val}" 

Questo uscite:

Before basic reference 
    the value of a is 5 
After b.val = 4 
    the value of a is 4 
    the value of b.val is 4 
1

Come è stato notato, la sintassi che si sta utilizzando non può essere eseguita. Basta buttare questo là fuori anche se si potrebbe fare una classe wrapper dipende da cosa si vuole realmente fare

ruby-1.8.7-p334 :007 > class Wrapper 
ruby-1.8.7-p334 :008?> attr_accessor :number 
ruby-1.8.7-p334 :009?> def initialize(number) 
ruby-1.8.7-p334 :010?>  @number = number 
ruby-1.8.7-p334 :011?> end 
ruby-1.8.7-p334 :012?> end 
=> nil 
ruby-1.8.7-p334 :013 > a = Wrapper.new(4) 
=> #<Wrapper:0x100336db8 @number=4> 
ruby-1.8.7-p334 :014 > b = a 
=> #<Wrapper:0x100336db8 @number=4> 
ruby-1.8.7-p334 :015 > a.number = 6 
=> 6 
ruby-1.8.7-p334 :016 > a 
=> #<Wrapper:0x100336db8 @number=6> 
ruby-1.8.7-p334 :017 > b 
=> #<Wrapper:0x100336db8 @number=6> 
1

È possibile utilizzare le matrici:

a = [5] 
b = a 
b[0] = 4 
puts a[0] #=> 4 

Questa idea si basa su this answer.

1

Solo per riferimento.

>> a = 5 
=> 5 
>> a.object_id 
=> 11 
>> b = a 
=> 5 
>> b.object_id 
=> 11 
>> b = 4 
=> 4 
>> b.object_id 
=> 9 
>> a.object_id 
=> 11 
# We did change the Fixnum b Object. 
>> Fixnum.superclass 
=> Integer 
>> Integer.superclass 
=> Numeric 
>> Numeric.superclass 
=> Object 
>> Object.superclass 
=> BasicObject 
>> BasicObject.superclass 
=> nil 

Spero che questo ci consenta di capire meglio gli oggetti in Ruby.

1

Un'opzione nei casi in cui si ritiene che si desideri avere operazioni puntatore diretto consiste nell'utilizzare il metodo di sostituzione di Hash, Array & stringhe.

Questo è utile per quando si desidera che un metodo restituisca una variabile che un processo che il metodo imposta cambierà in un secondo momento e non vuole il fastidio di utilizzare un oggetto wrapper.

esempio:

def hash_that_will_change_later 
    params = {} 
    some_resource.on_change do 
    params.replace {i: 'got changed'} 
    end 
    params 
end 
a = hash_that_will_change_later 
=> {} 
some_resource.trigger_change! 
a 
{i: 'got changed'} 

E 'probabilmente meglio in generale l'uso dei wrapper di oggetti espliciti per tali casi, ma questo modello è utile per la costruzione di specifiche/test di roba asincrona.