"append-only" / "write-only" 散列 Ruby
An "append-only" / "write-only" hash in Ruby
我正在寻找一种 "append-only" 哈希,其中键只能设置一次。
例如:
capitals = AppendOnlyHash.new
capitals['france'] = 'paris'
capitals['japan'] = 'tokyo'
capitals['france'] = 'nice' # raises immutable exception
任何图书馆建议或想法如何实现这一目标?
(用例是一个日志类型的对象,它将被传递给松散连接的 numerouis 类,并且想要检测是否有任何使用相同的密钥。)
第一个想法,我没有考虑任何缺点:
class HashImmutable < Hash
def []=(key,val)
if self[key].frozen?
super(key,val)
else
# self[key]
raise 'Immutable'
end
end
end
hh = HashImmutable.new
hh[:france] = 'Paris'
hh[:italy] = 'Roma'
hh #=> {:france=>"Paris", :italy=>"Roma"}
hh[:italy] = 'Brescia'
#=> Immutable (RuntimeError)
这是创建这样一个 class 的幼稚尝试。它似乎适用于 "basic" 用法:
class AppendOnlyHash < Hash
def []=(key, value)
raise "APPEND ONLY!!" if keys.include?(key)
super
end
end
但是,这肯定有一些缺陷。
首先,如果您在对象上调用一个试图删除一些键的破坏性方法,会发生什么情况?也许您可以覆盖所有此类方法 - 即 filter!
, keep_if
、delete
、compact!
、reject!
、select!
、transform_keys!
和 transform_values!
. (我有没有漏掉什么?...)
那么,Hash#merge!
怎么办?我想这也可以特殊处理;因为使用 if 是有效的,所以没有键被重新定义。
最后,您如何确保 "append-only" 哈希值永远不会 突变 ?考虑以下因素:
capitals = AppendOnlyHash.new
str = "paris"
capitals['france'] = str
str << " CHANGED"
您可以在每个值添加到哈希时调用 .freeze
,但即使这样也不是 100% 防弹的 - 因为该值可能反过来是另一个 Hash
,这很容易受到同样的行为。
所以总而言之,我认为这可以通过我上面的基本实现实现,但我会对 "weird ways".
中的异议突变引起的日益复杂的边缘情况持谨慎态度
有10种方法,直接对hash进行变异:
Hash.instance_methods.grep(/.+!\z/) << %i|[]= delete keep_if|
#⇒ [:select!, :filter!, :reject!, :compact!, delete, keep_if,
# :transform_keys!, :transform_values!, :merge!, :[]=]
此外,值本身有可能发生变异(capitals['france'] << ' and Lyon'
,)所以我们也要防止这种情况发生。
class MyHash < Hash; end
MyHash.prepend(
Module.new do
(Hash.instance_methods.grep(/.+!\z/) | %i|delete keep_if|).each do |method|
define_method(method) do |*args|
raise "Method #{method} is restricted since it is mutating"
end
end
def []=(key, val)
raise "This hash is immutable" if key?(key)
super(key, val.freeze) # to prevent inplace mutations
end
end
)
需要从 Hash
派生,否则我们将破坏所有哈希值。
我没有测试这段代码,但它应该开箱即用,(如果没有,这个想法应该很清楚。)
我正在寻找一种 "append-only" 哈希,其中键只能设置一次。
例如:
capitals = AppendOnlyHash.new
capitals['france'] = 'paris'
capitals['japan'] = 'tokyo'
capitals['france'] = 'nice' # raises immutable exception
任何图书馆建议或想法如何实现这一目标?
(用例是一个日志类型的对象,它将被传递给松散连接的 numerouis 类,并且想要检测是否有任何使用相同的密钥。)
第一个想法,我没有考虑任何缺点:
class HashImmutable < Hash
def []=(key,val)
if self[key].frozen?
super(key,val)
else
# self[key]
raise 'Immutable'
end
end
end
hh = HashImmutable.new
hh[:france] = 'Paris'
hh[:italy] = 'Roma'
hh #=> {:france=>"Paris", :italy=>"Roma"}
hh[:italy] = 'Brescia'
#=> Immutable (RuntimeError)
这是创建这样一个 class 的幼稚尝试。它似乎适用于 "basic" 用法:
class AppendOnlyHash < Hash
def []=(key, value)
raise "APPEND ONLY!!" if keys.include?(key)
super
end
end
但是,这肯定有一些缺陷。
首先,如果您在对象上调用一个试图删除一些键的破坏性方法,会发生什么情况?也许您可以覆盖所有此类方法 - 即 filter!
, keep_if
、delete
、compact!
、reject!
、select!
、transform_keys!
和 transform_values!
. (我有没有漏掉什么?...)
那么,Hash#merge!
怎么办?我想这也可以特殊处理;因为使用 if 是有效的,所以没有键被重新定义。
最后,您如何确保 "append-only" 哈希值永远不会 突变 ?考虑以下因素:
capitals = AppendOnlyHash.new
str = "paris"
capitals['france'] = str
str << " CHANGED"
您可以在每个值添加到哈希时调用 .freeze
,但即使这样也不是 100% 防弹的 - 因为该值可能反过来是另一个 Hash
,这很容易受到同样的行为。
所以总而言之,我认为这可以通过我上面的基本实现实现,但我会对 "weird ways".
中的异议突变引起的日益复杂的边缘情况持谨慎态度有10种方法,直接对hash进行变异:
Hash.instance_methods.grep(/.+!\z/) << %i|[]= delete keep_if|
#⇒ [:select!, :filter!, :reject!, :compact!, delete, keep_if,
# :transform_keys!, :transform_values!, :merge!, :[]=]
此外,值本身有可能发生变异(capitals['france'] << ' and Lyon'
,)所以我们也要防止这种情况发生。
class MyHash < Hash; end
MyHash.prepend(
Module.new do
(Hash.instance_methods.grep(/.+!\z/) | %i|delete keep_if|).each do |method|
define_method(method) do |*args|
raise "Method #{method} is restricted since it is mutating"
end
end
def []=(key, val)
raise "This hash is immutable" if key?(key)
super(key, val.freeze) # to prevent inplace mutations
end
end
)
需要从 Hash
派生,否则我们将破坏所有哈希值。
我没有测试这段代码,但它应该开箱即用,(如果没有,这个想法应该很清楚。)