C# Aggregate アキュムレータ

C# Aggregate アキュムレータ

アキュムレータとは何か?アキュムレータの使い方は?焦らないでください。新しい技術の誕生は、基本的には何らかのニーズを満たすためです。ニーズから出発することで、この関数の特徴をより理解しやすくなります。

最終更新 2022/04/20 7:11
遇水寒
読了目安 4 分
カテゴリ
.NET
タグ
.NET C# Aggregate

1. 需要

アキュムレータとは何か?アキュムレータの使い方は?焦らずに、新しい技術が生まれるのは基本的に何らかのニーズを満たすためであり、ニーズから考えることで、この関数の特性を理解しやすくなります。

理解を容易にするために、int型の1次元配列があり、5つの数字が格納されていると仮定し、最初の値(-1)と2番目の値(0)の和(-1)を求め、その求めた値(-1)と配列の3番目の数(3)を加算し、再度和(2)を求め、さらにその値と配列の4番目の数(5)を加算する... これを繰り返し、結局は int result = -1 + 0 + 3 + 5 + 8; となる。

int[] numbers={-1, 0, 3, 5,8};

1.1 基本要件

//V1.0 バージョン
static void Main(string[] args)
{
    int[] numbers = { -1, 0, 3, 5, 8 };
    int result = numbers[0];
    for (int i = 1; i < numbers.Length; i++)
    {
        result = result + numbers[i];
    }
    Console.WriteLine(result);//出力:15
    Console.ReadKey();
}

1.2 アルゴリズム部分のカプセル化

中央のアルゴリズムを抽出することで、他の異なる長さのint型配列を入力できるようになります:

//V1.1 バージョン
static void Main(string[] args)
{
    int[] numbers = { -1, 0, 3, 5, 8 };
    var result = Aggregate(numbers);
    Console.WriteLine(result);
    Console.ReadKey();
}

static int Aggregate(int[] array)
{
    int result = array[0];
    for (int i = 1; i < array.Length; i++)
    {
        result = result + array[i];
    }
    return result;
}

1.3 加算だけではない – アルゴリズムの置き換え

アキュムレータは加算だけでなく、例えば次のような演算も行える必要があります:

int result = -1 * 0 * 3 * 5 * 8;

int result = -1 - 0 - 3 - 5 - 8;
//V1.2 バージョン、アルゴリズム置き換えの実現
// デリゲート、ラムダに関する知識が必要
static void Main(string[] args)
{
    int[] numbers = { -1, 0, 3, 5, 8 };
    var result = Aggregate(numbers, (result, next) => result * next); // 乗算を実現
    //var result = Aggregate(numbers, (result, next) => result - next); // 減算を実現
    Console.WriteLine(result);
    Console.ReadKey();
}

static int Aggregate(int[] array, Func<int, int, int> func)
{
    int result = array[0];
    for (int i = 1; i < array.Length; i++)
    {
        result = func(result, array[i]);
    }
    return result;
}

1.4 int型だけではない – ジェネリック

現在このアルゴリズム int Aggregate(int[] array, Func<int,int,int> func) はint型のみサポートしていますが、任意の型に拡張する必要があります – ジェネリック。

//V1.3 バージョン、ジェネリックの実現
// ジェネリック、IEnumerator インターフェースに関する知識が必要
static void Main(string[] args)
{
    int[] numbers = { -1, 0, 3, 5, 8 };
    var result = Aggregate(numbers, (result, next) => result + next);
    Console.WriteLine(result);
    Console.ReadKey();
}

static TSource Aggregate<TSource>(IEnumerable<TSource> source, Func<TSource, TSource, TSource> func)
{
    using (IEnumerator<TSource> e = source.GetEnumerator())
    {
        if (!e.MoveNext())
        {
            throw new ArgumentException();
        }

        TSource result = e.Current;
        while (e.MoveNext())
        {
            result = func(result, e.Current);
        }

        return result;
    }
}

1.5 拡張メソッドとしての実装

IEnumerator インターフェースを実装したクラスに直接拡張メソッドを追加します。これで、この Aggregate の実装は公式で提供されているアキュムレータと類似したものになります。

//V1.4 バージョン、拡張メソッド
class Program
{
    static void Main(string[] args)
    {
        int[] numbers = { -1, 0, 3, 5, 8 };
        var result = numbers.Aggregate((result, next) => result + next);
        Console.WriteLine(result);
        Console.ReadKey();
    }
}

public static class Helper
{
    public static TSource Aggregate<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func)
    {
        using (IEnumerator<TSource> e = source.GetEnumerator())
        {
            if (!e.MoveNext())
            {
                throw new ArgumentException("少なくとも2つの要素が必要です");
            }

            TSource result = e.Current;
            while (e.MoveNext())
            {
                result = func(result, e.Current);
            }

            return result;
        }
    }
}

2. マイクロソフト提供のAPI

System.Linq 名前空間には、次のものが用意されています:

1. public static TSource Aggregate<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func);

2. public static TAccumulate Aggregate<TSource, TAccumulate>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func);

3. public static TResult Aggregate<TSource, TAccumulate, TResult>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func, Func<TAccumulate, TResult> resultSelector);

このうち最初のインターフェースは、上記の例と同じ機能です。他の2つの機能については後で説明します(時間があれば)。

3. 浅いところから深いところへ

初心者の場合、頭の中ではこの Aggregate は足し算・引き算・掛け算・割り算しかできないと思いがちですが、もしそう考えるなら、まだ理解が浅いということです。ここでこのメソッドの引数 func を深く分析してみましょう。

下図に示すように、アキュムレータは各要素を走査します。最初は最初と2番目の要素を直接 func 演算する以外は、3番目以降は前回の計算結果 result を func の最初の入力パラメーターとし、2番目の入力パラメーターは配列の次の要素とします。最後まで走査したら、最終的な result の値を返します。

実際の使用例:データベース内の1つのテーブルが複数のテーブルの外部キーを参照している場合、これらの外部キーをクエリする必要があるとき、アキュムレータを使用して、クエリ条件を累積し、データベースにクエリを実行できます。

さらに探索

関連読書

その他の記事
同じカテゴリ / 同じタグ 2026/04/22

各OSバージョンの.NETサポート状況(250707更新)

仮想マシンとテストマシンを使用して、各OSバージョンの.NETサポート状況を確認します。OSインストール後、対応するランタイムをインストールし、Stardustエージェントを実行できることを確認します(合格条件)。

続きを読む
同じカテゴリ / 同じタグ 2026/02/07

AOTの使用経験のまとめ

プロジェクト作成当初から、新機能を追加したり新しい構文を使用したりした場合には、すぐにAOT公開テストを実施するという良い習慣を身につけるべきです。

続きを読む