Rubyでの多次元Hashの作成法 2 : 修正とクラス化
スコープを意識しながらよく見直してみるとなんか無駄にArrayに格納してることが判明。ってかなんでこんな事してたんだ俺w
よりスマートになった感じ。
dimension = 3 node = {} # 末端Hash (dimension-1).times do current = node node = Hash.new{ |hash, key| hash[key] = current.dup } end tree = node
ついでにクラス化してみた。
ArgumentError出すのにごにょごにょしてるのは、エラー時のstacktraceをHashと全く同じにしたいというだけの本質とは無関係な処理。
class MultidimensionHash < Hash attr_reader :dimension def initialize dimension, ifnone = nil, &default raise DimensionError if dimension <= 0 if ifnone if default traceback = caller traceback.unshift traceback.first.gsub(/`new'$/, "`initialize'") raise ArgumentError, "wrong number of arguments", traceback end node = Hash.new(ifnone) else node = Hash.new(&default) end (dimension-1).times do current = node node = Hash.new{ |hash, key| hash[key] = current.dup } end self.replace node @dimension = dimension end def self.[] dimension new dimension end class DimensionError < IndexError; end end
テスト
シンタックスシュガー的なHash[]*1も上書きしてるけど、MultidimensionHash[]なんて書けても嬉しくなさすw
require "test/unit" require "multidimension_hash" class MultidimensionHashTest < Test::Unit::TestCase def test_new (-16..0).each do |dimension| assert_raise(MultidimensionHash::DimensionError){ MultidimensionHash.new(dimension) } end (1..16).each do |dimension| mdhash = MultidimensionHash.new(dimension) assert_kind_of Hash, mdhash assert_equal dimension, mdhash.dimension (1..32).each do |d| reader = "mdhash"+"[:test]"*d if d < dimension assert_kind_of Hash, eval(reader) elsif d == dimension assert_equal nil, eval(reader) else assert_raise(NoMethodError){ eval(reader) } end end end end def test_new_with_default (1..16).each do |dimension| mdhash = MultidimensionHash.new(dimension, :default) reader = "mdhash"+"[:test]"*dimension assert_equal :default, eval(reader), "#{dimension}D" end end def test_new_with_default_block (1..16).each do |dimension| mdhash = MultidimensionHash.new(dimension){ |hash, key| hash[key] = :default_block } reader = "mdhash"+"[:test]"*dimension assert_equal :default_block, eval(reader), "#{dimension}D" end end def test_new_instance_with_default_and_default_block_exception errors = [] 2.times do |i| begin [Hash, MultidimensionHash][i].new(1, 1){} rescue => errors[i] end end assert_equal errors[0].class, errors[1].class assert_equal errors[0].message, errors[1].message assert_equal errors[0].backtrace, errors[1].backtrace end def test_new_syntax_sugar assert_equal MultidimensionHash.new(2), MultidimensionHash[2] end end
*1:半角だと表示されない……