[前][次][番号順一覧][スレッド一覧]

ruby-changes:40423

From: nobu <ko1@a...>
Date: Mon, 9 Nov 2015 21:27:43 +0900 (JST)
Subject: [ruby-changes:40423] nobu:r52504 (trunk): dig

nobu	2015-11-09 21:27:26 +0900 (Mon, 09 Nov 2015)

  New Revision: 52504

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=52504

  Log:
    dig
    
    * array.c (rb_ary_dig): new method Array#dig.
    * hash.c (rb_hash_dig): new method Hash#dig.
    * object.c (rb_obj_dig): dig in nested arrays/hashes.
      [Feature #11643]

  Modified files:
    trunk/ChangeLog
    trunk/NEWS
    trunk/array.c
    trunk/hash.c
    trunk/internal.h
    trunk/object.c
    trunk/test/ruby/test_array.rb
    trunk/test/ruby/test_hash.rb
Index: array.c
===================================================================
--- array.c	(revision 52503)
+++ array.c	(revision 52504)
@@ -1302,7 +1302,7 @@ rb_ary_aref(int argc, const VALUE *argv, https://github.com/ruby/ruby/blob/trunk/array.c#L1302
  *     a.at(-1)    #=> "e"
  */
 
-static VALUE
+VALUE
 rb_ary_at(VALUE ary, VALUE pos)
 {
     return rb_ary_entry(ary, NUM2LONG(pos));
@@ -5532,6 +5532,29 @@ rb_ary_any_p(VALUE ary) https://github.com/ruby/ruby/blob/trunk/array.c#L5532
 }
 
 /*
+ * call-seq:
+ *   ary.dig(idx, ...)                 -> object
+ *
+ * Retrieves the value object corresponding to the each <i>idx</i>
+ * objects repeatedly.
+ *
+ *   a = [[1, [2, 3]]]
+ *
+ *   a.dig(0, 1, 1)                    #=> 3
+ *   a.dig(0, 0, 0)                    #=> nil
+ */
+
+VALUE
+rb_ary_dig(int argc, VALUE *argv, VALUE self)
+{
+    rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
+    self = rb_ary_at(self, *argv);
+    if (!--argc) return self;
+    ++argv;
+    return rb_obj_dig(argc, argv, self, Qnil);
+}
+
+/*
  *  Arrays are ordered, integer-indexed collections of any object.
  *
  *  Array indexing starts at 0, as in C or Java.  A negative index is assumed
@@ -5882,6 +5905,7 @@ Init_Array(void) https://github.com/ruby/ruby/blob/trunk/array.c#L5905
     rb_define_method(rb_cArray, "bsearch", rb_ary_bsearch, 0);
     rb_define_method(rb_cArray, "bsearch_index", rb_ary_bsearch_index, 0);
     rb_define_method(rb_cArray, "any?", rb_ary_any_p, 0);
+    rb_define_method(rb_cArray, "dig", rb_ary_dig, -1);
 
     id_cmp = rb_intern("<=>");
     id_random = rb_intern("random");
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 52503)
+++ ChangeLog	(revision 52504)
@@ -1,3 +1,12 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Mon Nov  9 21:27:23 2015  Nobuyoshi Nakada  <nobu@r...>
+
+	* array.c (rb_ary_dig): new method Array#dig.
+
+	* hash.c (rb_hash_dig): new method Hash#dig.
+
+	* object.c (rb_obj_dig): dig in nested arrays/hashes.
+	  [Feature #11643]
+
 Mon Nov  9 18:00:47 2015  Yuki Nishijima  <mail@y...>
 
 	* gems/bundled_gems: Upgrade the did_you_mean gem to 1.0.0.beta3
Index: object.c
===================================================================
--- object.c	(revision 52503)
+++ object.c	(revision 52504)
@@ -3144,6 +3144,52 @@ rb_f_hash(VALUE obj, VALUE arg) https://github.com/ruby/ruby/blob/trunk/object.c#L3144
     return rb_Hash(arg);
 }
 
+struct dig_method {
+    VALUE klass;
+    int basic;
+};
+
+static ID id_dig;
+
+static int
+dig_basic_p(VALUE obj, struct dig_method *cache)
+{
+    VALUE klass = RBASIC_CLASS(obj);
+    if (klass != cache->klass) {
+	cache->klass = klass;
+	cache->basic = rb_method_basic_definition_p(klass, id_dig);
+    }
+    return cache->basic;
+}
+
+VALUE
+rb_obj_dig(int argc, VALUE *argv, VALUE obj, VALUE notfound)
+{
+    struct dig_method hash = {Qnil}, ary = {Qnil};
+
+    for (; argc > 0; ++argv, --argc) {
+	if (!SPECIAL_CONST_P(obj)) {
+	    switch (BUILTIN_TYPE(obj)) {
+	      case T_HASH:
+		if (dig_basic_p(obj, &hash)) {
+		    obj = rb_hash_aref(obj, *argv);
+		    continue;
+		}
+		break;
+	      case T_ARRAY:
+		if (dig_basic_p(obj, &ary)) {
+		    obj = rb_ary_at(obj, *argv);
+		    continue;
+		}
+	    }
+	}
+	obj = rb_check_funcall(obj, id_dig, argc, argv);
+	if (obj == Qundef) return notfound;
+	break;
+    }
+    return obj;
+}
+
 /*
  *  Document-class: Class
  *
@@ -3521,5 +3567,6 @@ void https://github.com/ruby/ruby/blob/trunk/object.c#L3567
 Init_Object(void)
 {
     id_to_f = rb_intern_const("to_f");
+    id_dig = rb_intern_const("dig");
     InitVM(Object);
 }
Index: hash.c
===================================================================
--- hash.c	(revision 52503)
+++ hash.c	(revision 52504)
@@ -2691,6 +2691,29 @@ rb_hash_any_p(VALUE hash) https://github.com/ruby/ruby/blob/trunk/hash.c#L2691
     return ret;
 }
 
+/*
+ * call-seq:
+ *   hsh.dig(key, ...)                 -> object
+ *
+ * Retrieves the value object corresponding to the each <i>key</i>
+ * objects repeatedly.
+ *
+ *   h = { foo: {bar: {baz: 1}}}
+ *
+ *   h.dig(:foo, :bar, :baz)           #=> 1
+ *   h.dig(:foo, :zot)                 #=> nil
+ */
+
+VALUE
+rb_hash_dig(int argc, VALUE *argv, VALUE self)
+{
+    rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
+    self = rb_hash_aref(self, *argv);
+    if (!--argc) return self;
+    ++argv;
+    return rb_obj_dig(argc, argv, self, Qnil);
+}
+
 static int path_tainted = -1;
 
 static char **origenviron;
@@ -4114,6 +4137,7 @@ Init_Hash(void) https://github.com/ruby/ruby/blob/trunk/hash.c#L4137
     rb_define_method(rb_cHash,"compare_by_identity?", rb_hash_compare_by_id_p, 0);
 
     rb_define_method(rb_cHash, "any?", rb_hash_any_p, 0);
+    rb_define_method(rb_cHash, "dig", rb_hash_dig, -1);
 
     /* Document-class: ENV
      *
Index: NEWS
===================================================================
--- NEWS	(revision 52503)
+++ NEWS	(revision 52504)
@@ -42,6 +42,7 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L42
 * Array
 
   * Array#bsearch_index [Feature #10730]
+  * Array#dig [Feature #11643]
 
 * Enumerable
 
@@ -57,6 +58,7 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L58
 * Hash
 
   * Hash#fetch_values [Feature #10017]
+  * Hash#dig [Feature #11643]
 
 * IO
 
Index: internal.h
===================================================================
--- internal.h	(revision 52503)
+++ internal.h	(revision 52504)
@@ -653,6 +653,7 @@ VALUE rb_ary_last(int, const VALUE *, VA https://github.com/ruby/ruby/blob/trunk/internal.h#L653
 void rb_ary_set_len(VALUE, long);
 void rb_ary_delete_same(VALUE, VALUE);
 VALUE rb_ary_tmp_new_fill(long capa);
+VALUE rb_ary_at(VALUE, VALUE);
 size_t rb_ary_memsize(VALUE);
 #ifdef __GNUC__
 #define rb_ary_new_from_args(n, ...) \
@@ -968,6 +969,7 @@ void rb_obj_copy_ivar(VALUE dest, VALUE https://github.com/ruby/ruby/blob/trunk/internal.h#L969
 VALUE rb_obj_equal(VALUE obj1, VALUE obj2);
 VALUE rb_class_search_ancestor(VALUE klass, VALUE super);
 double rb_num_to_dbl(VALUE val);
+VALUE rb_obj_dig(int argc, VALUE *argv, VALUE self, VALUE notfound);
 
 struct RBasicRaw {
     VALUE flags;
Index: test/ruby/test_array.rb
===================================================================
--- test/ruby/test_array.rb	(revision 52503)
+++ test/ruby/test_array.rb	(revision 52504)
@@ -2651,6 +2651,12 @@ class TestArray < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_array.rb#L2651
     end
   end
 
+  def test_dig
+    h = @cls[@cls[{a: 1}], 0]
+    assert_equal(1, h.dig(0, 0, :a))
+    assert_nil(h.dig(1, 0))
+  end
+
   private
   def need_continuation
     unless respond_to?(:callcc, true)
Index: test/ruby/test_hash.rb
===================================================================
--- test/ruby/test_hash.rb	(revision 52503)
+++ test/ruby/test_hash.rb	(revision 52504)
@@ -1302,6 +1302,12 @@ class TestHash < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_hash.rb#L1302
     x = x
   end
 
+  def test_dig
+    h = @cls[a: @cls[b: [1, 2, 3]], c: 4]
+    assert_equal(1, h.dig(:a, :b, 0))
+    assert_nil(h.dig(:c, 1))
+  end
+
   class TestSubHash < TestHash
     class SubHash < Hash
       def reject(*)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]