From: Nobuyoshi Nakada Date: 2008-02-13T15:17:06+09:00 Subject: [ruby-dev:33751] Re: HashからStructを作る なかだです。 At Wed, 13 Feb 2008 13:07:52 +0900, rubikitch@ruby-lang.org wrote in [ruby-dev:33734]: > あるいは、Hashのキーを固定する(追加不可能、存在しないキーにアクセスしたら例外) > メソッドを作成できないものでしょうか? > Hash#fix_keys, Hash#freeze_keys, Hash#fix, Hash#lock, Hash#pin, Hash#fixate… ためしに作ってみました。fix!,unfix!,fixed?を追加、[],[]=,delete で存在しないキーにアクセスしたらKeyErrorを発生します。 initialize,shift,clear,delete_ifなどの単独でキーを指定するのでな いものは例外にはなりません。これだとfixというのとはちょっとイメー ジが違うような気がしますが。 Index: hash.c =================================================================== --- hash.c (revision 15455) +++ hash.c (working copy) @@ -25,4 +25,5 @@ static VALUE rb_hash_s_try_convert(VALUE #define HASH_DELETED FL_USER1 #define HASH_PROC_DEFAULT FL_USER2 +#define HASH_FIXED FL_USER3 VALUE @@ -426,4 +427,25 @@ rb_hash_rehash(VALUE hash) } +VALUE +rb_hash_fix(VALUE hash) +{ + FL_SET(hash, HASH_FIXED); + return hash; +} + +VALUE +rb_hash_unfix(VALUE hash) +{ + FL_UNSET(hash, HASH_FIXED); + return hash; +} + +VALUE +rb_hash_fixed_p(VALUE hash) +{ + if (FL_TEST(hash, HASH_FIXED)) return Qtrue; + return Qfalse; +} + /* * call-seq: @@ -446,4 +468,7 @@ rb_hash_aref(VALUE hash, VALUE key) if (!RHASH(hash)->ntbl || !st_lookup(RHASH(hash)->ntbl, key, &val)) { + if (FL_TEST(hash, HASH_FIXED)) { + rb_raise(rb_eKeyError, "key not found"); + } return rb_funcall(hash, id_default, 1, key); } @@ -683,12 +708,17 @@ VALUE rb_hash_delete(VALUE hash, VALUE key) { - VALUE val; + VALUE val = Qundef; - rb_hash_modify(hash); - val = rb_hash_delete_key(hash, key); + rb_hash_modify_check(hash); + if (RHASH(hash)->ntbl) { + val = rb_hash_delete_key(hash, key); + } if (val != Qundef) return val; if (rb_block_given_p()) { return rb_yield(key); } + else if (FL_TEST(hash, HASH_FIXED)) { + rb_raise(rb_eKeyError, "key not found"); + } return Qnil; } @@ -736,8 +766,10 @@ rb_hash_shift(VALUE hash) struct shift_var var; - rb_hash_modify(hash); + rb_hash_modify_check(hash); var.key = Qundef; - rb_hash_foreach(hash, RHASH(hash)->iter_lev > 0 ? shift_i_safe : shift_i, - (st_data_t)&var); + if (RHASH(hash)->ntbl) { + rb_hash_foreach(hash, RHASH(hash)->iter_lev > 0 ? shift_i_safe : shift_i, + (st_data_t)&var); + } if (var.key != Qundef) { @@ -929,10 +961,14 @@ VALUE rb_hash_aset(VALUE hash, VALUE key, VALUE val) { - rb_hash_modify(hash); - if (TYPE(key) != T_STRING || st_lookup(RHASH(hash)->ntbl, key, 0)) { - st_insert(RHASH(hash)->ntbl, key, val); + rb_hash_modify_check(hash); + if (TYPE(key) != T_STRING || + (RHASH(hash)->ntbl && st_lookup(RHASH(hash)->ntbl, key, 0))) { + st_insert(rb_hash_tbl(hash), key, val); } else { - st_add_direct(RHASH(hash)->ntbl, rb_str_new4(key), val); + if (FL_TEST(hash, HASH_FIXED)) { + rb_raise(rb_eKeyError, "can't insert into fixed hash"); + } + st_add_direct(rb_hash_tbl(hash), rb_str_new4(key), val); } return val; @@ -2613,4 +2649,8 @@ Init_Hash(void) rb_define_method(rb_cHash,"compare_by_identity?", rb_hash_compare_by_id_p, 0); + rb_define_method(rb_cHash,"fix!", rb_hash_fix, 0); + rb_define_method(rb_cHash,"unfix!", rb_hash_unfix, 0); + rb_define_method(rb_cHash,"fixed?", rb_hash_fixed_p, 0); + #ifndef __MACOS__ /* environment variables nothing on MacOS. */ origenviron = environ; -- --- 僕の前にBugはない。 --- 僕の後ろにBugはできる。 中田 伸悦