hash.c: add __pat_values() for pattern matching optimization

Add Hash#__pat_values(keys) that returns an array of values if all
keys exist, or false if any key is missing. This replaces per-key
key?() + []() calls (2N hash lookups) with a single method call
(N hash lookups). The compiler generates __pat_values() followed by
array indexing to extract each value for pattern matching.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2026-02-17 14:48:32 +09:00
parent 5f429e151a
commit 4e63489d1a
2 changed files with 55 additions and 21 deletions
+31
View File
@@ -2042,6 +2042,36 @@ mrb_hash_compact(mrb_state *mrb, mrb_value hash)
return hash;
}
/*
* Internal method for pattern matching key check + value extraction.
* Returns an array of values if all keys exist, false otherwise.
*
* {a: 1, b: 2}.__pat_values([:a, :b]) #=> [1, 2]
* {a: 1}.__pat_values([:a, :b]) #=> false
*/
static mrb_value
mrb_hash_pat_values(mrb_state *mrb, mrb_value hash)
{
mrb_value keys;
mrb_get_args(mrb, "A", &keys);
const mrb_value *ary = RARRAY_PTR(keys);
mrb_int klen = RARRAY_LEN(keys);
struct RHash *h = mrb_hash_ptr(hash);
mrb_value result = mrb_ary_new_capa(mrb, klen);
int ai = mrb_gc_arena_save(mrb);
for (mrb_int i = 0; i < klen; i++) {
mrb_value val;
if (!h_get(mrb, h, ary[i], &val)) {
return mrb_false_value();
}
mrb_ary_push(mrb, result, val);
mrb_gc_arena_restore(mrb, ai);
}
return result;
}
/*
* Internal method for pattern matching **rest.
* Returns a new hash excluding keys in the given array.
@@ -2314,6 +2344,7 @@ mrb_init_hash(mrb_state *mrb)
mrb_define_method_id(mrb, h, MRB_SYM(rassoc), mrb_hash_rassoc, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, h, MRB_SYM(__merge), mrb_hash_merge_m, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, h, MRB_SYM(__compact), mrb_hash_compact, MRB_ARGS_NONE()); /* implementation of Hash#compact! */
mrb_define_method_id(mrb, h, MRB_SYM(__pat_values), mrb_hash_pat_values, MRB_ARGS_REQ(1)); /* for pattern matching keys */
mrb_define_method_id(mrb, h, MRB_SYM(__except), mrb_hash_except_keys, MRB_ARGS_REQ(1)); /* for pattern matching **rest */
}
#undef lesser