Ruby: how can I copy this array?

(Following my previous question, Ruby: how can I copy a variable without pointing to the same object? )

I am writing a simple Ruby program to make some replacements in a .svg file. The first step is to extract the information from the file and put it in an array. In order not to read the file from disk every time this function is called, I try to use the memoize template - use the cached result for each call after the first.

To do this, I use a global variable defined immediately before the function. But even if I .dup this variable for a local one, before returning the local variable, the function that calls it still modifies the global variable.

Here is my actual code:

 #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 

Two questions (one or both answers):

  • Why do other functions that accept and modify the value returned here also affect the global variable, although I used .dup to copy it?
  • I'm new to Ruby, and I'm sure this is not the most Rubyesque way to do this (and I don't like global variables anyway). Can you suggest a better strategy?
+5
ruby
Sep 23 '09 at 12:31
source share
3 answers

Changing the duped array will not affect the original. However, changes to the lines inside the array will be visible globally because the global array and the deliberate array still contain references to the same lines (dup does not perform a deep copy).

Thus, either make a deep copy ( svg_filedata_cache = $svg_filedata_cache.map {|line| line.dup} ), or simply avoid svg_filedata_cache = $svg_filedata_cache.map {|line| line.dup} actions.

+9
Sep 23 '09 at 12:45
source share

Speeding up the code a bit:

 $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 

Update: A simple trick for deep copying in general:

 def deep_copy(obj) Marshal.load(Marshal.dump(obj)) end 
+6
Sep 23 '09 at 12:51
source share

The global is probably not changing, but the elements that it and your .dup help are changing. To make it more canonical ruby, get rid of the global one, use the class and read the file in the initialize function. (Constructor.) Make an array of instance variable with @v.

+2
Sep 23 '09 at 12:52
source share



All Articles