100までの素数を求めるプログラム、その後
http://d.hatena.ne.jp/kurip/20060219#1140316336
の続き.
Visual C# 2008 Express Editionのインストール記念で再びチャレンジしてみました.
前回の最終形は以下の通りでした.
class Program { static List<int> GetPrimes(int maxNum) { List<int> primeList = new List<int>(); for (int i = 2; i <= maxNum; i++) { if (primeList.TrueForAll(delegate(int j) { return i % j != 0; })) { primeList.Add(i); } } return primeList; } static void Main(string[] args) { foreach (int prime in GetPrimes(100)) { Console.Write("{0},", prime); } Console.WriteLine(""); } }
いまだったらどうしようかな?GetPrimesメソッドの部分は別クラスにして,イテレータを実装しようかしら?
TrueForAllはAllメソッドにして,匿名型(var)も使ってと.こんな感じかな?
internal class PrimeList : IEnumerable<int> { int count; internal PrimeList(int count) { this.count = count; } public IEnumerator<int> GetEnumerator() { var primes = new List<int>(); for (var i = 2; i<count; i++) { if (primes.All(j => i % j != 0)) { primes.Add(i); yield return i; } } } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<int>)this).GetEnumerator(); } } class Program { static void Main(string[] args) { foreach(var prime in new PrimeList(100)) { Console.Write("{0},", prime); } Console.WriteLine(); } }
さて,ここからどうしましょうか?
よくC# 3.0は関数型言語っぽくなったなんて言われますので,ちょっとそれっぽく変化させてみましょうか?
とはいえ自分の関数型言語の知識は,
ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門
- 作者: 青木峰郎,山下伸夫
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2006/06/01
- メディア: 単行本
- 購入: 25人 クリック: 314回
- この商品を含むブログ (320件) を見る
まずはfor文を再帰にしてみます.
public IEnumerator<int> GetEnumerator() { var primes = new List<int>(); Action<int> func = i => { if (primes.All(j => i % j != 0)) { primes.Add(i); } }; int x = 1; while (true) { func(++x); if (x >= count) break; } foreach (var prime in primes) { yield return prime; } } }
yieldはラムダ式の中には入れられないっぽいので,仕方なく素数リストを作ったあとで,foreachで回すことにしました.それにしても,激しくイマイチですね(^^;;
ところでHaskell本を読むと,Haskellにはif文はあるにはあるんですが,ちょっと考え方が違うみたいで,どちらかというと3項演算子に近いものらしいです.Action
Func<List<int>, int, List<int>> addPrime = (list, i) => list.All(j => i % j != 0) ? list.Add(i) : list;
残念!!list.Addの戻り値はvoidでした.仕方ないのでヘルパー関数を作りましょう.
Func<List<int>, int, List<int>> addList = (list, i) => { list.Add(i); return list; }; Func<List<int>, int, List<int>> addPrime = (list, i) => list.All(j => i % j != 0) ? addList(list, i) : list;
これらを使えば,再帰の部分もなんとかなりそうです.
Func<List<int>, int, List<int>> rec = (list, i) => i < count ? rec(addPrime(list, i), ++i) : list;
またまた残念!!これだと何故かコンパイルエラーになってしまいます.以下のようにすればOK(なんでだろう?)
Func<List<int>, int, List<int>> rec = null; rec = (list, i) => i < count ? rec(addPrime(list, i), ++i) : list;
そんなわけで最終的には,以下のようになりました.
public IEnumerator<int> GetEnumerator() { Func<List<int>, int, List<int>> addList = (list, i) => { list.Add(i); return list; }; Func<List<int>, int, List<int>> addPrime = (list, i) => list.All(j => i % j != 0) ? addList(list, i) : list; Func<List<int>, int, List<int>> rec = null; rec = (list, i) => i < count ? rec(addPrime(list, i), ++i) : list; var primes = rec(new List<int>(), 2); foreach (var prime in primes) { yield return prime; } }
いまの自分にはこれが精いっぱい.また数年後つづき書くかも(^^;;