Как использовать значение по умолчанию для хэша пустых массивов? - PullRequest
6 голосов
/ 10 мая 2011

Я хочу использовать default для сброса моего ary, когда мне это нужно.Но я не могу понять, как не изменять значения по умолчанию при изменении значений ary.

> default = {"a"=>[], "b"=>[], "c"=>[]}
=> {"a"=>[], "b"=>[], "c"=>[]} 

> ary = default.clone
=> {"a"=>[], "b"=>[], "c"=>[]} 

> ary["a"] << "foo"
=> ["foo"] 

> default
=> {"a"=>["foo"], "b"=>[], "c"=>[]} 

Ответы [ 6 ]

7 голосов
/ 10 мая 2011

Здесь вы обнаружили, что Hash#clone делает только мелкий клон, то есть он копирует только себя, но не объекты, на которые есть ссылки.

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

class Hash
  def deep_clone
    Hash[collect { |k,v| [ k, v.respond_to?(:deep_clone) ? v.deep_clone : v ] }]
  end
end

class Array
  def deep_clone
    collect { |v| v.respond_to?(:deep_clone) ? v.deep_clone : v }
  end
end

Это позволит вам клонировать произвольные объекты Hash и Array по мере необходимости.

3 голосов
/ 10 мая 2011

И clone, и dup создают неглубокую копию вашего объекта, что приводит к такому поведению. Я не уверен, каков правильный способ получения глубокой копии, но вместо:

ary = default.clone

Попробуйте:

ary = Marshal.load(Marshal.dump(default))

Это взято из живой среды 2.3.8 на ruby ​​1.8.7

2 голосов
/ 10 мая 2011

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

def default
  {"a"=>[], "b"=>[], "c"=>[]}
end

ary = default #=> {"a"=>[], "b"=>[], "c"=>[]}

ary["a"] << "foo" #=> {"a"=>["foo"], "b"=>[], "c"=>[]}

default #=> {"a"=>[], "b"=>[], "c"=>[]}

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

2 голосов
/ 10 мая 2011

Способ сделать это следующим образом:

ary = Marshal.load(Marshal.dump(default)) 
2 голосов
/ 10 мая 2011
class Object
  def deep_clone
    Marshal::load(Marshal.dump(self))
  end
end

default = {"a"=>[], "b"=>[], "c"=>[]}
ary = default.deep_clone
ary["a"] << "foo"
default {"a"=>[], "b"=>[], "c"=>[]}
2 голосов
/ 10 мая 2011

clone делает только мелкие копии, поэтому при клонировании вашего хэша все остальное направляется на те же вложенные массивы.

Этого можно избежать с помощью класса Marshal, создав дамп и загрузив его взначения объекта:

> default = {"a" => [], "b" => [], "c" => []}
=> {"a"=>[], "b"=>[], "c"=>[]} 
> ary = Marshal.load(Marshal.dump(default))
=> {"a"=>[], "b"=>[], "c"=>[]} 
> ary["a"] << "foo"
=> ["foo"]
> default
=> {"a"=>[], "b"=>[], "c"=>[]} 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...