最近、.net 3.0(Windowsフォーム、C#)に移行しました。述語とラムダ式についてもっと知りたいです。どこで使うべきですか?それらはパフォーマンスを改善しますか?そしてそれらは内部でどのように機能しますか。ありがとう。
3 に答える
Stack Overflowを検索すると、その目的を説明する約1,000の回答が見つかります。要するに、ラムダは、別のメソッドに渡したいポイントで匿名メソッドを作成する方法です。技術的には匿名メソッドの構文と同じdelegateですが、型推論の機能が追加されているため、パラメーターの型を指定する必要はありません。述語は、ある値を受け入れてaを返すメソッドです。bool例として、の引数がありますWhere。
外部変数を参照しないラムダは、作成された名前を持つプライベート静的メソッドに変換されます。囲んでいるクラスのインスタンスメンバーを参照している場合は、インスタンスメソッドになります。ローカル変数を参照する場合、それらの変数は、包含メソッドの実行開始時に割り当てられるコンパイラー生成クラスのフィールドに「引き上げられ」、ラムダの本体はその新しいクラスのメソッドになります。
パフォーマンスに関しては、それほど大きな違いはありません。それらは一時的なオブジェクトの作成を伴いますが、これらはGCによって非常に効率的に収集されることがわかりました。
C#のさまざまなバージョンとそれらがどのように異なるかを調べたい場合は、ジョン・スキートの本C.Sharp.in.Depthを読んでください。これにより、新しいバージョンをよりよく理解できるようになります
それらはパフォーマンスを改善しますか?そしてそれらは内部でどのように機能しますか。ありがとう。
ほとんどの場合、パフォーマンスの低下に気付くことはありません。ただし、パフォーマンスを低下させる病理学的なケースがいくつかあります。つまり、固定小数点コンビネータの過度の使用です。
Y-combinatorを使用して再帰的なラムダ関数を記述できることはよく知られているトリックですが、次のコードを検討してください。
using System;
using System.Diagnostics;
namespace YCombinator
{
class Program
{
static Func<T, U> y<T, U>(Func<Func<T, U>, Func<T, U>> f)
{
return f(x => y<T, U>(f)(x));
}
static int fibIter(int n)
{
int fib0 = 0, fib1 = 1;
for (int i = 1; i <= n; i++)
{
int tmp = fib0;
fib0 = fib1;
fib1 = tmp + fib1;
}
return fib0;
}
static Func<int, int> fibCombinator()
{
return y<int, int>(f => n =>
{
switch (n)
{
case 0: return 0;
case 1: return 1;
default: return f(n - 1) + f(n - 2);
}
});
}
static int fibRecursive(int n)
{
switch (n)
{
case 0: return 0;
case 1: return 1;
default: return fibRecursive(n - 1) + fibRecursive(n - 2);
}
}
static void Benchmark(string msg, int iterations, Func<int, int> f)
{
int[] testCases = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20 };
Stopwatch watch = Stopwatch.StartNew();
for (int i = 0; i <= iterations; i++)
{
foreach (int n in testCases)
{
f(n);
}
}
watch.Stop();
Console.WriteLine("{0}: {1}", msg, watch.Elapsed.TotalMilliseconds);
}
static void Main(string[] args)
{
int iterations = 10000;
Benchmark("fibIter", iterations, fibIter);
Benchmark("fibCombinator", iterations, fibCombinator());
Benchmark("fibRecursive", iterations, fibRecursive);
Console.ReadKey(true);
}
}
}
このプログラムは次のように出力します。
fibIter:14.8704 fibCombinator:61775.1485 fibRecursive:2591.2444
fibCombinatorとfibRecursiveは機能的に同等であり、計算の複雑さは同じですが、fibCombinatorは、すべての中間オブジェクト割り当てのために、完全に4100倍遅くなります。