だらだらやるよ。

こげつのIT技術メモ

yield returnを理解した!

いや、その理解があってるのかどうかはわからないけど(理解なんてそんなもんだ)、
使い方はわかりました。
たとえばこんなループ

		private void Form1_Load(object sender, EventArgs e) {
			//ファイルの中から数値だけの行のみを表示する
			//数値は奇数のみ
			string path = @"log.txt";
			Encoding enc = Encoding.UTF8;
			Console.WriteLine("プログラム開始");
			string line = "";
			using (System.IO.StreamReader sr
				= new System.IO.StreamReader(path, enc)) {
				while ((line = sr.ReadLine()) != null) {
					//数値へ変換
					int result;
					if (!int.TryParse(line, out result)) {
						continue;
					}
					//奇数のみ取り出し
					if (result % 2 != 1) {
						continue;
					}
					Console.WriteLine(result);
				}
			}
			Console.WriteLine("プログラム終了");
		}

これだとメソッドが大きすぎるし、このifの条件とかを再利用使用と思っても、
もとのループに依存しすぎているのでどうにかしたい。
てなわけでこんな感じにしてみるわけですよ。

		private void Form1_Load(object sender,EventArgs e) {
			//ファイルの中から数値だけの行のみを表示する
			//数値は奇数のみ
			string path = @"log.txt";
			Encoding enc = Encoding.UTF8;
			Console.WriteLine("プログラム開始");
			foreach(int a in FindOdd(ConvertStringToInteger(ReadAllLine2(path,enc)))) {
				Console.WriteLine(a);
			}
			Console.WriteLine("プログラム終了");
		}
		private List<string> ReadAllLine2(string path) {
			return ReadAllLine2(path,Encoding.UTF8);
		}
		private List<int> FindOdd(List<int> ints) {
			List<int> list = new List<int>();
			foreach(int i in ints) {
				if(i % 2 == 1) {
					Console.WriteLine("奇数のみ抽出。");
					list.Add(i);
				}
			}
			return list;
		}
		private List<int> ConvertStringToInteger(List<string> texts) {
			List<int> list = new List<int>();
			foreach(string line in texts) {
				int result;
				if(int.TryParse(line,out result)) {
					Console.WriteLine("数値へ変換完了。");
					list.Add(result);
				}
			}
			return list;
		}
		private List<string> ReadAllLine2(string path,Encoding enc) {
			Console.WriteLine("読み込み開始。");
			string line = "";
			List<string> list = new List<string>();
			using(System.IO.StreamReader sr
				= new System.IO.StreamReader(path,enc)) {
				while((line = sr.ReadLine()) != null) {
					Console.WriteLine("1行読む:" + line);
					list.Add(line);
				}
			}
			return list;
		}

はてとこれでは条件増えるたびにリストのコピーと無駄なループが繰り返されちゃうという悲しい話。
どう使用かなあというときにyieldって書くといいんだ!!

		private void Form1_Load(object sender, EventArgs e) {
			//ファイルの中から数値だけの行のみを表示する
			//数値は奇数のみ
			string path = @"log.txt";
			Encoding enc = Encoding.UTF8;
			Console.WriteLine("プログラム開始");
			foreach (int a in FindOdd(ConvertStringToInteger(ReadAllLine2(path, enc)))) {
				Console.WriteLine(a);
			}
			Console.WriteLine("プログラム終了");
		}
		private IEnumerable<string> ReadAllLine2(string path) {
			return ReadAllLine2(path, Encoding.UTF8);
		}
		private IEnumerable<int> FindOdd(IEnumerable<int> ints) {
			foreach (int i in ints) {
				if (i % 2 == 1) {
					Console.WriteLine("奇数のみ抽出。");
					yield return i;
				}
			}
		}
		private IEnumerable<int> ConvertStringToInteger(IEnumerable<string> texts) {
			foreach (string line in texts) {
				int result;
				if (int.TryParse(line, out result)) {
					Console.WriteLine("数値へ変換完了。");
					yield return result;
				}
			}
		}
		private IEnumerable<string> ReadAllLine2(string path, Encoding enc) {
			Console.WriteLine("読み込み開始。");
			string line = "";
			using (System.IO.StreamReader sr
				= new System.IO.StreamReader(path, enc)) {
				while ((line = sr.ReadLine()) != null) {
					Console.WriteLine("1行読む:" + line);
					yield return line;
				}
			}
		}

やあこれで楽に処理を小さくできますね。
ちょっととっつきにくいなーとか思ってたけど、意外と簡単でした。
動くコードを見ながら勉強て大事ですね。


ちなみに元ネタはこれ。いい本です。
Effectiveほげほげの本は何冊か読んだのですが、全部面白かったのでまったくもって内容見ずに注文したのですが良かったですね。勉強になりました。