Ruby:如何复制这个数组?

6

(回答我的早前问题,Ruby:如何复制变量而不指向同一对象?

我正在编写一个简单的 Ruby 程序,在 .svg 文件中进行一些替换。第一步是从文件中提取信息并将其放入数组中。为了避免在每次调用该函数时从磁盘读取文件,我尝试使用记忆化设计模式 - 在第一次调用后的每次调用中使用缓存结果。

为此,我在函数之前定义了一个全局变量。但即使在返回本地变量之前将该变量复制(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 的新手,我相信这不是最 Rubyesque 的方法(而且我也不喜欢全局变量)。你能建议一个更好的策略吗?

我知道实际上应该是 $svg_filedata_cache[filename],以允许使用不同文件名的函数调用,但在这种情况下并不需要。 - Nathan Long
顺便提一下,全局对象和返回的对象具有不同的object_id,我相信你是在提到返回的数组内部的字符串,对吗? - khelll
@khell - 是的,我的说法是基于原始数组的内容被改变这一事实的。 - Nathan Long
3个回答

11

修改复制的数组不会影响原始数组。但是对数组中字符串的修改会在全局范围内可见,因为全局数组和复制的数组仍然包含对同一字符串的引用(dup不执行深拷贝)。

因此,要么执行深拷贝(svg_filedata_cache = $svg_filedata_cache.map {|line| line.dup}),要么避免对字符串进行变异操作。


1
我没有意识到数组中的每个字符串都是它自己的对象!我想这真的是“Ruby中的所有东西都是对象”的真理。 :) - Nathan Long

7

稍微优化一下代码:

$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

所以 ||= 的意思是“如果左边为空(假),那么使用右边?” - Nathan Long
3
这句话的意思是,仅当左侧变量没有被赋值时,将右侧的值赋给它。请注意,在进行翻译时,不能改变原文的意思。 - khelll

3

全局变量可能没有被修改,但是它和你的.dup引用所指向的元素被改变了。为了使代码更符合Ruby的规范,应该摒弃全局变量,使用类,并在initialize函数中读取文件。(即构造函数)将数组作为实例变量使用@v。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接