Ruby通过将块传递给Hash.new
来支持哈希的自动生成:
hash = Hash.new { |h, k| h[k] = 42 }
hash[:foo] += 1 # => 43
我也想为结构实现自动修复。这是我能想到的最好的:
Foo = Struct.new(:bar) do
def bar
self[:bar] ||= 42
end
end
foo = Foo.new
foo.bar += 1 # => 43
当然,这只会对命名的访问者(foo.bar
)进行自动生成,而不是[]
表单(foo[:bar]
)。是否有更好的方法来实现结构的自动修复,特别是对foo.bar
和foo[:bar]
形式有效的结构?
答案 0 :(得分:3)
我会采用以下方法:
foo.bar
foo[:bar]
以上通过method_missing
魔术调用Struct#[]
,因此唯一要覆盖的是Hash#default_proc
方法。
预先安装模块使其更加健壮,每个实例并且通常更灵活。
上面的代码只是一个例子。要复制module StructVivificator
def self.prepended(base)
raise 'Sorry, structs only!' unless base < Struct
base.singleton_class.prepend(Module.new do
def new(*args, &λ) # override `new` to accept block
super(*args).tap { @λ = λ }
end
end)
base.send(:define_method, :default_proc=) { |λ| @λ = λ }
base.send(:define_method, :default_proc) { |&λ| λ ? @λ = λ : @λ }
# override accessors (additional advantage: performance/clarity)
base.members.each do |m|
base.send(:define_method, m) { self[m] }
base.send(:define_method, "#{m}=") { |value| self[m] = value }
end
end
def [](name)
super || default_proc && default_proc.(name) # or more sophisticated checks
end
end
一个人的行为(可以归功于@Stefan评论):
default_proc
现在name
lambda将收到一个Foo = Struct.new(:bar, :baz) do
prepend StructVivificator
end
foo = Foo.new
foo.default_proc = ->(name) { name == :bar ? 42 : 0 }
puts foo.bar # => 42
puts foo[:bar] += 1 # => 43
puts foo.bar += 1 # => 44
puts foo[:baz] += 1 # => 1
来决定如何在这种情况下表现。
<a href="/policies/shipping-policy" target="_blank" data-bind="i18n: 'Shipping Policy'"></a>