2015年1月4日日曜日

メモALL

■初期化
・配列
string[] str = {"aaa", "bbb", "ccc"};
int[] num = {1, 2, 3, 4, 5};
List list = new List {1, 2, 3};



・structの初期化
using System.Collections.Generic;

namespace SampleCode {

    class Program {
        static void Main(string[] args) {

            List list = new List();
            list.Add(new MyClass { age = 3, name = "bbb" });
            list.Add(new MyClass { age = 10, name = "aaa"});
        }
    }

    struct MyClass {
        public string name { get; set; }
        public int age { get; set; }
    }
}

LINQメモ

LINQ SampleCode
ど忘れした時のための参考サンプルコード

・メソッド一覧
Aggregate
All
Any
Count
DefaultIfEmpty

・いろいろ
大文字、小文字を無視してDistinct

Distinct

■public static IEnumerable Distinct
(this IEnumerable source);
// 概要:
// 既定の等値比較子を使用して値を比較することにより、シーケンスから一意の要素を返します。
//
// パラメーター:
// source:
// 重複する要素を削除する対象となるシーケンス。
//
// 型パラメーター:
// TSource:
// source の要素の型。
//
// 戻り値:
// ソース シーケンスの一意の要素を格納する System.Collections.Generic.IEnumerable
//
// 例外:
// System.ArgumentNullException:
// source が null です。

0 1 2 3 4 5 4 3 2 1 0 の重複を取り除く
using System;
using System.Linq;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            int[] num = { 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0 };
            
            foreach (var n in num.Distinct()) {
                Console.WriteLine(n);
            }
        }
    }
}

実行結果
0
1
2
3
4
5

2015年1月3日土曜日

DefaultIfEmpty

■public static IEnumerable DefaultIfEmpty(this IEnumerable source);
// 概要:
// 指定したシーケンスの要素を返します。シーケンスが空の場合はシングルトン コレクションにある型パラメーターの既定値を返します。
//
// パラメーター:
// source:
// シーケンスが空の場合に、既定値を返すシーケンス。
//
// 型パラメーター:
// TSource:
// source の要素の型。
//
// 戻り値:
// source が空の場合、TSource 型の既定値を格納する System.Collections.Generic.IEnumerable
// オブジェクト。それ以外の場合は source。
//
// 例外:
// System.ArgumentNullException:
// source が null です。

最初に配列の要素数を用意し、あるルールに従ってその数字の要素数に値を格納する時
残った部分が未定義になってしまうのでDefault値を設定してしまおうという場合
using System;
using System.Linq;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            int[] numList = new int[10];
            numList[3] = 3;
            numList[5] = 5;

            var numDefault = numList.DefaultIfEmpty();

            foreach (var num in numDefault) {
                Console.WriteLine(num);
            }
        }
    }
}
要素番号3,5以外は0になります。


ちなみに、Listに使うとnullが代入されます
using System;
using System.Collections.Generic;
using System.Linq;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            List strList = new List();
            Console.WriteLine(strList.Count()); //0

            var strDefault = strList.DefaultIfEmpty();
            Console.WriteLine(strDefault.Count()); //0

            foreach (var str in strDefault) {
                Console.WriteLine(str == null); //True
            }
        }
    }
}

Count

■public static int Count
(this IEnumerable source, Func predicate);
// 概要:
// 条件を満たす、指定されたシーケンス内の要素の数を表す数値を返します。
//
// パラメーター:
// source:
// テストおよびカウントする要素が格納されているシーケンス。
//
// predicate:
// 各要素が条件を満たしているかどうかをテストする関数。
//
// 型パラメーター:
// TSource:
// source の要素の型。
//
// 戻り値:
// 述語関数の条件を満たす、シーケンス内の要素数を表す数値。
//
// 例外:
// System.ArgumentNullException:
// source または predicate が null です。
//
// System.OverflowException:
// source 内の要素数が System.Int32.MaxValue を超えています。

using System;
using System.Linq;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            string test = "gooooooooooooooooooooooooooogle";
            
            //LINQで oの個数を調べる
            int count = test.Count(c => c == 'o'); //ラムダ式を渡す
            Console.WriteLine("oの数は{0}個です", count); //oの数は27個です
        }
    }
}

2015年1月2日金曜日

AsParallel

■public static ParallelQuery AsParallel
(this IEnumerable source);
// 概要:
// クエリの並列化を有効にします。
//
// パラメーター:
// source:
// System.Linq.ParallelQuery に変換する System.Collections.Generic.IEnumerable
//
// 型パラメーター:
// TSource:
// source の要素の型。
//
// 戻り値:
// ParallelEnumerable 拡張メソッドにバインドされる System.Linq.ParallelQuery としての
// source。
//
// 例外:
// System.ArgumentNullException:
// source が null 参照 (Visual Basic の場合は Nothing) です。



using System;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] a = new int[100000000];
            a[a.Length - 1] = 1;
            var start1 = DateTime.Now;
            var bySerial = a.Count(c => c == 1);
            Console.WriteLine("Serial  :{0}", DateTime.Now - start1);

            var start2 = DateTime.Now;
            var byParallel = a.AsParallel().Count(c => c == 1);
            Console.WriteLine("Parallel:{0}", DateTime.Now - start2);
        }
    }
}

2コアのPCで実行したとき下記のような差がでました。
Serial :00:00:01.0920000
Parallel:00:00:00.7488000

並列化された場合どの順に処理が完了するか不明なため
順番が保障されなくなる点など気をつける必要が出てきますね。

Any

■public static bool Any
(this IEnumerable source);
// 概要:
// シーケンスに要素が含まれているかどうかを判断します。
//
// パラメーター:
// source:
// 空かどうかを確認する System.Collections.Generic.IEnumerable
//
// 型パラメーター:
// TSource:
// source の要素の型。
//
// 戻り値:
// ソース シーケンスに要素が含まれている場合は true。それ以外の場合は false。
//
// 例外:
// System.ArgumentNullException:
// source が null です。

引数がないと単純に要素を持っているかどうかを判定するだけのようですね。


■public static bool Any(this IEnumerable source, Func predicate);
// 概要:
// シーケンスの任意の要素が条件を満たしているかどうかを判断します。
//
// パラメーター:
// source:
// 述語を適用する要素を含む System.Collections.Generic.IEnumerable
//
// predicate:
// 各要素が条件を満たしているかどうかをテストする関数。
//
// 型パラメーター:
// TSource:
// source の要素の型。
//
// 戻り値:
// 指定された述語でソース シーケンスの要素がテストに合格する場合は true。それ以外の場合は false。
//
// 例外:
// System.ArgumentNullException:
// source または predicate が null です。

要素中に少なくとも一つ、引数に渡した関数がtrueであればtrueを返す

using System;
using System.Linq;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            int[] emptyList = { };
            //要素はありますか?
            Console.WriteLine(emptyList.Any()); //False
            
            int[] numberList = { 1, 3, 5, 7 };
            //要素はありますか?
            Console.WriteLine(numberList.Any()); //True

            //2の倍数はありますか?
            Console.WriteLine(numberList.Any(num => num % 2 == 0)); //False

            //3の倍数はありますか?
            Console.WriteLine(numberList.Any(num => num % 3 == 0)); //True
        }
    }
}

All

■public static bool All
(this IEnumerable source, Func predicate);
// 概要:
// シーケンスのすべての要素が条件を満たしているかどうかを判断します。
//
// パラメーター:
// source:
// 述語を適用する要素を格納している System.Collections.Generic.IEnumerable
//
// predicate:
// 各要素が条件を満たしているかどうかをテストする関数。
//
// 型パラメーター:
// TSource:
// source の要素の型。
//
// 戻り値:
// 指定された述語でソース シーケンスのすべての要素がテストに合格する場合は true。それ以外の場合は false。
//
// 例外:
// System.ArgumentNullException:
// source または predicate が null です。

例えば、商品のリストがあるとして負の値があればエラーとしたい時
using System;
using System.Linq;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            int[] itemList = new int[] { 110, 210, 310, -100 };

            if (itemList.All(num => num < 0)) {
                Console.WriteLine("引数に問題ありません");
            }else{
                Console.WriteLine("引数に問題あります");
            }
        }
    }
}
こんな風にかけると。

Aggregate

各要素に対しアキュームレートするメソッドです。
アキュムレーターとは、演算結果を累積する処理のことを指すようです。

■public static TSource Aggregate
(this IEnumerable source, Func func);
// 概要:
// シーケンスにアキュムレータ関数を適用します。
//
// func:
// 各要素に対して呼び出すアキュムレータ関数。
//
// 型パラメーター:
// TSource:
// source の要素の型。
//
// 戻り値:
// 最終的なアキュムレータ値。
//
// 例外:
// System.ArgumentNullException:
// source または func が null です。
//
// System.InvalidOperationException:
// source に要素が含まれていません。

■public static TAccumulate Aggregate
(this IEnumerable source, TAccumulate seed, Func func);
//
// 概要:
// シーケンスにアキュムレータ関数を適用します。指定されたシード値が最初のアキュムレータ値として使用されます。
//
// パラメーター:
// source:
// 集計対象の System.Collections.Generic.IEnumerable
//
// seed:
// 最初のアキュムレータ値。
//
// func:
// 各要素に対して呼び出すアキュムレータ関数。
//
// 型パラメーター:
// TSource:
// source の要素の型。
//
// TAccumulate:
// アキュムレータ値の型。
//
// 戻り値:
// 最終的なアキュムレータ値。
//
// 例外:
// System.ArgumentNullException:
// source または func が null です。

■public static TResult Aggregate
(this IEnumerable source, TAccumulate seed, Func func, Func resultSelector);
// 概要:
// シーケンスにアキュムレータ関数を適用します。
// 指定したシード値は最初のアキュムレータ値として使用され、
// 指定した関数は結果値の選択に使用されます。
//
// パラメーター:
// source:
// 集計対象の System.Collections.Generic.IEnumerable
//
// seed:
// 最初のアキュムレータ値。
//
// func:
// 各要素に対して呼び出すアキュムレータ関数。
//
// resultSelector:
// 最終的なアキュムレータ値を結果値に変換する関数。
//
// 型パラメーター:
// TSource:
// source の要素の型。
//
// TAccumulate:
// アキュムレータ値の型。
//
// TResult:
// 結果の値の型。
//
// 戻り値:
// 変換された最終的なアキュムレータ値。
//
// 例外:
// System.ArgumentNullException:
// source、func、または resultSelector が null です。


aggregateメソッドでは名前の通り何かを行って集計するメソッドのようです。

こんなシチュエーションを考えてみました。
レジをイメージして商品のリスト(外税)に対し、お金を渡し消費税を掛けた金額を
差し引いていき、その結果いくら残ったか知りたいというような状況

using System;
using System.Linq;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            double tax = 1.05;
            int initMoney = 1000;
            int[] itemList = new int[] { 110, 210, 310 };
            
            var resutMoney = itemList.Aggregate(initMoney, (money, num) => (money - (int)(num * tax)));
            Console.WriteLine(resutMoney);
        }
    }
}

商品のリストに3つ格納されているので3回繰り返し処理を行います。
この時ラムダ式内の各変数は以下のようになります。

1回目
money:1000 //initMoneyの値を受け取る
num : 110 //itemListの最初の値を受け取る

money :885 //1個目の値が差し引かれる

2回目
money:885 //1ループ目の結果を受け取る
num : 210 //itemListの2つ目の値を受け取る

money :665 //2個目の値が差し引かれる

3回目
money:665 //2ループ目の結果を受け取る
num : 310 //itemListの3つ目の値を受け取る

money :340 //3個目の値が差し引かれる

340が戻り値となる

これが2引数の場合ですね。
TAccumulate seed
Func func


せっかくなので3引数の場合も試してみます。
上の例では消費税をかけたものを切り捨てして引き算しています。
消費税をかけたのち、切り捨てせずに引き算して一番最後に切り捨てしてみましょう。

確認。
消費税をかけて切り捨てをした結果を引き算すると
340になります。

まずは、切り捨てを辞めて小数で結果を返してみますか。
using System;
using System.Linq;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            double tax = 1.05;
            int initMoney = 1000;
            int[] itemList = new int[] { 110, 210, 310 };
            
            var resutMoney = itemList.Aggregate((double)initMoney, 
                                                (money, num) => (money - num * tax)
                                                );
            Console.WriteLine(resutMoney);
        }
    }
}
これなら、結果は338.5になります。
338.5に対し切り捨てを行ってみます。
第3引数で切り捨ての処理をしてみます。

using System;
using System.Linq;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            double tax = 1.05;
            int initMoney = 1000;
            int[] itemList = new int[] { 110, 210, 310 };
            
            var resutMoney = itemList.Aggregate((double)initMoney, 
                                                (money, num) => (money - num * tax),
                                                money2 => Math.Floor(money2)
                                                );
            Console.WriteLine(resutMoney);
        }
    }
}

(money, num) => (money - num * tax)で集約処理をした結果を
money2 => Math.Floor(money2) の式に渡しその結果を出力しています。
その結果338がかえってきます。

これが3引数の場合ですね。
TAccumulate seed
Func func
Func resultSelector


では、1引数の時はというと最初の要素がmoneyに渡され、第二要素以降がnumに渡されます。
(money, num) => (money - num * tax)

つまり、最初の例は
using System;
using System.Linq;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            double tax = 1.05;
            int[] itemList = new int[] { 1000, 110, 210, 310 }; //所持金を第一要素に格納した
            
            var resutMoney = itemList.Aggregate((money, num) => (money - (int)(num * tax)));
            Console.WriteLine(resutMoney);
        }
    }
}
結果は一緒ですが、商品のリストに所持金をいれるのは解釈的に気持ち悪いですね。
もっと他の最適な例があるのかもしれないのでそういう時に使うとします。

念のためラムダ式内の各変数は以下のようになります。

1回目
money:1000 //itemListの最初の値を受け取る
num : 110 //itemListの2番目の値を受け取る

money :885 //1個目の値が差し引かれる

2回目
money:885 //1ループ目の結果を受け取る
num : 210 //itemListの3番目の値を受け取る

money :665 //2個目の値が差し引かれる

3回目
money:665 //2ループ目の結果を受け取る
num : 310 //itemListの4番目の値を受け取る

money :340 //3個目の値が差し引かれる

340が戻り値となる

LINQ SampleCode

あの書き方どうだったっけというときに参照するようにいろいろコードを書いておく

ってときに見る用にいろんなサンプルコードを置いておく
・特定の文字をカウントする
string test = "gooooooooooooooooooooooooooogle";

//LINQで oの個数を調べる
int count = test.Count(c => c == 'o');

Console.WriteLine("oの数は{0}個です", count.ToString());


・数字を倍にして配列で受け取る
int[] num = { 1, 2, 3, 4, 5 };
num = num.Select(n => n * 2).ToArray();

・最初に現れる偶数は何番目か?
int[] numList = { 3, 8, 15, 18 };
Console.WriteLine(numList.Select((num, indexNo) => new { num ,indexNo})
                         .First(n =>  n.num % 2 == 0).indexNo);
Selectの第二引数を指定することで要素番号を取得する
Select内では、
numをキーにnumListから受け取る数字を値に指定
indexNoをキーに要素数を指定
Firstで2で割った時に0になる物が現れた時終了し
そのオブジェクトのindexNo(要素番号)を返す処理

・動的なAND条件
手っ取り早く、ある文字列の配列に対し、特定の文字全てを含んでいるかチェック
using System;
using System.Linq;

namespace ConsoleApplication1
{   
    class Program
    {
        static void Main(string[] args)
        {
            string[] str = { "睦月", "如月", "弥生", "卯月", "皐月", "水無月", "文月", "葉月", "長月", "神無月", "霜月", "師走"};

            string[] check = { "月", "無" };

            var result = str.Where(s => check.All(c => -1 < s.IndexOf(c)));

            Console.WriteLine(string.Join(", ", result)); //水無月, 神無月
        }
    }
}
・動的なOR条件 手っ取り早く、ある文字列の配列に対し、特定の文字がどれか含んでいるかチェック
using System;
using System.Linq;

namespace ConsoleApplication1
{   
    class Program
    {
        static void Main(string[] args)
        {
            string[] str = { "睦月", "如月", "弥生", "卯月", "皐月", "水無月", "文月", "葉月", "長月", "神無月", "霜月", "師走"};

            string[] check = { "弥", "無" };

            var result = str.Where(s => check.Any(c => -1 < s.IndexOf(c)));

            Console.WriteLine(string.Join(", ", result)); //弥生, 水無月, 神無月
        }
    }
}

2015年1月1日木曜日

LINQを学習してみる

LINQ to Objectsとは
"LINQ to Objects" という用語は、LINQ to SQL [LINQ to SQL] や LINQ to XML などの中間 LINQ プロバイダーまたは API を使用せずに、LINQ クエリを任意の IEnumerable コレクションまたは IEnumerable コレクションと直接組み合わせて使用することを意味します。
http://msdn.microsoft.com/ja-jp/library/bb397919.aspx

IEnumerableを継承していれば使えるということで
例えばstring型なんかも継承しているので使うことができます。

たとえば、下記文字列にoが何個含まれているか調べるとすると
gooooooooooooooooooooooooooogle


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

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            string test = "gooooooooooooooooooooooooooogle";
            //Inumerableが継承されているか確認する
            Console.WriteLine("Inumerable実装されて{0}", (test is IEnumerable) ? "いる" : "いない");

            //LINQで oの個数を調べる
            int count = test.Count(c => c == 'o'); //ラムダ式を渡す
            Console.WriteLine("oの数は{0}個です", count.ToString());
        } 
    }

}

とても便利ですね。
string型の場合一文字ずつ渡されるのでそれをcという変数で受け取り
カウントするさいの条件式を渡してあげているわけですね。
ラムダ式になじみがないとみづらいかもしれませんが
c => c == 'o' は、javascriptで言えばこれと一緒ですね
function(c){ return c == "o" }


LINQを使わないのであれば下記のようになりそうですね。
//LINQで oの個数を調べる
            int count = test.Count(c => c == 'o'); //ラムダ式を渡す
            Console.WriteLine("oの数は{0}個です", count.ToString());
            ↓
            //LINQで oの個数を調べる
            int count = 0;
            for (int i = 0; i < test.Length; i++) {
                count += (test.Substring(i, 1) == "o") ? 1 : 0;
            }

            Console.WriteLine("oの数は{0}個です", count.ToString());

LINQなら、明示的に1文字ずつ取り出す処理を書かなくてもやってくれるあたりがらくちんなんでしょうね。
と、いうことで使い方がすぐになれないLINQなので少しずつ勉強して使い方を覚えていく予定。