2014年12月14日

C#で重複無し乱数を生成する

   このエントリーをはてなブックマークに追加 Clip to Evernote
どう書く?orgに感謝を込めて」シリーズ その56
■問題 (出題者: raynstard さん)
整数nを渡すと1 〜 n までの整数を重複しないようランダムに出力する関数「bingo」を作ってください。

この問題は、1 〜 n までの数をシャッフルせよ、って問題と同義ですね。
.NET Framework に List<T> や IEnumerable<T>に要素をシャッフルするメソッドがあればいいんですが、無いみたいなので IEnumerable<T> の拡張メソッド Shuffle を自作しました。

Fisher-Yates shuffle アルゴリズムを採用しています。

using System;
using System.Collections.Generic;
using System.Linq;

namespace Doukaku.Org {
    static class ListExtentions {
        static Random rnd = new Random();
        // シャッフル拡張メソッド : Fisher-Yates shuffle アルゴリズムを採用
        public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> list) {
            T[] result = list.ToArray();
            for (int i = result.Length - 1; i > 1; i--) {
                int j = rnd.Next(0, i);
                T a = result[i];
                result[i] = result[j];
                result[j] = a;
            }
            return result;
        }
    }
}

このShuffle メソッドがあれば、Bingoメソッドを作るのは簡単ですね。
ここでは、BingoクラスのCreate static メソッドとしました。

using System;
using System.Collections.Generic;
using System.Linq;

namespace Doukaku.Org {

    static class Bingo {
        // シャッフル拡張メソッド : Fisher-Yates shuffle アルゴリズムを採用
        public static IEnumerable<int> Create(int n) {
            return Enumerable.Range(1, n).Shuffle();
        }
    }
}

このメソッドがただしく動作するかどうかも以下のコードで確認しました。

using System;
using System.Collections.Generic;
using System.Linq;

namespace Doukaku.Org {
    class Program {
        static void Main(string[] args) {
            for (int x = 0; x < 40; x++) {
                var nums = Bingo.Create(20);
                foreach (int n in nums)
                    Console.Write("{0} ", n);
                Console.WriteLine(IsBingo(nums, 20));
            }
            Console.WriteLine();
            Console.ReadLine();
        }

        static bool IsBingo(IEnumerable<int> seq, int n) {
            if (seq.Count() != n)
                return false;
            for (int i = 1; i <= n; i++) {
                if (!seq.Contains(i))
                    return false;
            }
            return true;
        }
    }
}

ちなみに、Bingo.Createは、LINQ 使った以下のような実装もありますね。

public static IEnumerable<int> Create(int n) {
    var list = Enumerable.Range(1, n).OrderBy(x => rnd.Next(n));
    return list;
}

  

Posted by gushwell at 21:52Comments(0)TrackBack(0)

2012年01月22日

n回処理を繰り返したい (2)

   このエントリーをはてなブックマークに追加 Clip to Evernote
前回「n回処理を繰り返したい (1)」の続きです。

こういった繰り返し処理の時には、何回目の処理かを知りたい場合があります。
そんな時のために、もう一つ拡張メソッドを用意しましょう。


こすれば、次のようなコードが可能になります。


参考に、for文使ったコードも示します。


  
Posted by gushwell at 21:34Comments(0)TrackBack(0)

2012年01月12日

リストのすべての要素に対しラムダ式を適用する

   このエントリーをはてなブックマークに追加 Clip to Evernote
リストをデリゲートが返す値で埋め尽くす では、リストをデリゲート(ラムダ式)の値で初期化する拡張メソッドを
示しましたが、今度は、既にリスト内にセットされている値に対し、ラムダ式を適用し、
新たな値をセットする拡張メソッドを示します。


この拡張メソッドを利用したコードです。

ここでは、各要素を1.05倍しています。
105 210 315 420
がリストの新たな要素となります。

この例では、ラムダ式の第1引数を利用していませんが、この i には、
その要素のインデックス番号がわたるので、そのインデックス値を使って、
要素の値に対し、処理をすることもできます。

ジェネリックなメソッドにしているので、リストの要素が文字列であってもOKです。
まったく、意味のないコードですが、以下にその例を示します。


リストには、順に

 "SILVERLIGHT"
 "INDOWS"
 "OX"
 "ROSOFT"

が格納されます。
  
Posted by gushwell at 22:00Comments(0)TrackBack(0)

2012年01月10日

リストをデリゲートが返す値で埋め尽くす

   このエントリーをはてなブックマークに追加 Clip to Evernote
以前アップした「リストを指定した値で埋め尽くす」をちょっと工夫すれば、さらに汎用性の高い拡張メソッドが作れます。


こんな風に使います。


これで、0 2 4 6 8 10 12 14 16 18 と要素がセットされます。

  
Posted by gushwell at 20:58Comments(2)TrackBack(0)

2011年11月29日

ToNullable拡張メソッド

   このエントリーをはてなブックマークに追加 Clip to Evernote
var を使いたいための、拡張メソッド。


これを使えば、

int? n = 10;

ではなくて、

var n = 10.ToNullable();

と書けます。

まあ、あくまでもお遊びです。
  
Posted by gushwell at 22:30Comments(0)TrackBack(0)

2011年11月23日

文字列の配列を指定区切り文字で連結する

   このエントリーをはてなブックマークに追加 Clip to Evernote
以下のような拡張メソッドを定義しておきます。たんにJoinを呼び出しているだけのものです。


文字列の配列に格納されている文字列を、区切り記号を使い連結する拡張メソッドです。
単にJoinメソッドを呼び出しているだけのメソッドですが、インテリセンスの力が働き、
とても使いやすいメソッドに変身します。

このメソッドを使えば、以下のようなコードを書くことができます。


この実行結果は、以下のように表示されます。

123-456-789-abc-def

なお、メソッド名を Joinにしなかったのは、LINQのJoinメソッドと
混同しないようにするためです。

ところで、String.Joinって、.NET Framework4になって、オーバーロードされているメソッドの数が
増えたんですね。

Join(String, IEnumerable<String>)
とか
Join<T>(String, IEnumerable<T>)
とか。

なので、上の拡張メソッドは、以下のように書く事でさらに、汎用性が あがりますね。

このブログにC#のコードを載せたのは、実に、3ヶ月ぶりです。
  
Posted by gushwell at 16:47Comments(0)TrackBack(0)

2010年11月23日

コンソールで処理中であることを示す文字を表示

   このエントリーをはてなブックマークに追加 Clip to Evernote
元ネタ いろいろ備忘録日記 - コンソールで処理中であることを示す文字を表示

おもしろそうだったので、僕もやってみました。

指定した複数の値を繰り返す」で示したRepeat拡張メソッドを使って、gsf_zero1 さんが書いたコードを書き換えています。

以下に示すコードのProgressメソッドが、処理中であることを示すメソッドです。
gsf_zero1 さんは、ラムダ式としていましたが、これをメソッドにしています。

このメソッドの中で、Repeatメソッドを使い、コンソールに表示する文字を繰り返し取得しています。
僕のコードでは、'.' 'o' 'O' '@' 'O' 'o' という6文字を繰り返して取り出し、コンソールに表示しています。
別の文字を表示したいなら、文字列 ".oO@Oo" の部分を"|/-" など別のものに置き換えるだけです。


  
Posted by gushwell at 23:10Comments(0)TrackBack(0)

2010年10月07日

指定した複数の値を繰り返す

   このエントリーをはてなブックマークに追加 Clip to Evernote
前回の記事の続きです。

前回示した false true を繰り返す以下のコードは、bool型しか扱えないので、使い道が限られています。
任意の型にも対応させれば、使い道がありそうです。
ジェネリックスの出番ですね。
というクラスを作れば、
とか、
とか、いろいろ使えます。

foreach も使いたいということであれば、以下のようにGetEnumerable メソッドを追加します。
そうすれば、
なんて、書けますね。

ここまで書くと、別に要素が2つに限定することもないなーということで、
シーケンスを受け取る拡張メソッドを書いてみました。
これを使うコード例を以下に示します。
これだと、foreachでしか使えないんじゃないのと思う人もいると思いますが、いえいえ、次のような書き方もできます。
でも、使い勝手は良くないです。ラムダ式を返す部分をメソッドにしてしまったほうが良いかもしれません。
興味のある方は、EnumerableExtentions クラスにメソッドを追加してみてください。   
Posted by gushwell at 22:58Comments(2)TrackBack(0)