Search on the blog

2014年6月29日日曜日

C#入門(9)Action、Func、Predicateの違い

Action、Func、Predicateについて簡単に整理しておく。
  • Actionは、任意個の引数を取り、かつ、戻り値のないメソッドへのDelegate。
  • Funcは、任意個の引数を取り、かつ、戻り値のあるメソッドへのDelegate。
  • Predicateは、任意個の引数を取り、かつ、り値の型がBooleanであるようなメソッドへのDelegate。
簡単なサンプルを書いてみた。
using System;

public class Sample
{
    public static T[] Map<T> (T[] xs, Func<T, T> func)
    {
        T[] ret = new T[xs.Length];

        for (int i = 0; i < xs.Length; i++) {
            ret [i] = func (xs [i]);
        }

        return ret;
    }

    public static T[] Filter<T> (T[] xs, Predicate<T> predicate)
    {
        int size = 0;
        foreach (var x in xs) {
            if (predicate (x))
                ++size;
        }
        
        T[] ret = new T[size];
        int pos = 0;
        foreach (var x in xs) {
            if (predicate (x))
                ret [pos++] = x;
        }
        
        return ret;
    }

    public static void Each<T> (T[] xs, Action<T> action)
    {
        foreach (var x in xs) {
            action (x);
        }
    }

    public static void Main (string[] args)
    {
        int[] xs = {0,1,2,3,4,5,6,7,8,9};
        xs = Map (xs, (x) => x * x);
        xs = Filter (xs, (x) => x % 2 == 0);
        Each (xs, (x) => Console.WriteLine (x));
    }

}

2014年6月26日木曜日

C#入門(8)delegateで移譲してみる

 「delegate = Cの関数ポインタ」という認識だった。

 しかし、delegateに対応する日本語が「移譲」であることや、インスタンスメソッドを参照できることを考えると、delegateとはいわゆる「移譲」(※)を実現するための言語仕様として用意されたもので、そのように使われることを期待しているのではないかと思えてきた。

(※) ”いわゆる移譲”と言っているのは、オブジェクト指向で使われる「継承」と「移譲」の移譲のこと。

 ということでdelegateを使って移譲するサンプルを書いてみた。

コンストラクタで注入したオブジェクトに移譲するサンプル
これは、C#のdelegateを使わずに移譲する例。
using System;

class Hoge 
{
    public void DoThis() {
        Console.WriteLine("Do This!");
    }

    public void DoThat() {
        Console.WriteLine("Do that!");
    }
}

class Fuga
{
    public void ProcThis() {
        Console.WriteLine("Process This!");
    }

    public void ProcThat() {
        Console.WriteLine("Process That!");
    }
}

class HogeFuga
{
    private Hoge hoge;
    private Fuga fuga;

    public HogeFuga(Hoge hoge, Fuga fuga) {
        this.hoge = hoge;
        this.fuga = fuga;
    }

    public void DoThis() {
        hoge.DoThis();
    }

    public void DoThat() {
        hoge.DoThat();
    }
    public void ProcThis() {
        fuga.ProcThis();
    }
    public void ProcThat() {
        fuga.ProcThat();
    }
}

public class Sample
{
    public static void Main(string[] args) {
        HogeFuga hogefuga = new HogeFuga(new Hoge(), new Fuga());
        hogefuga.DoThis();
        hogefuga.ProcThat();

        // 実行結果
        // > Do This!
        // > Process That!

    }
}
移譲を使うと多重継承をサポートしていない言語でも多重継承っぽいことが出来る。
あと、HogeとFugaに複数のサブクラスがあったりすると、コンストラクタで注入するサブクラスを変えることでいろいろなバリエーションを作れて便利。コンストラクタの代わりにセッターで移譲先のオブジェクトを注入してもよい。
どの具象クラスを移譲先として使うかはソースに書かないで実行時に決められるため、継承よりも”結合度の弱い”機能の共通化が出来る。

delegateを使って移譲するサンプル
次に、C#のdelegateを使って移譲するサンプル。
class HogeFuga {
    public delegate void MyDelegate();

    public MyDelegate DoThis;
    public MyDelegate DoThat;
    public MyDelegate ProcThis;
    public MyDelegate ProcThat;
}

public class Sample {
    public static void Main(string[] args)
    {
        Hoge hoge = new Hoge();
        Fuga fuga = new Fuga();
        HogeFuga hogeFuga = new HogeFuga();

        // 前のサンプルと同様の移譲を定義する
        hogeFuga.DoThis = hoge.DoThis;
        hogeFuga.DoThat = hoge.DoThat;
        hogeFuga.ProcThis = fuga.ProcThis;
        hogeFuga.ProcThat = fuga.ProcThat;

        hogeFuga.DoThis();
        hogeFuga.ProcThat();

        // 実行結果
        // > Do This!
        // > Process That!

        // より細かい粒度で(メソッド単位で)移譲を定義できる
        hogeFuga.DoThis = hoge.DoThis;
        hogeFuga.DoThat = fuga.ProcThat;
        hogeFuga.ProcThis = fuga.ProcThis;
        hogeFuga.ProcThat = hoge.DoThat;

        hogeFuga.DoThis();
        hogeFuga.ProcThat();

        // 実行結果
        // > Do This!
        // > Do That!

        // マルチキャストデリゲートで複数のメソッドを実行する
        hogeFuga.DoThis += hoge.DoThat;
        hogeFuga.DoThis += fuga.ProcThis;
        hogeFuga.DoThis += fuga.ProcThat;

        hogeFuga.DoThis();

        // 実行結果
        // > Do This!
        // > Do that!
        // > Process This!
        // > Process That!

    }
}
上の例のとおり、より柔軟な移譲が可能になる。
delegateは、上手に使えるようになるとすごい武器になると思う。

2014年6月24日火曜日

C#入門(7)Actionで遊んでみる

 Actionを使って遊んでみた。

Actionは、0個または1個以上の引数を取り、何も返却しないメソッドへのポインタ(Delegate)[1] らしい。

まずは、受け取ったActionを2回実行するだけのサンプルを書いてみた。ついでにラムダ式にも手を出してみた。ラムダ式は、LINQを使う場合に重宝するそうだ。
using System;

public class Sample
{
    public static void Main (string[] args)
    {
        new Sample ().Run ();
    }

    private void doItTwice (Action action)
    {
        action ();
        action ();
    }

    private void SayHello ()
    {
        Console.WriteLine ("Hello, World!");
    }

    public void Run ()
    {
        doItTwice (SayHello);

        doItTwice (() => {
            Console.WriteLine ("Hello, Lambda World!");
        });
    }

}
続いて引数を3つ受け取るActionで何かやってみた。
引数の順序のすべてのpermutationを試してActionを呼ぶサンプル。
using System;

public class Sample
{
    public static void Main (string[] args)
    {
        new Sample ().Run ();
    }

    private void applyAllPermutationTo(Action<int, int, int> action, int x, int y, int z)
    {
        action(x, y, z);
        action(x, z, y);
        action(y, x, z);
        action(y, z, x);
        action(z, x, y);
        action(z, y, x);
    }

    public void Run() {
        applyAllPermutationTo((int x, int y, int z) => {
            Console.WriteLine("{0} * {1} - {2} = {3}", x, y, z, x * y - z);
        }, 10, 20, 30);
    }

}
C#いろいろ出来てなかなか面白いですね。

2014年6月23日月曜日

C#入門(6)DataTableをJoinする

 DataRelationを使うと、2つのDataTableを結合出来る。
例として、会社テーブルと従業員テーブルを作って、「1つの会社はN人の従業員を有する」という関係を定義してみた。
 
 僕の経験上、Javaの場合はORMでテーブルをEntityに変換するのが一般的だったけど、C#はDataTable使って永続化された情報をメモリ上で扱うのが一般的なんだろうか。慣れの問題かもしれないけど、いまいちしっくりこない。DataTable使うと、DataGridで簡単に表示出来るので便利っちゃ便利。
using System;
using System.Data;

namespace sample
{
    public class DataRelations
    {
        public static void Main(string[] args) {
            new DataRelations().Run();
        }

        public void Run() {
            DataSet ds = new DataSet();

            // 会社テーブルの定義
            ds.Tables.Add("companies");
            ds.Tables["companies"].Columns.Add("id", typeof(int));
            ds.Tables["companies"].Columns.Add("name", typeof(String));

            // 従業員テーブルの定義
            ds.Tables.Add("employees");
            ds.Tables["employees"].Columns.Add("id", typeof(int));
            ds.Tables["employees"].Columns.Add("name", typeof(String));
            ds.Tables["employees"].Columns.Add("companyId", typeof(int));

            // 会社テーブルにレコード挿入
            String[] companies = {"HAL", "ORANGE", "GIANTSOFT"};
            for (int i = 0; i < companies.Length; i++) {
                DataRow row = ds.Tables["companies"].NewRow();
                row["id"] = i + 1;
                row["name"] = companies[i];
                ds.Tables["companies"].Rows.Add(row);
            }

            // 従業員テーブルにレコード挿入
            String[] employees = {"Taro", "Hanako", "Yuta", "Kumi", "Takashi", "Naomi"};
            for (int i = 0; i < employees.Length; i++) {
                DataRow row = ds.Tables["employees"].NewRow();
                row["id"] = i + 1;
                row["name"] = employees[i];
                row["companyId"] = i % 3 + 1;
                ds.Tables["employees"].Rows.Add(row);
            }

           // 関連を定義(会社は従業員を有する)
           ds.Relations.Add("companyHasEmployees", 
                             ds.Tables["companies"].Columns["id"], 
                             ds.Tables["employees"].Columns["companyId"]
                             );

            // ある従業員が所属する会社を取得
            foreach (DataRow employee in ds.Tables["employees"].Rows) {
                DataRow company = employee.GetParentRow("companyHasEmployees");
                Console.WriteLine("{0} belongs to {1}.", employee["name"], company["name"]);
            }

            // ある会社に所属する従業員を取得
            foreach (DataRow company in ds.Tables["companies"].Rows) {
                Console.WriteLine("Employees who work for {0}:", company["name"]);
                foreach (DataRow emps in company.GetChildRows("companyHasEmployees")) {
                    Console.WriteLine("\t{0}", emps["name"]);
                }
            }
         
        }
    }
}

C#入門(5)DataViewでSort

DataViewを使ってみた。
  • DataTable.DefaultViewでDataViewを取得できる。
  • Sortプロパティにカラム名をセットするとソートされる。ASCとかDESCとか付けられる。デフォルトはASC。コンマで区切ると複数キーの指定ができる。
  • DataView.Countで行の数を取得できる
  • DataView[index]とすると、index番目の行にアクセスできる。
  • DataView.ToTable()でDataTableに変換できる。
using System;
using System.Data;

namespace sample
{
    public class DataViewSample
    {
        public static void Main(string[] args) {
            new DataViewSample().Run();
        }

        public void Run() {
            DataTable table = new DataTable();

            table.Columns.Add("name", typeof(String));
            table.Columns.Add("age", typeof(int));

            String[] names = {"taro", "jiro", "saburo", "shiro", "goro"};
            int[] age = {100, 15, 14, 13 ,12};

            for (int i = 0; i < names.Length; i++) {
                DataRow row = table.NewRow();

                row["name"] = names[i];
                row["age"] = age[i];
            
                table.Rows.Add(row);
            }

            DataView view = table.DefaultView;
            view.Sort = "age";

            // iterate items in DataView: with "for" loop
            for (int i = 0; i < view.Count; i++) {
                Console.WriteLine("{0} {1}", view[i]["name"], view[i]["age"]);
            }

            // iterate imtes in DataView: with "foreach" loop
            foreach (DataRowView row in view) {
                Console.WriteLine("{0} {1}", row["name"], row["age"]);
            }
               
        }
    }
}

2014年6月22日日曜日

Google Code Jam 2014


 巷では「誰が一番上手にボールを蹴って枠に入れることが出来るか」を競う世界大会が話題になっているようです。プログラマーたちの間では「誰が一番正確にかつ高速に問題を解くアルゴリズムを考え、それを正確に実装出来るか」を競う世界大会が話題になっています。まあ、僕は早々と敗退したのでワールドカップ見てますが。

 とりあえず今年のCode Jamを振り返ってみます。

結果
去年は1バイトの違いでRound 3行きを逃してしまいました。
今年は現実的に"TシャツをGETする"を目標にして参加しました。結果は、惜しくも届かず。
Qualification Round 2,067 out of 25,462
Round 1A N/A
Round 1B 1,959 out of 7,381
Round 1C 875 out of 4,309
Round 2 1,005 out of 2,526

面白かった問題
Round 2までで個人的に面白かった問題。
  1. Don't Break The Nile
  2. Trie Sharding
  3. Enclosure
  4. Proper Shuffle
特に、Don't Break The Nileは印象的でした。最小カットを求めることで、最大フローを求めるという問題です。
最大フローから最小カットを求めるという問題はよく見ますが、その逆のパターンで、柔軟な発想力が求められる問題でした。

来年に向けて
去年まではたくさん問題を解くことに力を入れていましたが、今年からは良問を繰り返し解くというやり方にスイッチしました。
来年のCode Jamの結果を見てこの練習方針が効率的なのか否かを見極めようと思います。

2014年6月14日土曜日

元気が出る動画(2)Embrace the near win

successとmasteryの違いについてのプレゼンテーション。


successからmasteryへと進化するには何が必要なのか?
”near win”を評価することで、熟練への道は開かれる。

アーチャリーの世界には、「アーチャーのパラドックス」と呼ばれるものがある。
”標的を射ぬくためには、微妙にずれた部分を狙わないといけない。”

successとは的の中心を射抜くことである。
しかしmasteryは、それ自体に意味を見い出さない。
たまたま的の中心を射抜けたことに意味はないから。何度でも再現できなければ意味はないのだ。

masteryとは成功の瞬間ではない。
masteryとは目標に向けてのコミットメントではない。
恒久的に追い求めることである。
それを達成するためには、"near win"を評価することが大切である。

芸術における大作を思い浮かべて欲しい。
製作者自信は未完のもの(= near win)であると考えているものが、世間から高く評価されることがしばしばある。

masteryの追求とは、絶えず前進を続けるものである。
masteryにゴールはない。常に自身の理想と現実のギャップを埋めようという衝動に駆り立てられるのだ。

near winのわかりやすい例はオリンピックの銀メダリストである。銀メダリストの気持ちは次の大会に向かっている。

near winが効果的なのは、私達の目標 -- それは遠い未来に向けられることが多い -- をより近いところに設定してくれるからだ。
遠い未来に向けられた目標は抽象的だが、現状に近いところに向けた目標は具体的だ。
私たちを現在に集中させてくれて、目の前の問題の解決に意識を向かせるのだ。

C#入門(4)DataTableでselect

 DataTableの練習。C#では、DataTableにDBから取得したデータを詰めて管理するのが一般的なようだ。JavaでいうEntityのListみたいな感じかと思う。
さらに、DataSetなるものがあって、複数のDataTableをひとつのDataSetで管理できる。
  • DataTable = テーブル
  • DataSet = スキーマ
のようなものと理解している。

 手始めにDataTable#Selectメソッドで
  • データのフィルタリング
  • データのソート
を行うサンプルを書いてみた。なかなか便利ですね。

using System;
using System.Data;

namespace sample
{
    public class DataSetSample
    {
        public static void Main(string[] args) {
            new DataSetSample().Run();
        }

        private DataSet createSampleData() {
            DataSet dataSet = new DataSet();
            
            DataTable dataTable = new DataTable("student");
            
            dataTable.Columns.Add("name");
            dataTable.Columns.Add("age");
            dataTable.Columns.Add("sex");
            
            dataSet.Tables.Add(dataTable);
            
            String[] names = {"taro", "hanako", "jiro", "saburo", "yuka"};
            int[] ages = {10, 10, 9, 8, 7};
            char[] sex = {'M', 'F', 'M', 'M', 'F'};
            
            for (int i = 0; i < names.Length; i++) {
                DataRow dataRow = dataTable.NewRow();
                dataRow["name"] = names[i];
                dataRow["age"]  = ages[i];
                dataRow["sex"] = sex[i];
                dataSet.Tables["student"].Rows.Add(dataRow);
            }

            return dataSet;
        }

        public void Run() {

            DataSet dataSet = createSampleData();

            // select male students
            DataRow[] res = dataSet.Tables["student"].Select("sex = 'M'");
            foreach (var row in res) {
                Console.WriteLine("{0} is a {1}-year-old boy.", row["name"], row["age"]);
            }
            Console.WriteLine();
            
            // select students of the age 10 or above
            res = dataSet.Tables["student"].Select("age >= 10");
            foreach (var row in res) {
                Console.WriteLine("{0} is of the age 10 or above.", row["name"]);
            }
            Console.WriteLine();
            
            // select students whose name ends with "ro"
            res = dataSet.Tables["student"].Select("name like '%ro'");
            foreach (var row in res) {
                Console.WriteLine("{0} is one of 'XXro.'", row["name"]);
            }
            Console.WriteLine();
            
            // sort students by his/her name
            res = dataSet.Tables["student"].Select(null, "name");
            foreach (var row in res) {
                Console.WriteLine("{0}", row["name"]);
            }
            Console.WriteLine();

        }

    }
}

2014年6月10日火曜日

C#入門(3)Tuple

 C++のpair的なものを探していたらTupleがあったので使ってみた。
pairとの違いは、
  • Tupleは3組以上の組み合わせも出来る
  • Tupleはimmutable

とりあえず
  • Tupleを生成する
  • Tupleを要素に持つListをソートする
というコードを書いてみた。

ソートは、デフォルトソート(要素1, 要素2, 要素3, ...という順にソートしていく)とラムダ式を使ったソートを書いてみた。
using System;
using System.Collections.Generic;

namespace sample
{
    public class TupleSample
    {
        public static void Main(string[] args) {
            // tuple generation example
            Tuple<string, int> t = Tuple.Create("taro", 15);
            Console.WriteLine("{0} {1}, {2}", t, t.Item1, t.Item2);

            var u = Tuple.Create("taro", "yamada", 15, 170, 60, "AB");
            Console.WriteLine("{0}", u);

            // tuple sort example
            List<Tuple<int, int>> ts = new List<Tuple<int, int>>();
            Random rnd = new Random();
            for (int i = 0; i < 10; i++) {
                int x = rnd.Next(10);
                int y = rnd.Next(10);
                ts.Add(Tuple.Create(x, y));
            }

            Console.WriteLine("sort by item1, then item2.");
            ts.Sort();
            foreach (var x in ts) 
                Console.WriteLine("{0}", x);

            Console.WriteLine("sort by item2, then item1.");
            ts.Sort((a, b) => (a.Item2 != b.Item2) ? (a.Item2 - b.Item2) : (a.Item1 - b.Item1));
            foreach (var x in ts) 
                Console.WriteLine("{0}", x);

        }
    }
}

2014年6月3日火曜日

C#入門(2)ISet

C++のset、unsorted_setに相当するものを使ってみた。
C#だとSortedSet、HashSetというコレクションが用意されている。ISetというインターフェースがあるので、参照にはISet型を使った。
どうやらコレクションのインターフェースは大文字の"I"から始まるらしい。
あとメソッドの頭文字は大文字にするのが慣例らしい。メソッドは、というか、引数以外は頭文字を大文字にするのが慣例らしい。ローカル変数も引数と同じでCamel Casingにするのが一般的っぽい。
using System;
using System.Collections.Generic;

namespace sample
{
    public class SetSample
    {
        public static void Main(string[] args) {
            ISet<int> st1 = new HashSet<int>();
            ISet<int> st2 = new HashSet<int>();

            for (int i = 0; i < 30; i++) {
                st1.Add(2 * i);
                st2.Add(3 * i);
            }

            st1.IntersectWith(st2);

            // print multiples of both 2 and 3
            foreach (int x in st1) {
                Console.WriteLine("{0}", x);
            }

            ISet<int> st3 = new SortedSet<int>();
            Random rand = new Random();
            for (int i = 0; i < 100; i++)
                st3.Add(rand.Next(1000000));

            foreach (int x in st3) {
                Console.WriteLine("{0}", x);
            }

        }
    }
}

んー、C#の勉強はじめたけど方向性あってるのだろうか。ASP.NETの勉強したほうがいいような...。ASP.NETで書かれた綺麗なコードを読みたい。