Ruby Hash
的可枚举方法需要2个参数,一个用于键,一个用于值:
hash.each { |key, value| ... }
但是,我注意到当你只传递一个参数时,可枚举方法的行为是不一致的:
student_ages = {
"Jack" => 10,
"Jill" => 12,
}
student_ages.each { |single_param| puts "param: #{single_param}" }
student_ages.map { |single_param| puts "param: #{single_param}" }
student_ages.select { |single_param| puts "param: #{single_param}" }
student_ages.reject { |single_param| puts "param: #{single_param}" }
# results:
each...
param: ["Jack", 10]
param: ["Jill", 12]
map...
param: ["Jack", 10]
param: ["Jill", 12]
select...
param: Jack
param: Jill
reject...
param: Jack
param: Jill
正如您所看到的,对于each
和map
,单个参数会分配给[key, value]
数组,但对于select
和reject
,参数只是key
。
这种行为有特殊原因吗?文档似乎根本没有提到这一点;给出的所有示例都假设您传递了两个参数。
答案 0 :(得分:1)
我的猜测是内部map
只有each
collect
。有趣的是,它们的工作方式并不相同。
关于each
...
源代码如下。它会检查您传递给块的参数数量。如果不止一个,则会调用each_pair_i_fast
,否则只调用each_pair_i
。
static VALUE
rb_hash_each_pair(VALUE hash)
{
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
if (rb_block_arity() > 1)
rb_hash_foreach(hash, each_pair_i_fast, 0);
else
rb_hash_foreach(hash, each_pair_i, 0);
return hash;
}
each_pair_i_fast
返回两个不同的值:
each_pair_i_fast(VALUE key, VALUE value)
{
rb_yield_values(2, key, value);
return ST_CONTINUE;
}
each_pair_i
没有:
each_pair_i(VALUE key, VALUE value)
{
rb_yield(rb_assoc_new(key, value));
return ST_CONTINUE;
}
rb_assoc_new
返回一个两元素数组(至少我假设这是rb_ary_new3
所做的
rb_assoc_new(VALUE car, VALUE cdr)
{
return rb_ary_new3(2, car, cdr);
}
select
看起来像这样:
rb_hash_select(VALUE hash)
{
VALUE result;
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
result = rb_hash_new();
if (!RHASH_EMPTY_P(hash)) {
rb_hash_foreach(hash, select_i, result);
}
return result;
}
和select_i
看起来像这样:
select_i(VALUE key, VALUE value, VALUE result)
{
if (RTEST(rb_yield_values(2, key, value))) {
rb_hash_aset(result, key, value);
}
return ST_CONTINUE;
}
我将假设rb_hash_aset
返回两个与each_pair_i
类似的不同参数。
最重要的是注意select
/ etc根本不检查参数arity。
来源:
答案 1 :(得分:1)
刚检查了Rubinius的行为,这确实与CRuby一致。所以看看Ruby的实现 - 确实是因为#select
yields two values:
yield(item.key, item.value)
while #each
yields an array with two values:
yield [item.key, item.value]
为块生成两个值,期望一个取第一个参数并忽略第二个参数:
def foo
yield :bar, :baz
end
foo { |x| p x } # => :bar
如果块有一个参数,则生成一个数组将被完全赋值,或者如果有两个或多个参数,则获取解包并分配给每个单独的值(如果你逐个传递它们)。
def foo
yield [:bar, :baz]
end
foo { |x| p x } # => [:bar, :baz]
至于他们为什么做出这种决定 - 可能背后没有任何好理由,只是不期望人们用一个论点来称呼他们。