2014年07月13日

C#で配列のuniq

   このエントリーをはてなブックマークに追加 Clip to Evernote
どう書く?orgに感謝を込めて」シリーズ その24


■問題 (出題者:にしお さん)
アレイ(複数の値が配列状になっているもの)xsが与えられたときに、同じ値が2回以上出現しないように、2回目以降の出現を取り除いたアレイを返すコードを書いてください。 Rubyで表現すると下のようになります。
irb(main):001:0> xs = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9]
=> [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9]
irb(main):002:0> xs.uniq
=> [3, 1, 4, 5, 9, 2, 6, 8, 7]


間違えないように:よくある「ハッシュを使う」「集合オブジェクトを使う」などの方法は順番が乱れてしまうので使えません。出現順序を守りつつ、2回目以降の出現だけを取り除いてください。 この投稿は匿名での挑戦状の投稿を元に作成しています。ご投稿ありがとうございます。

たぶん、LINQ to Objects の Distinctメソッド使えばOKだと思います。出現順は保持されていたと記憶しています。
 
でも、ここでは、Distinctメソッドを使わないコードとしました。結果用リストに順に値を入れていくのですが、その際、すでに結果用リストにあるものは 入れないようにするという、ごくありふれたコードです。 いちおう、要素の型に依存しないようジェネリックス使ってます。

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

namespace Doukaku.Org {
    class Program {
        static void Main(string[] args) {
            int[] nums = { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9 };
            var result = Uniq(nums);
            string s = String.Join(", ", result.Select(n => n.ToString()).ToArray());
            Console.WriteLine("[{0}]", s);

            Console.ReadLine();
        }

        static T[] Uniq<T>(T[] array) {
            List<T> list = new List<T>();
            foreach (var x in array) {
                if (!list.Contains(x))
                    list.Add(x);
            }
            return list.ToArray();
        }
    }
}
  

Posted by gushwell at 22:00Comments(0)TrackBack(0)

2010年10月11日

C#でunfold

   このエントリーをはてなブックマークに追加 Clip to Evernote
T_GYOUTENさんの記事 に触発されて、C#で3つのUnfold関数を書いてみました。

名前をどうしようか迷ったのですが、Unfoldクラスを定義し、そこに Do メソッドを定義
することにしました。一つの機能しかないstaticクラスを作る時には、名前をどうするか
悩みますね。
引数の順番は、F#とは逆にし、初期値, generator の順にしました。

まずは、一つ目のバージョンのUnfold.Doメソッド
使い方はこんな感じ。
true, false が繰り返されます。
generatorが null を返すと終了しますが、Tが値型の場合、nullがとれないので、
無限に繰り返すことになります。

2つめは、繰り返し条件を指定できるバージョン。これならば、値型でも有限のシーケンスを 得ることが できます。
使い方です。
1 2 4 8 16 32 64 128 256 512 と表示されます。

最後は、最も汎用性のあるバージョンで、F#のSeq.unfoldとほぼ同等のものです。
Tupleを使っているので、ちょっと分かりにくいかもしれませんが、
これだと、初期値に与える型と、戻り値の各要素の型を別のものにすることができます。

以下、使い方の例です。
true, false を繰り返しています。 これだと、最初に示した true, false を返すコードのほうが簡単なので、
このTuple使ったDoメソッドだからできる例を示します。
フィボナッチ数列を求めるコードです。
unfoldは強力ですが、使うにはちょっと慣れが必要ですね。

  
Posted by gushwell at 21:25Comments(2)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)