Ruby:创建数组项目" on demand"

时间:2014-03-11 18:27:27

标签: ruby arrays

在Ruby with Hashes中我可以这样做:

h = Hash.new { |h,k| h[k] = "created #{k}" }

这样每当我尝试使用Hash中不存在的键访问项目时,它将调用该块并创建此新项目并使用该键存储。

使用Arrays有类似的方法吗?

2 个答案:

答案 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"]