ちょっとマイナーなeachの使い方

# 普通のイテレーション

```rb
[1,2,3].each do |i|
  p i
end
```

みんな知っている

---
# じゃあこれは？

```rb
x = [1,2,3].each
```

`x` は何でしょう？

---
# 答え

```rb
x = [1,2,3].each
=> #<Enumerator: [1, 2, 3]:each>
```

Enumeratorでした。

---
# Enumerator？？

https://docs.ruby-lang.org/ja/latest/class/Enumerator.html

（引用）
"each 以外のメソッドにも Enumerable の機能を提供するためのラッパークラスです。 また、外部イテレータとしても使えます。"
今回は後者

---
# 外部イテレータの利用例

```rb
iter = [1,2,3].each
begin
  print iter.next() while true
rescue StopIteration
  puts "iteration reached the end"
end
```

出力

`1 2 3 iteration reached the end`

---
# nextメソッド??

`Enumerator#next`

- 現在までの列挙状態に応じて「次」のオブジェクトを返し、列挙状態を1つ分進める。
- 列挙が既に最後へ到達している場合は、`StopIteration` 例外を発生する。

ref: https://docs.ruby-lang.org/ja/latest/class/Enumerator.html#I_NEXT

---
# 内部イテレータとの比較

内部イテレータ

```rb
[1,2,3].each {|i| p i }
```

外部イテレータ

```rb
it = [1,2,3].each
loop { p it.next }  # Kernel.loopはStopIterationを捕捉する
```

- ユーザーが繰り返しを明示的に制御

---
# （参考） C++やJavaは外部イテレータ

```cpp
for( auto it = v.begin(); it != v.end(); ++it ) {
  std::cout << *it << std::endl;
}
```

言語のclosureのサポートが弱いため

---
# 外部イテレータのメリットは？

あんまりない。基本、内部イテレータ使っておけばいい。

複数のコレクションに対して並列に処理したい場合には有用なケースもある

---
# 例

```rb
require 'prime'
a = (1..100).each
b = %w(first second third).each
c = []
loop do
  i = a.next
  c << i
  c << b.next if Prime.prime?(i)
end

c
# => [1, 2, "first", 3, "second", 4, 5, "third", 6, 7]
```

---
# 実装はどうなっている？

Fiberを使って実装されている

Rubiniusの実装を見てみる
https://github.com/rubinius/rubinius/blob/master/core/enumerator.rb

---

```rb
def next
  reset unless @fiber
  val = @fiber.resume
  raise StopIteration, "iteration has ended" if @done
  return val
end
...
def reset
  @done = false
  @fiber = Fiber.new stack_size: STACK_SIZE do
    obj = @object
    @result = obj.each { |*val| Fiber.yield *val }
    @done = true
  end
end
```

---
# Fiberとは？

https://docs.ruby-lang.org/ja/latest/class/Fiber.html

"ノンプリエンプティブな軽量スレッド(以下ファイバーと呼ぶ)を提供します。 他の言語では coroutine あるいは semicoroutine と呼ばれることもあります。"

実行中にコンテキストを切り替えることができる

---
# Fiberの例

```rb
f = Fiber.new do
  puts "a"
  Fiber.yield
  puts "c"
end

f.resume
puts "b"
f.resume
puts "d"
#=> a b c d と出力される
```

---
# 外部イテレータの実装（再掲）

```rb
def next
  reset unless @fiber
  val = @fiber.resume
  raise StopIteration, "iteration has ended" if @done
  return val
end
...
def reset
  @done = false
  @fiber = Fiber.new stack_size: STACK_SIZE do
    obj = @object
    @result = obj.each { |*val| Fiber.yield *val }
    @done = true
  end
end
```

---
# ちなみにPythonでは

内部イテレータの中でも実は外部イテレータが呼ばれている。
（Rubyでは外部イテレータの実装の中で内部イテレータが呼ばれている）

http://anandology.com/python-practice-book/iterators.html

---

forでループを回すと

```py3
for i in my_container:
  print(i)
```

中ではこんなことが行われる

- まず `my_container.__iter__()` が呼ばれて、iteratorが返る
- iteratorに対して `__next__` が呼ばれる
    - 例外`StopIteration`が起きるまで繰り返す

---
# nextのoverride

```py3
class MyCollection:
    def __init__(self, l):
        self.list = l
    def __iter__(self):
        self.idx = 0
        return self
    def __next__(self):
        print("next is called") # override
        if self.idx >= len(self.list): raise StopIteration
        a = self.list[self.idx]
        self.idx += 1
        return a
```

---

```py3
c = MyCollection([1,2])
for a in c: print(a)

# =>
#  next is called
#  1
#  next is called
#  2
#  next is called
```

ループの挙動が変わった。

Rubyの場合はnextをオーバーライドしても内部イテレータの挙動は変わらない。

---
# RubyとPythonの比較

- Rubyでは内部イテレータが基本
    - 外部iteratorはeach+Fiberで実現
    - coroutineを作りたい時にはFiberを使う
- Pythonでは外部イテレータが基本
    - forループの中では実は外部イテレータが使われている
    - coroutineを作りたい時には、Generator(RubyでいうところのEnumerator）を使って実現する

---
# ちなみに rb_call では

https://github.com/yohm/rb_call

```py3
for book in Book.where({"author":"foo"}):
    ... # ActiveRecordのcollectionに対するループ
```

- `__iter__` が呼ばれたら、対応するRubyのオブジェクトの`each`を呼ぶ
- `__next__`が呼ばれたら、対応するRubyのオブジェクトの`next`を呼ぶ
- Rubyで`StopIteration`が投げられたら、Pythonで`StopIteration`を投げる

---
# まとめ

- rubyでは内部イテレータが基本
- 複数のコレクションを並列にイテレートしたい場合に外部イテレータが有用な箇所もある
- Rubyでは外部イテレータは「内部イテレータ+Fiber」を使って実装されている