私はRuby初心者です(今はrakeスクリプトを書いています)ことを認めます。ほとんどの言語では、コピーコンストラクターは簡単に見つかります。 30分間の検索では、Rubyでは見つかりませんでした。ハッシュのコピーを作成して、元のインスタンスに影響を与えずに変更できるようにします。
意図したとおりに機能しない期待されるメソッド:
h0 = { "John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"}
h1=Hash.new(h0)
h2=h1.to_hash
それまでの間、私はこの洗練されていない回避策に頼りました
def copyhash(inputhash)
h = Hash.new
inputhash.each do |pair|
h.store(pair[0], pair[1])
end
return h
end
clone
メソッドは、Rubyの標準である shallow-copy を実行する組み込みの方法です。
irb(main):003:0> h0 = {"John" => "Adams", "Thomas" => "Jefferson"}
=> {"John"=>"Adams", "Thomas"=>"Jefferson"}
irb(main):004:0> h1 = h0.clone
=> {"John"=>"Adams", "Thomas"=>"Jefferson"}
irb(main):005:0> h1["John"] = "Smith"
=> "Smith"
irb(main):006:0> h1
=> {"John"=>"Smith", "Thomas"=>"Jefferson"}
irb(main):007:0> h0
=> {"John"=>"Adams", "Thomas"=>"Jefferson"}
動作がオーバーライドされる可能性があることに注意してください。
このメソッドには、クラス固有の動作があります。その場合、その動作はクラスの
#initialize_copy
メソッドで文書化されます。
他の人が指摘したように、clone
はそれを行います。ハッシュのclone
は浅いコピーを作成することに注意してください。それは言うことです:
h1 = {:a => 'foo'}
h2 = h1.clone
h1[:a] << 'bar'
p h2 # => {:a=>"foobar"}
何が起こっているのかというと、ハッシュの参照がコピーされていますが、参照が参照しているオブジェクトはコピーされていません。
ディープコピーが必要な場合:
def deep_copy(o)
Marshal.load(Marshal.dump(o))
end
h1 = {:a => 'foo'}
h2 = deep_copy(h1)
h1[:a] << 'bar'
p h2 # => {:a=>"foo"}
deep_copy
は、マーシャリングできるすべてのオブジェクトに対して機能します。ほとんどの組み込みデータ型(配列、ハッシュ、文字列、&c)はマーシャリングできます。
マーシャリングは、Rubyの serialization の名前です。マーシャリングでは、オブジェクトは、それが参照するオブジェクトとともに、一連のバイトに変換されます。これらのバイトは、元のような別のオブジェクトを作成するために使用されます。
ハッシュは、既存のハッシュから新しいハッシュを作成できます。
irb(main):009:0> h1 = {1 => 2}
=> {1=>2}
irb(main):010:0> h2 = Hash[h1]
=> {1=>2}
irb(main):011:0> h1.object_id
=> 2150233660
irb(main):012:0> h2.object_id
=> 2150205060
私はRubyの初心者でもあり、ハッシュの複製で同様の問題に直面しました。以下を使用してください。この方法の速度についてはわかりません。
copy_of_original_hash = Hash.new.merge(original_hash)
Marshalドキュメントのセキュリティに関する考慮事項セクション で述べたように、
信頼できないデータを逆シリアル化する必要がある場合は、JSONまたは、String、Array、Hashなどの単純な「プリミティブ」タイプのみをロードできる別のシリアル化形式を使用します。
RubyでJSONを使用してクローンを作成する方法の例を次に示します。
require "json"
original = {"John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"}
cloned = JSON.parse(JSON.generate(original))
# Modify original hash
original["John"] << ' Sandler'
p original
#=> {"John"=>"Adams Sandler", "Thomas"=>"Jefferson", "Johny"=>"Appleseed"}
# cloned remains intact as it was deep copied
p cloned
#=> {"John"=>"Adams", "Thomas"=>"Jefferson", "Johny"=>"Appleseed"}
以下を使用して、ハッシュオブジェクトをディープコピーできます。
deeply_copied_hash = Marshal.load(Marshal.dump(original_hash))
標準のクローン作成方法は凍結状態を保持するため、元のオブジェクトに基づいて新しい不変オブジェクトを作成するのには適していません(新しいオブジェクトを元のオブジェクトとわずかに異なるようにする場合(ステートレスプログラミングが必要な場合))。
クローンが遅い。パフォーマンスのために、おそらく空白のハッシュとマージで開始する必要があります。ネストされたハッシュの場合はカバーしません...
require 'benchmark'
def bench Benchmark.bm do |b|
test = {'a' => 1, 'b' => 2, 'c' => 3, 4 => 'd'}
b.report 'clone' do
1_000_000.times do |i|
h = test.clone
h['new'] = 5
end
end
b.report 'merge' do
1_000_000.times do |i|
h = {}
h['new'] = 5
h.merge! test
end
end
b.report 'inject' do
1_000_000.times do |i|
h = test.inject({}) do |n, (k, v)|
n[k] = v;
n
end
h['new'] = 5
end
end
end
end
ベンチユーザーシステムの合計(実数) clone 1.960000 0.080000 2.040000(2.029604) merge 1.690000 0.080000 1.770000(1.767828) inject 3.120000 0.030000 3.150000 (3.152627)
Object#clone
を使用します。
h1 = h0.clone
(紛らわしいことに、clone
のドキュメントには、initialize_copy
がこれをオーバーライドする方法であると書かれていますが、Hash
のそのメソッドへのリンクは、代わりにreplace
に移動します...)
これは特別な場合ですが、取得してコピーを作成する定義済みのハッシュで開始する場合は、ハッシュを返すメソッドを作成できます。
def johns
{ "John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"}
end
h1 = johns
私が持っていた特定のシナリオは、いくつかのハッシュが他のハッシュを構築するJSONスキーマハッシュのコレクションを持っていたことでした。最初にそれらをクラス変数として定義していましたが、このコピーの問題に遭遇しました。
Rubyには百万通りの方法があるため、Enumerableを使用する別の方法を次に示します。
h0 = { "John"=>"Adams","Thomas"=>"Jefferson","Johny"=>"Appleseed"}
h1 = h0.inject({}) do |new, (name, value)|
new[name] = value;
new
end