2009-09-23 5 views
5

(私の以前の質問へのフォローアップRuby: how can I copy a variable without pointing to the same object?Ruby:この配列をコピーするにはどうしたらいいですか?

単純なRubyプログラムを作成して.svgファイルに置き換えます。最初のステップは、ファイルから情報を取り出して配列に入れることです。この関数が呼び出されるたびにディスクからファイルを読み込まないようにするには、memoizeデザインパターンを使用しようとしています。最初の呼び出しの後にすべての呼び出しでキャッシュされた結果を使用します。

これを行うには、関数の直前で定義されたグローバル変数を使用しています。しかし、私は.dupローカル変数を返す前にその変数をローカル変数に戻しても、この変数を呼び出す関数はグローバル変数を変更しています。

#memoize to keep from having to read original file on each pass 
$svg_filedata_cache = [] #the global variable 
def svg_filedata(filename) 
    if $svg_filedata_cache.empty? 
     File.open(filename, "r"){|f| $svg_filedata_cache = f.readlines} 
    end 
    svg_filedata_cache = $svg_filedata_cache.dup #try to copy it 
    return svg_filedata_cache #SHOULD point to a different object (but doesn't) 
end 

二つの質問(いずれかまたは両方を答える):

  1. なぜにとり、値がここで返さ修正する他の機能は、また影響を与えない。ここ

    は私の実際のコードです私はそれをコピーするために.dupを使用しましたが、グローバル変数?

  2. 私はRubyを初めて使いました。これはRubesqueのやり方ではないと確信しています(とにかく、グローバル変数が好きではありません)。より良い戦略を提案できますか?
+0

P.S.実際には$ svg_filedata_cache [filename]でなければならないことが分かります。これは、異なるファイル名を使って関数呼び出しをすることができますが、この場合は必要ありません。 –

+0

ところで、グローバルと返されたオブジェクトは別のobject_idを持っています。返された配列内の文字列に言及していると思います。 – khelll

+0

@khell - はい、私は元の配列の内容が変更されているという事実に基づいて声明を出しました。 –

答えて

9

二重化された配列を変更しても、オリジナルには影響しません。ただし、配列内の文字列の変更は、グローバル配列と二重化配列には同じ文字列への参照が含まれているため(dupは深いコピーを実行しないため)、グローバルに表示されます。

したがって、深いコピー(svg_filedata_cache = $svg_filedata_cache.map {|line| line.dup})を実行するか、文字列の操作を変更しないでください。

+1

配列内のすべての文字列が独自のオブジェクトであることはわかりませんでした。私は本当に "Rubyのすべてがオブジェクトである"と思うのです。 :) –

6

コードを少し強化:

$svg_filedata_cache = [] #the global variable 
def svg_filedata(filename) 
    # Use ||= for memoiziation 
    $svg_filedata_cache ||= File.open(filename, "r"){|f| $svg_filedata_cache = f.readlines} 
    $svg_filedata_cache.dup #shallow copying 
end 

更新:簡単なトリックは、一般的に深いコピーを行うには、次のとおりです。

def deep_copy(obj) 
    Marshal.load(Marshal.dump(obj)) 
end 
+0

So || = "左側が偽(空)の場合は、右側を使用しますか?" –

+2

これは、その変数が既に設定されていない場合にのみ、左側の変数に右側の値を代入することを意味します。 – khelll

2

世界はおそらく変更されていないが、要素それとあなたの.dup参照が変化していることを示します。より標準的なルビにするには、グローバルを取り除き、クラスを使用し、initialize関数でファイルを読み込みます。 (コンストラクタです。)配列を@vでインスタンス変数にします。

関連する問題