なかだです。

[ruby-dev:19475]を読んでいてOptionParser#==を作っていないことに
気づいたんですが、インスタンス変数を比較するようにすると多いと
きに結構面倒です。そこで、インスタンス変数を含む同値性をテスト
するようなメソッドを提供するのはどうでしょうか。

  Kernel#same?(obj)	# => true/false

再帰的な構造を考えると何らかの制限が必要ですが、object
inspectionを一般化するまではお預けということで、とりあえず
Array#eql?と同レベルのところまで。


	* intern.h: prototypes; rb_tbl_equal

	* ruby.h: prototypes; rb_obj_same

	* hash.c (rb_tbl_equal): test equality of two st_table's with
	  method :==.

	* object.c (rb_obj_same): test if two objects have same
	  instance variables.

	* st.c (st_foreach): returns true if iteration finished
	  successively, or false if breaked out.

	* st.h (st_foreach): returns boolean.


Index: intern.h =================================================================== RCS file: /cvs/ruby/src/ruby/intern.h,v retrieving revision 1.110 diff -u -2 -p -r1.110 intern.h --- intern.h 3 Feb 2003 05:34:14 -0000 1.110 +++ intern.h 4 Feb 2003 04:31:30 -0000 @@ -242,4 +242,5 @@ VALUE rb_hash_aset _((VALUE, VALUE, VALU VALUE rb_hash_delete_if _((VALUE)); VALUE rb_hash_delete _((VALUE,VALUE)); +int rb_tbl_equal _((struct st_table*, struct st_table*)); int rb_path_check _((char*)); int rb_env_path_tainted _((void)); Index: ruby.h =================================================================== RCS file: /cvs/ruby/src/ruby/ruby.h,v retrieving revision 1.73 diff -u -2 -p -r1.73 ruby.h --- ruby.h 31 Jan 2003 04:00:17 -0000 1.73 +++ ruby.h 4 Feb 2003 05:15:01 -0000 @@ -510,4 +510,5 @@ VALUE rb_iv_set _((VALUE, const char*, V VALUE rb_equal _((VALUE,VALUE)); +VALUE rb_obj_same _((VALUE,VALUE)); EXTERN VALUE ruby_verbose, ruby_debug; Index: hash.c =================================================================== RCS file: /cvs/ruby/src/ruby/hash.c,v retrieving revision 1.94 diff -u -2 -p -r1.94 hash.c --- hash.c 3 Feb 2003 05:34:14 -0000 1.94 +++ hash.c 4 Feb 2003 04:55:49 -0000 @@ -842,17 +842,15 @@ struct equal_data { static int -equal_i(key, val1, data) +equal_i(key, val1, tbl) VALUE key, val1; - struct equal_data *data; + struct st_table *tbl; { VALUE val2; if (key == Qundef) return ST_CONTINUE; - if (!st_lookup(data->tbl, key, &val2)) { - data->result = Qfalse; + if (!st_lookup(tbl, key, &val2)) { return ST_STOP; } if (!rb_equal(val1, val2)) { - data->result = Qfalse; return ST_STOP; } @@ -860,10 +858,25 @@ equal_i(key, val1, data) } +int +rb_tbl_equal(tbl1, tbl2) + struct st_table *tbl1, *tbl2; +{ + if (tbl1 == tbl2) return Qtrue; + if (!tbl1) { + return tbl2->num_entries ? Qfalse : Qtrue; + } + else if (!tbl2) { + return tbl1->num_entries ? Qfalse : Qtrue; + } + if (tbl1->num_entries != tbl2->num_entries) return Qfalse; + if (st_foreach(tbl1, equal_i, (st_data_t)tbl2)) + return Qtrue; + return Qfalse; +} + static VALUE rb_hash_equal(hash1, hash2) VALUE hash1, hash2; { - struct equal_data data; - if (hash1 == hash2) return Qtrue; if (TYPE(hash2) != T_HASH) return Qfalse; @@ -874,9 +887,7 @@ rb_hash_equal(hash1, hash2) return Qfalse; - data.tbl = RHASH(hash2)->tbl; - data.result = Qtrue; - st_foreach(RHASH(hash1)->tbl, equal_i, (st_data_t)&data); - - return data.result; + if (st_foreach(RHASH(hash1)->tbl, equal_i, (st_data_t)RHASH(hash2)->tbl)) + return Qtrue; + return Qfalse; } Index: object.c =================================================================== RCS file: /cvs/ruby/src/ruby/object.c,v retrieving revision 1.103 diff -u -2 -p -r1.103 object.c --- object.c 3 Feb 2003 08:45:26 -0000 1.103 +++ object.c 4 Feb 2003 05:15:10 -0000 @@ -64,4 +64,34 @@ rb_obj_equal(obj1, obj2) VALUE +rb_obj_same(obj1, obj2) + VALUE obj1, obj2; +{ + struct st_table *iv1 = 0, *iv2 = 0; + + if (obj1 == obj2) return Qtrue; + + if (IMMEDIATE_P(obj1) || IMMEDIATE_P(obj2)) return Qfalse; + if (BUILTIN_TYPE(obj1) != BUILTIN_TYPE(obj2)) return Qfalse; + if (RBASIC(obj1)->klass != RBASIC(obj2)->klass) return Qfalse; + + switch (BUILTIN_TYPE(obj1)) { + case T_MODULE: + case T_CLASS: + case T_ICLASS: + if (!rb_tbl_equal(RCLASS(obj1)->m_tbl, RCLASS(obj1)->m_tbl)) + return Qfalse; + /* fall through */ + case T_OBJECT: + return rb_tbl_equal(ROBJECT(obj1)->iv_tbl, ROBJECT(obj1)->iv_tbl); + + default: + if (FL_TEST(obj1, FL_EXIVAR)) iv1 = rb_generic_ivar_table(obj1); + if (FL_TEST(obj2, FL_EXIVAR)) iv2 = rb_generic_ivar_table(obj2); + if ((iv1 || iv2) && !rb_tbl_equal(iv1, iv2)) return Qfalse; + return rb_equal(obj1, obj2); + } +} + +VALUE rb_obj_id(obj) VALUE obj; @@ -1319,4 +1349,5 @@ Init_Object() rb_define_method(rb_mKernel, "eql?", rb_obj_equal, 1); + rb_define_method(rb_mKernel, "same?", rb_obj_same, 1); rb_define_method(rb_mKernel, "hash", rb_obj_id, 0); Index: st.c =================================================================== RCS file: /cvs/ruby/src/ruby/st.c,v retrieving revision 1.27 diff -u -2 -p -r1.27 st.c --- st.c 9 Jan 2003 04:28:28 -0000 1.27 +++ st.c 4 Feb 2003 04:18:52 -0000 @@ -480,5 +480,5 @@ st_cleanup_safe(table, never) } -void +int st_foreach(table, func, arg) st_table *table; @@ -500,5 +500,5 @@ st_foreach(table, func, arg) break; case ST_STOP: - return; + return 0; case ST_DELETE: tmp = ptr; @@ -515,4 +515,5 @@ st_foreach(table, func, arg) } } + return 1; } Index: st.h =================================================================== RCS file: /cvs/ruby/src/ruby/st.h,v retrieving revision 1.6 diff -u -2 -p -r1.6 st.h --- st.h 9 Jan 2003 04:28:28 -0000 1.6 +++ st.h 4 Feb 2003 04:19:02 -0000 @@ -37,5 +37,5 @@ int st_delete_safe(st_table *, st_data_t int st_insert(st_table *, st_data_t, st_data_t); int st_lookup(st_table *, st_data_t, st_data_t *); -void st_foreach(st_table *, int (*)(), st_data_t); +int st_foreach(st_table *, int (*)(), st_data_t); void st_add_direct(st_table *, st_data_t, st_data_t); void st_free_table(st_table *);
-- --- 僕の前にBugはない。 --- 僕の後ろにBugはできる。 中田 伸悦