2015年1月2日金曜日

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が戻り値となる

0 件のコメント:

コメントを投稿