gpt4 book ai didi

ruby Enumerable#first vs #take

转载 作者:数据小太阳 更新时间:2023-10-29 07:30:05 24 4
gpt4 key购买 nike

ruby Enumerable/Array first(n)take(n) 有什么区别?

我依稀记得 take 与惰性评估有关,但我不知道如何使用它来做到这一点,也找不到任何有用的谷歌搜索或文档。 “take”是一个很难用谷歌搜索的方法名称。

first(n)take(n)documented完全相同,不是太有帮助。

first → obj or nil
first(n) → an_array
Returns the first element, or the first n elements, of the enumerable. If the enumerable is empty, the first form returns nil, and the second form returns an empty array.

-

take(n) → array
Returns first n elements from enum.

告诉我“take 与惰性求值有关”是不够的,我有点记得,我需要一个例子来说明如何使用它,与 first 相比。

最佳答案

好吧,我查看了源代码 (Ruby 2.1.5)。 在引擎盖下,如果first提供了一个参数,它将它转发给take .否则,它返回一个值:

static VALUE
enum_first(int argc, VALUE *argv, VALUE obj)
{
NODE *memo;
rb_check_arity(argc, 0, 1);
if (argc > 0) {
return enum_take(obj, argv[0]);
}
else {
memo = NEW_MEMO(Qnil, 0, 0);
rb_block_call(obj, id_each, 0, 0, first_i, (VALUE)memo);
return memo->u1.value;
}
}

take ,另一方面,需要一个参数,并且总是返回一个给定大小或更小的数组,其中的元素是从头开始的。

static VALUE
enum_take(VALUE obj, VALUE n)
{
NODE *memo;
VALUE result;
long len = NUM2LONG(n);

if (len < 0) {
rb_raise(rb_eArgError, "attempt to take negative size");
}

if (len == 0) return rb_ary_new2(0);
result = rb_ary_new2(len);
memo = NEW_MEMO(result, 0, len);
rb_block_call(obj, id_each, 0, 0, take_i, (VALUE)memo);
return result;
}

是的,这就是这两者如此相似的原因。唯一的区别似乎是 first可以在没有参数的情况下调用,并且不会输出数组,而是单个值<...>.first(1) , 另一方面,相当于 <...>.take(1) .就这么简单。

然而,对于惰性集合,情况就不同了。 first在惰性集合中仍然是 enum_first如上所示,这是 enum_take 的快捷方式. take ,但是,是 C 编码的 lazy_take :

static VALUE
lazy_take(VALUE obj, VALUE n)
{
long len = NUM2LONG(n);
VALUE lazy;

if (len < 0) {
rb_raise(rb_eArgError, "attempt to take negative size");
}
if (len == 0) {
VALUE len = INT2FIX(0);
lazy = lazy_to_enum_i(obj, sym_cycle, 1, &len, 0);
}
else {
lazy = rb_block_call(rb_cLazy, id_new, 1, &obj,
lazy_take_func, n);
}
return lazy_set_method(lazy, rb_ary_new3(1, n), lazy_take_size);
}

...不会立即评估,需要 .force呼吁这一点。

事实上,它暗示了in the docs under lazy ,它列出了所有延迟实现的方法。该列表确实包含 take , 但不包含 first .也就是说,在惰性序列上 take保持懒惰和first没有。

这是一个示例,它们的工作方式有何不同:

lz = (1..Float::INFINITY).lazy.map{|i| i }
# An infinite sequence, evaluating it head-on won't do
# Ruby 2.2 also offers `.map(&:itself)`

lz.take(5)
#=> #<Enumerator::Lazy: ...>
# Well, `take` is lazy then
# Still, we need values

lz.take(5).force
#=> [1, 2, 3, 4, 5]
# Why yes, values, finally

lz.first(5)
#=> [1, 2, 3, 4, 5]
# So `first` is not lazy, it evaluates values immediately

在 2.2 之前的版本中运行并使用 2.2 的代码 ( <...>.lazy.map(&:itself) ) 可以获得一些额外的乐趣,因为那样一来你失去懒惰的那一刻会立即引发 NoMethodError .

关于ruby Enumerable#first vs #take,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28417601/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com