以查找

时间:2016-10-25 11:56:42

标签: ruby

关于调查员链的情况,我无法理解:

[1, 2, 3, 4, 5].find.map { |x| x * x }
#=> [1, 4, 9, 16, 25]

这会返回初始值方块的数组,但我希望它只返回[1]

我尝试解构所有内容,这就是我所取得的成果:.map在原始数组的find枚举器上调用。它会调用each来获取迭代值。枚举器上的each委托迭代到创建枚举数的方法,即findfind获取数组的第一个元素,将其生成,然后一直生成,直到它到达示例中的块。值得到平方,块返回它,each定义中的基础map块返回[1],它下降到find,因为它true在布尔意义上,我希望find在这一点上返回,有效地结束迭代,但不知怎的,它一直在从数组中一直提供值到map块。

这不是一个现实世界的例子,我只是想了解如何正确阅读这些链,这个案子让我感到困惑。

UPD

由于有人多次建议find在没有阻止的情况下调用{&#39}默认'枚举器,这是一个例子:

[1, 2, 3, 4, 5].find
#=> #<Enumerator: [1, 2, 3, 4, 5]:find>

[1, 2, 3, 4, 5].find.each { |x| x < 4 }
#=> 1

1 个答案:

答案 0 :(得分:0)

好的,我终于明白了。

find在块处理第一个值之后没有终止迭代的原因是,collect_i枚举模块的collect又名map方法中的迭代器显式返回每次迭代后nil,无论通过调用mapcollect提供的块的返回值是多少。这是来自enum.c

static VALUE
collect_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
{
    rb_ary_push(ary, enum_yield(argc, argv));

    return Qnil;
}

因此,对初始数组的find的内部调用总是得到nil作为产生值的结果,因此不会停止迭代直到处理完最后一个元素。通过将ruby作为存档下载并修改此函数,可以很容易地证明这一点:

static VALUE
collect_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
{
    rb_ary_push(ary, enum_yield(argc, argv));

    return ary;
}

从修改后的源代码保存并构建ruby之后,我们得到了这个:

irb(main):001:0> [1,2,3,4,5].find.map { |x| x*x } => [1]

另一个有趣的事情是,Rubinius完全按照这个方式实现了collect,所以我认为MRI和Rubinius有可能为这个陈述产生不同的结果。我现在没有安装Rubinius,但是当我有机会时我会检查这个,并用结果更新这篇文章。

不确定这对任何人是否有用,除非是为了满足一个人的好奇心,但仍然:)