From: nobu.nakada@... Date: 2000-12-18T00:21:48+09:00 Subject: [ruby-list:26582] Re: Hash.new なかだです。 At Sun, 17 Dec 2000 22:55:30 +0900, matz@zetabits.com (Yukihiro Matsumoto) wrote: > 今回の問題はデフォルト値が代入されないことだけが問題ではない > のだろうと思います。今度はきっと配列が共有されているので、 > modifyしたら思わぬ変更が行われてしまったと、ひっかかる人が出 > るでしょう。 この場合、別のところに代入されるものは当然別のオブジェクトと いうのが自然な気がします。 > たとえば、ブロックによるデフォルトならば、代入することに意味 > があるのかもしれませんが、参照しただけでデフォルト値が追加さ > れることには反対です。 > > ary =[] > ary[1000] > > だけで配列のサイズが拡大されてしまうような動作だと思います。 これは同感なのですが、ブロックを前提にすれば自動的に代入され るのも意外と違和感ないかも。ブロックを使えば普通に書いた場合毎 回別個になるはずだし、逆に代入しないとブロックの結果が失われて しまうような。 ちょっと試してみました。hash.c の HASH_AUTOADD をセットすれば 自動的に代入するようになります。 diff -rup current/eval.c devel/eval.c --- current/eval.c Tue Dec 12 23:19:13 2000 +++ devel/eval.c Sun Dec 17 11:44:04 2000 @@ -6101,6 +6101,41 @@ callargs(args) } } +VALUE +rb_proc_callv(proc, argc, argv) + VALUE proc; + int argc; + VALUE *argv; +{ + return proc_call(proc, argc > 0 ? rb_ary_new4(argc, argv) : Qundef); +} + +VALUE +#ifdef HAVE_STDARG_PROTOTYPES +rb_proc_call(VALUE proc, int argc, ...) +#else +rb_proc_call(proc, argc, va_alist) + VALUE proc; + int argc; + va_dcl +#endif +{ + VALUE *argv; + + if (argc > 0) { + int i; + va_list ar; + argv = ALLOCA_N(VALUE, argc); + + va_init_list(ar, argc); + for (i = 0; i < argc; i++) { + argv[i] = va_arg(ar, VALUE); + } + va_end(ar); + } + return rb_proc_callv(proc, argc, argv); +} + static VALUE proc_call(proc, args) VALUE proc, args; /* OK */ diff -rup current/hash.c devel/hash.c --- current/hash.c Tue Dec 5 23:19:16 2000 +++ devel/hash.c Sun Dec 17 12:06:26 2000 @@ -18,6 +18,8 @@ #include "rubysig.h" #define HASH_DELETED FL_USER1 +#define HASH_PROCDEF FL_USER2 +#define HASH_AUTOADD 0 /* FL_USER3 */ static void rb_hash_modify(hash) @@ -205,7 +207,14 @@ rb_hash_initialize(argc, argv, hash) rb_scan_args(argc, argv, "01", &ifnone); rb_hash_modify(hash); - RHASH(hash)->ifnone = ifnone; + if (argc == 0 && rb_block_given_p()) { + RHASH(hash)->ifnone = rb_f_lambda(); + FL_SET(hash, HASH_PROCDEF | HASH_AUTOADD); + } + else { + RHASH(hash)->ifnone = ifnone; + FL_UNSET(hash, HASH_PROCDEF | HASH_AUTOADD); + } return hash; } @@ -285,13 +294,39 @@ rb_hash_rehash(hash) } VALUE +rb_hash_autoadd(hash) + VALUE hash; +{ + return FL_TEST(hash, HASH_AUTOADD) ? Qtrue : Qfalse; +} + +VALUE +rb_hash_set_autoadd(hash, val) + VALUE hash, val; +{ + if (RTEST(val)) + FL_SET(hash, HASH_AUTOADD); + else + FL_UNSET(hash, HASH_AUTOADD); + return val; +} + +VALUE rb_hash_aref(hash, key) VALUE hash, key; { VALUE val; if (!st_lookup(RHASH(hash)->tbl, key, &val)) { - return RHASH(hash)->ifnone; + if (FL_TEST(hash, HASH_PROCDEF)) { + val = rb_proc_callv(RHASH(hash)->ifnone, 1, &key); + } + else { + val = RHASH(hash)->ifnone; + } + if (FL_TEST(hash, HASH_AUTOADD)) { + rb_hash_aset(hash, key, val); + } } return val; } @@ -326,6 +361,11 @@ static VALUE rb_hash_default(hash) VALUE hash; { + if (rb_block_given_p()) { + RHASH(hash)->ifnone = rb_f_lambda(); + FL_SET(hash, HASH_PROCDEF | HASH_AUTOADD); + return hash; + } return RHASH(hash)->ifnone; } @@ -337,6 +377,13 @@ rb_hash_set_default(hash, ifnone) return hash; } +static VALUE +rb_hash_has_block_p(hash) + VALUE hash; +{ + return FL_TEST(hash, HASH_PROCDEF) ? Qtrue : Qfalse; +} + static int index_i(key, value, args) VALUE key, value; @@ -1451,6 +1498,9 @@ Init_Hash() rb_define_method(rb_cHash,"store", rb_hash_aset, 2); rb_define_method(rb_cHash,"default", rb_hash_default, 0); rb_define_method(rb_cHash,"default=", rb_hash_set_default, 1); + rb_define_method(rb_cHash,"autoadd", rb_hash_autoadd, 0); + rb_define_method(rb_cHash,"autoadd=", rb_hash_set_autoadd, 1); + rb_define_method(rb_cHash,"has_block?", rb_hash_has_block_p, 0); rb_define_method(rb_cHash,"index", rb_hash_index, 1); rb_define_method(rb_cHash,"indexes", rb_hash_indexes, -1); rb_define_method(rb_cHash,"indices", rb_hash_indexes, -1); diff -rup current/intern.h devel/intern.h --- current/intern.h Fri Dec 8 23:19:13 2000 +++ devel/intern.h Sun Dec 17 11:45:12 2000 @@ -142,6 +142,8 @@ VALUE rb_protect _((VALUE (*)(), VALUE, void rb_set_end_proc _((void (*)(), VALUE)); void rb_mark_end_proc _((void)); void rb_exec_end_proc _((void)); +VALUE rb_proc_callv _((VALUE, int, VALUE*)); +VALUE rb_proc_call __((VALUE, int, ...)); void ruby_finalize _((void)); void ruby_stop _((int)); void rb_gc_mark_threads _((void)); -- --- 僕の前にBugはない。 --- 僕の後ろにBugはできる。 中田 伸悦