C# , Python , Ruby の yield の違い
毎週木曜日に社内でC#の勉強会をやっていて、そこで yield なる文がなにをやっているかわからないという話題に。そういえば Python で フィボナッチ数列 を取得するときに yield 使っていたっけ。同じかな?って思ったので試してみました。
C#
C#の場合、yieldはイテレータブロック内で列挙子オブジェクトに値を生成する場合に、 yield return 式として使用します。
yield break として反復処理の終了を通知することもできるらしい。
using System; using System.Collections; using System.Collections.Generic; using System.Linq; // f.Take で LINQ使用 namespace Fib { class Fibonacci : IEnumerable<int> { int x = 0; int y = 1; public IEnumerator<int> GetEnumerator() { // 無限に求める while (true) { yield return x; int tmp_x = x; x = y; y = tmp_x + y; } } IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } } class Program { static void Main(string[] args) { Fibonacci f = new Fibonacci(); // f(0)..F(9)までを列挙する foreach (var i in f.Take(10)) { Console.WriteLine(i.ToString()); } Console.ReadLine(); } } }
Python
Python の場合、ジェネレータ関数内において データを返す場合 yield を使います。
こんな感じ。
#!/usr/bin/env python # coding:utf-8 # ジェネレータ関数fib() の nextメソッド # (ジェネレータ関数では__init__メソッドと # nextメソッドが自動で定義される)が呼び出されると # yield文までが実行されて、yieldで指定されている # xの値が戻り値として返される。 # # 以降、nextメソッドが呼び出されると、yieldの直後の文 # から処理を再開し、次のyield文までが実行されて、 # yieldで指定されているxの値が戻り値として返される # # フィボナッチ数列のような無限回の繰り返し処理を表現するのに都合が良い def fib(): x, y = 0, 1 while True: yield x x, y = y, x + y z = fib() for i in range(10): print z.next()
Ruby
Rubyは初めて書いてみました。
Rubyだと yield の挙動が異なるようです。
うまく書けなかったので、ideone に書いて呟いてみました。
いくつかの言語で yield を使ってみたかったので ruby をインストールしてみた。しかしよくわからないエラーが... ideone.com/yc4Dln
— こいんとすさん (@cointoss1973) 12月 20, 2012
光の速さで猛者たちからリプライが...!
@cointoss1973 こんな感じdef fib(n)i1,i2 = 0,1n.times do yield i1 i1, i2 = i2, i1+i2endendfib(10){|n|puts n}
— TERAJIMA, Motoyukiさん (@trmmy) 12月 20, 2012
@cointoss1973 pythonとrubyとでは主従が逆なんですよ。なのでfibのためにyieldを使うというのがちょっと違う感じですね。yieldを使うコードサンプル書いてみました gist.github.com/4345307
— Takayuki Shimizukawaさん (@shimizukawa) 12月 20, 2012
@cointoss1973 さっきの、ちょっと頑張ってみました。Rubyist はどうするか分からないですが…。 > ideone.com/O2327F
— let ガブ = Some(エンジニア)さん (@gab_km) 12月 20, 2012
@cointoss1973 この記事がちょうど良い説明をしてます atmarkit.co.jp/fdotnet/chushi… 「Rubyにも列挙用のyieldキーワードがあるが、ほかの言語とは挙動が異なり、...」
— Takayuki Shimizukawaさん (@shimizukawa) 12月 20, 2012
@cointoss1973 とりあえず自分のコードが動けばいいだけなら、「10.times{|n| puts "#{fib(){}}"} 」にしつつ、while true と 対のend 消せば動きますよ。期待通りではないですが。while消さないと無限ループ。
— TAKAHIROさん (@c_o_t) 12月 20, 2012
ruby の yield を使ったサンプル*1
rubyの場合は、C#とPythonと違い、渡されたブロックに値を送り込むというものです。
主従が異なるので、rubyは"逆"と覚えてください。
#!/usr/bin/env ruby # -*- coding: utf-8 -*- def fib(n) x,y = 0,1 n.times do yield x x, y = y, x+y end end # rubyのyieldは渡されたblockに値を送り込むためのもの。 fib(10){|n| puts n}
参考
謝辞
Replyを頂いた @gab_kmさん @trmmyさん @shimizukawaさん @c_o_t さん、ありがとうございます!
*1:trmmyさんのコードが答えそのままでした