如何使我的枚举器接口接受Feed?

时间:2022-11-25 00:28:40

From the docs for Ruby v2.5

来自Ruby v2.5的文档

e = [1,2,3].map
p e.next           #=> 1
e.feed "a"
p e.next           #=> 2
e.feed "b"
p e.next           #=> 3
e.feed "c"
begin
  e.next
rescue StopIteration
  p $!.result      #=> ["a", "b", "c"]
end

But what about when I create my enum via Enumerator.new?

但是当我通过Enumerator.new创建枚举时呢?

# a naive rework of the above enum
e2 = Enumerator.new do |y|
  [1,2,3].each do |x|
    y << x
  end
  # raise StopIteration, FED # <= how to get `FED`?
end

p e2.next           #=> 1
e2.feed "a"
p e2.next           #=> 2
e2.feed "b"
p e2.next           #=> 3
e2.feed "c"
begin
  e2.next
rescue StopIteration
  p $!.result      #=> nil
end

How would I modify this to match the API?

我如何修改它以匹配API?

Things I have tried:

我尝试过的事情:

e2 = Enumerator.new do |y|
  [1,2,3].each do |x|
    @fed = yield
    y << x
  end
  raise StopIteration, @fed
end

e2 = Enumerator.new do |y|
  [1,2,3].each do |x|
    y << yield(x)
  end
  raise StopIteration, y
end

e2 = Enumerator.new do |y|
  enum = [1,2,3].each{|x| yield x }.to_enum
  y << enum.next
  raise StopIteration, y
end

Interestingly they all produce the same error when feed is called a second time:

有趣的是,当第二次调用feed时,它们都会产生相同的错误:

# Ignoring all the other errors that jump up…
p e2.next           #=> 1
e2.feed "a"
# nil
p e2.next           #=> 2
e2.feed "b"

TypeError: feed value already set

TypeError:已设置的Feed值

TypeError: feed value already set means it is collecting the the value somewhere, I just don't know how to access it.

TypeError:已设置的feed值意味着它正在某处收集值,我只是不知道如何访问它。

The C source for #feed:

#feed的C源代码:

static VALUE
enumerator_feed(VALUE obj, VALUE v)
{
    struct enumerator *e = enumerator_ptr(obj);

    if (e->feedvalue != Qundef) {
        rb_raise(rb_eTypeError, "feed value already set");
    }
    e->feedvalue = v;

    return Qnil;
}

So feedvalue is my target. I've dropped into the operation of the method using Pry but can't find a method or variable that appears to relate to feed or feedvalue. Rubinius makes this available explicitly (at least as an instance variable).

所以feedvalue是我的目标。我已经使用Pry进入了该方法的操作,但找不到与feed或feedvalue相关的方法或变量。 Rubinius明确地提供了这个(至少作为一个实例变量)。

I'm stumped.

Any help or insight would be much appreciated.

任何帮助或见解将不胜感激。

2 个解决方案

#1


1  

Your first example is enumerator with a yield method of :map:

你的第一个例子是枚举数,其yield方法为:map:

e = [1,2,3].map
=> #<Enumerator: [1, 2, 3]:map>

Your second example is an enumerator with yield method of :each.

你的第二个例子是一个枚举器,其yield方法为:each。

e2 = Enumerator.new do |y|
  [1,2,3].each do |x|
    y << x
  end
  # raise StopIteration, FED # <= how to get `FED`?
end
=> #<Enumerator: #<Enumerator::Generator:0x007fa69b056b50>:each> 

You should use to_enum or enum_for with the yield method of your choice:

您应该使用to_enum或enum_for和您选择的yield方法:

[1,2,3].to_enum(:map)
=> #<Enumerator: [1, 2, 3]:map>

Using ::new in the manner below is now deprecated, so I would NOT recommend using it in favor of to_enum or enum_for which offer the same functionality:

以下方式使用:: new现在已弃用,因此我不建议使用它来支持提供相同功能的to_enum或enum_for:

Enumerator.new([1,2,3], :map)

Summary

To sum this up, #map is the method being called by your first iterator, and when it is called, the return values of it will decide the value of the result. When using #each like your other examples, it doesn't matter what you end your block (#feed) with since it won't affect the return value of #each.

总结一下,#map是第一个迭代器调用的方法,当它被调用时,它的返回值将决定结果的值。当像其他例子一样使用#each时,你的块(#feed)结束并不重要,因为它不会影响#each的返回值。

#2


3  

The reason your first example works the way it does is because you've used #map which "passes the array's elements to "yield" and collects the results of "yield" as an array." There's an interesting note about it in Enumerator#feed http://ruby-doc.org/core-2.5.0/Enumerator.html#method-i-feed

你的第一个例子以它的方式工作的原因是因为你使用#map“将数组的元素传递给”yield“并将”yield“的结果收集为数组。”在Enumerator#feed中有一个有趣的注释http://ruby-doc.org/core-2.5.0/Enumerator.html#method-i-feed

Anyway, your custom enumerator will behave the same way as the Array in your first example if you also call map on it:

无论如何,如果您还在其上调用map,则自定义枚举器的行为方式与第一个示例中的Array相同:

e2 = Enumerator.new { |y|
  [1,2,3].each do |x|
    y << x
  end
}.map

p e2.next           #=> 1
e2.feed "a"
p e2.next           #=> 2
e2.feed "b"
p e2.next           #=> 3
e2.feed "c"
begin
  e2.next
rescue StopIteration
  p $!.result      #=> ["a", "b", "c"]
end

#1


1  

Your first example is enumerator with a yield method of :map:

你的第一个例子是枚举数,其yield方法为:map:

e = [1,2,3].map
=> #<Enumerator: [1, 2, 3]:map>

Your second example is an enumerator with yield method of :each.

你的第二个例子是一个枚举器,其yield方法为:each。

e2 = Enumerator.new do |y|
  [1,2,3].each do |x|
    y << x
  end
  # raise StopIteration, FED # <= how to get `FED`?
end
=> #<Enumerator: #<Enumerator::Generator:0x007fa69b056b50>:each> 

You should use to_enum or enum_for with the yield method of your choice:

您应该使用to_enum或enum_for和您选择的yield方法:

[1,2,3].to_enum(:map)
=> #<Enumerator: [1, 2, 3]:map>

Using ::new in the manner below is now deprecated, so I would NOT recommend using it in favor of to_enum or enum_for which offer the same functionality:

以下方式使用:: new现在已弃用,因此我不建议使用它来支持提供相同功能的to_enum或enum_for:

Enumerator.new([1,2,3], :map)

Summary

To sum this up, #map is the method being called by your first iterator, and when it is called, the return values of it will decide the value of the result. When using #each like your other examples, it doesn't matter what you end your block (#feed) with since it won't affect the return value of #each.

总结一下,#map是第一个迭代器调用的方法,当它被调用时,它的返回值将决定结果的值。当像其他例子一样使用#each时,你的块(#feed)结束并不重要,因为它不会影响#each的返回值。

#2


3  

The reason your first example works the way it does is because you've used #map which "passes the array's elements to "yield" and collects the results of "yield" as an array." There's an interesting note about it in Enumerator#feed http://ruby-doc.org/core-2.5.0/Enumerator.html#method-i-feed

你的第一个例子以它的方式工作的原因是因为你使用#map“将数组的元素传递给”yield“并将”yield“的结果收集为数组。”在Enumerator#feed中有一个有趣的注释http://ruby-doc.org/core-2.5.0/Enumerator.html#method-i-feed

Anyway, your custom enumerator will behave the same way as the Array in your first example if you also call map on it:

无论如何,如果您还在其上调用map,则自定义枚举器的行为方式与第一个示例中的Array相同:

e2 = Enumerator.new { |y|
  [1,2,3].each do |x|
    y << x
  end
}.map

p e2.next           #=> 1
e2.feed "a"
p e2.next           #=> 2
e2.feed "b"
p e2.next           #=> 3
e2.feed "c"
begin
  e2.next
rescue StopIteration
  p $!.result      #=> ["a", "b", "c"]
end