在Ruby with Hashes中我可以这样做:
h = Hash.new { |h,k| h[k] = "created #{k}" }
这样每当我尝试使用Hash中不存在的键访问项目时,它将调用该块并创建此新项目并使用该键存储。
使用Arrays有类似的方法吗?
答案 0 :(得分:2)
Array.new
方法可以接收块。它传递元素的索引,块的结果存储在数组中。
Array.new(3) { |index| index ** 2 }
# => [0, 1, 4]
但是,您在调用方法时将创建所有元素。它们也将被存储在块中,并且无法阻止它。
我们可以继承Array
并实现所需的Hash
类行为。
class CustomArray < Array
def [](index)
if super.nil? then @default_proc.call self, index end
super
end
end
class << CustomArray
def new(*arguments, **keyword_arguments, &block)
if arguments.empty? and block
super().tap do |array|
array.instance_variable_set :@default_proc, block
end
else super end
end
end
这样,保留了通常的Array
API。如果尺寸参数未与块一起传递,则它将用作默认Proc
。
array = CustomArray.new { |array, index| array[index] = index + 2 }
p array[10]
# => 12
p array
# => [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 12]
看到它运行here。
请注意,由于nil
的含义,此实现存在问题。在这种情况下,由于数组如何工作,它被定义为 empty 值,这在连续内存的上下文中是合理的。如果将一个元素存储在一个大于数组大小的索引处,它将填充nil
的空格,以指示最后一个元素与刚刚插入的元素之间的空白区域。
如果nil
是有意义的值,请考虑使用带有整数键的Hash
而不是Array
。
答案 1 :(得分:1)
默认情况下,我不认为这是可能的。如果您愿意进行一些猴子修补,您可以自己添加类似的方法。例如,通过使用接受块的Array
方法扩展get
,您可以模拟您想要的内容。
当没有给出Block时,get
方法将像常规[]
一样。当您传递一个Block并且值为nil
时,它将存储来自块i
的块的任何结果。
class Array
def get(i, &block)
return self[i] if !block || !self[i].nil?
self[i] = block.call(i)
end
end
array = [1, 2]
array.get(0) # => 1
array.get(5) # => nil
array.get(5) { |i| "Created index #{i}" } # => "Created index 5"
p array # => [1, 2, nil, nil, nil, "Created index 5"]