[trunk:feature] Hash#update with Enumerable

$B$J$+$@$G$9!#(B

$B:#$N$H$3$m(BHash#update$B$O(BHash$B$7$+<u$1IU$1$^$;$s$,!"(BEnumerable$B$^$G(B
$B<u$1IU$1$k$h$&$K3HD%$9$k$N$O$I$&$G$7$g$&$+!#(B

diff --git i/hash.c w/hash.c
index 873219a…8d0c72a 100644
— i/hash.c
+++ w/hash.c
@@ -1748,6 +1748,45 @@ rb_hash_update_block_i(VALUE key, VALUE value,
VALUE hash)
return ST_CONTINUE;
}

+static VALUE
+rb_enum_update_i(VALUE obj, VALUE hash, int argc, VALUE *argv)
+{

  • if (argc == 1 && RB_TYPE_P(obj, T_ARRAY)) {
  • argc = RARRAY_LEN(obj);
  • argv = RARRAY_PTR(obj);
  • }
  • if (argc == 2) {
  • st_insert(RHASH(hash)->ntbl, argv[0], argv[1]);
  • }
  • return Qnil;
    +}

+static VALUE
+rb_enum_update_block_i(VALUE obj, VALUE hash, int argc, VALUE *argv)
+{

  • VALUE key, value = Qundef, oldval;
  • if (argc == 1 && RB_TYPE_P(obj, T_ARRAY)) {
  • argc = RARRAY_LEN(obj);
  • argv = RARRAY_PTR(obj);
  • }
  • switch (argc) {
  •  case 2:
    
  • value = argv[1];
  •  case 1:
    
  • key = argv[0];
  • oldval = rb_hash_lookup2(hash, key, Qundef);
  • if (oldval != Qundef) {
  •  value = rb_yield_values(argc + 1, key, oldval, value);
    
  • }
  • else if (value == Qundef) {
  •  value = rb_yield(key);
    
  • }
  • st_insert(RHASH(hash)->ntbl, key, value);
  • break;
  • }
  • return Qnil;
    +}

/*

  • call-seq:
  • hsh.merge!(other_hash)                                 -> hsh
    

@@ -1769,13 +1808,36 @@ rb_hash_update_block_i(VALUE key, VALUE value,
VALUE hash)

  • h2 = { "b" => 254, "c" => 300 }
    
  • h1.merge!(h2) { |key, v1, v2| v1 }
    
  •                 #=> {"a"=>100, "b"=>200, "c"=>300}
    
    • If other_hash is not a Hash but an Enumerable, its #each
    • method should yield [key, newval] pairs or mere key. In the latter
    • case, thegiven block is always called regardless oldval, but
      without
    • newval.
    • h1 = { "a" => 100, "b" => 200 }
      
    • e1 = [["b", 254], ["c", 300]]
      
    • h1.merge!(e1) { |key, *v| v }
      
    •                 #=> {"a"=>100, "b"=>[200,254], "c"=>300}
      
    • h1 = { "a" => 100, "b" => 200 }
      
    • e1 = [["b"], ["c"]]
      
    • h1.merge!(e1) { |key, v1, v2| v1 }
      
    •                 #=> {"a"=>100, "b"=>[200], "c"=>[]}
      
    */

static VALUE
rb_hash_update(VALUE hash1, VALUE hash2)
{

  • VALUE tmp;
    rb_hash_modify(hash1);
  • hash2 = to_hash(hash2);
  • tmp = rb_check_hash_type(hash2);
  • if (NIL_P(tmp)) {
  • VALUE (*bfunc)(ANYARGS) = rb_block_given_p() ?
  •  rb_enum_update_block_i : rb_enum_update_i;
    
  • rb_each_call(hash2, 0, 0, bfunc, hash1);
  • return hash1;
  • }
  • hash2 = tmp;
    if (rb_block_given_p()) {
    rb_hash_foreach(hash2, rb_hash_update_block_i, hash1);
    }
    diff --git i/include/ruby/ruby.h w/include/ruby/ruby.h
    index 4a3e0ff…aa7808b 100644
    — i/include/ruby/ruby.h
    +++ w/include/ruby/ruby.h
    @@ -1164,6 +1164,7 @@ PRINTF_ARGS(void rb_compile_warn(const char ,
    int, const char
    , …), 3, 4);
    typedef VALUE rb_block_call_func(VALUE, VALUE, int, VALUE*);

VALUE rb_each(VALUE);
+VALUE rb_each_call(VALUE,int,VALUE*,VALUE(*)(ANYARGS),VALUE);
VALUE rb_yield(VALUE);
VALUE rb_yield_values(int n, …);
VALUE rb_yield_values2(int n, const VALUE *argv);
diff --git i/vm_eval.c w/vm_eval.c
index 05fd3fa…6f93897 100644
— i/vm_eval.c
+++ w/vm_eval.c
@@ -947,6 +947,12 @@ rb_each(VALUE obj)
return rb_call(obj, idEach, 0, 0, CALL_FCALL);
}

+VALUE
+rb_each_call(VALUE obj, int argc, VALUE *argv, VALUE
(*bl_proc)(ANYARGS), VALUE arg)
+{

  • return rb_block_call(obj, idEach, argc, argv, bl_proc, arg);
    +}

static VALUE
eval_string_with_cref(VALUE self, VALUE src, VALUE scope, NODE *cref,
const char *volatile file, volatile int line)
{