はじめに
移植やってます。
( from python 3.7 to ruby 2.7 )
辞書クラスの拡張 (Python)
from collections import defaultdict
class BasicComposition(defaultdict):
def __init__(self, *args, **kwargs):
defaultdict.__init__(self, int)
for k, v in list(self.items()):
if not v:
del self[k]
def __missing__(self, key):
return 'nil'
def __setitem__(self, key, value):
if isinstance(value, float):
value = int(round(value))
elif not isinstance(value, int):
print('Error')
if value:
super(BasicComposition, self).__setitem__(key, value)
elif key in self:
del self[key]
d = BasicComposition()
d.update({'A': 1, 'B': 2.1, 'C': 0})
d['D'] = 0
d['E'] = 1.2
for k, v in d.items():
print(k, v)
print(d['F'])
# output
A 1
B 2.1
C 0
E 1
nil
コメント欄にてご指摘いただいた通り、非整数を代入することが可能です。
運用で逃げようとも思いましたが、別件の多重継承をどうするかという話もありますので、考えてみました。
継承案 (Ruby)
class BasicComposition < Hash
def initialize(*args, **kwargs)
self.default = 0
if args
args.each do |x|
self[x] += 1
end
end
if kwargs
kwargs.each do |k, v|
self[k] = v
end
end
end
def []=(key, value)
if value.instance_of?(Float)
value = value.round
elsif value.instance_of?(Integer).!
raise TypeError, 'IntegerもしくはFloat以外が指定されました。'
end
if value != 0 # reject 0's
self.merge!({key => value})
elsif self.include?(key)
self.delete(key)
end
end
end
d = BasicComposition.new
d.merge!({'A' => 1, 'B' => 2.1, 'C' => 0})
d['D'] = 0
d['E'] = 1.2
d.each do |k, v|
p [k, v]
end
puts d['F']
# output
["A", 1]
["B", 2.1]
["C", 0]
["E", 1]
0
これは、単に移植しただけですので、非整数の代入が可能です。
委譲案1 (Ruby)
class BasicComposition
def initialize(*args, **kwargs)
@h = Hash.new(0)
if args
args.each do |x|
@h[x] += 1
end
end
if kwargs
kwargs.each do |k, v|
@h[k] = v
end
end
end
def []=(key, value)
if value.instance_of?(Float)
value = value.round
elsif value.instance_of?(Integer).!
raise TypeError, 'IntegerもしくはFloat以外が指定されました。'
end
if value != 0 # reject 0's
@h.merge!({key => value})
elsif self.include?(key)
@h.delete(key)
end
end
def merge!(**kwargs)
if kwargs
kwargs.each do |k, v|
self[k] = v
end
end
end
def include?(key)
@h.include?(key)
end
def [](key)
@h[key]
end
def h
@h
end
end
d = BasicComposition.new
d.merge!('A' => 1, 'B' => 2.1, 'C' => 0)
d['D'] = 0
d['E'] = 1.2
d.h.each do |k, v|
p [k, v]
end
puts d['F']
# output
["A", 1]
["B", 2]
["E", 1]
0
独習Ruby 450p に委譲の説明があります。
# class BasicComposition < Hash
@h = Hash.new(0)
Hash
を継承
するのではなく、内部に保持(has)
します。
d.h.each
のところが格好悪いのですが、'B'
の値が整数となり、'C' => 0
が削除されました。
委譲案2 (Ruby)
def merge!(**kwargs)
# if kwargs
# kwargs.each do |k, v|
# self[k] = v
# end
# end
end
# output
["E", 1]
0
委譲案1のコードで、def merge!
を空のメソッドにします。
結果として、merge!
メソッドからの値変更をスルーすることにより、非整数の代入を防ぎます。
forwardable (Ruby)
require 'forwardable'
class BasicComposition
extend Forwardable
def initialize(*args, **kwargs)
@h = Hash.new(0)
if args
args.each do |x|
@h[x] += 1
end
end
if kwargs
kwargs.each do |k, v|
@h[k] = v
end
end
end
def_delegators :@h, :[], :include?, :each
def []=(key, value)
if value.instance_of?(Float)
value = value.round
elsif value.instance_of?(Integer).!
raise TypeError, 'IntegerもしくはFloat以外が指定されました。'
end
if value != 0 # reject 0's
@h.merge!({key => value})
elsif self.include?(key)
@h.delete(key)
end
end
def merge!(**kwargs)
if kwargs
kwargs.each do |k, v|
self[k] = v
end
end
end
end
d = BasicComposition.new
d.merge!('A' => 1, 'B' => 2.1, 'C' => 0)
d['D'] = 0
d['E'] = 1.2
d.each do |k, v|
p [k, v]
end
puts d['F']
# output
["A", 1]
["B", 2]
["E", 1]
0
require 'forwardable'
つよいですね。
d.each
になってスッキリしています。
@Nabetani さん、ご指摘ありがとうございました。
メモ
- Python の 辞書クラスの拡張2 を学習した
- 百里を行く者は九十里を半ばとす