System.Linq.Expressions.Expression の問題点

さらにいろいろと Expression Tree を触ってみて思ったこと。
一度 Expression 型を自前のラムダ計算クラスに変換して、自前のクラスで微分して、最後に Expression に戻して Compile する方がいいかもしれない。

ラムダ計算するなら、やっぱり↓みたいなことしたいし。

Lambda f1 = Lambda.New((x, y) => x * y);
Lambda f2 = Lambda.New((x, y) => x * y);
Lambda f = f1 * f2;

こういうことをする上で、障害は、Expression 型が operator をサポートしてないのと、あと、↓みたいな問題。

static void RightCase()
{
	var x = Expression.Parameter(typeof(double), "x");
	var e = Expression.Lambda<Func<double, double>>(
		Expression.Multiply(x, x),
		x);

	var f = e.Compile(); // これは OK

	Console.Write(f(10));
}

static void WrongCase()
{
	var x1 = Expression.Parameter(typeof(double), "x");
	var x2 = Expression.Parameter(typeof(double), "x");
	var e = Expression.Lambda<Func<double, double>>(
		Expression.Multiply(x1, x2),
		x1);

	var f = e.Compile(); // ここで例外発生

	Console.Write(f(10));
}

ParameterExpression、型と名前が一致していても、別インスタンスは別パラメータ扱いされるっぽい。reference equal でしか等価判定してくれないのね。

operator はないにしても、例えば以下のような Add を書けば Expression 同士の和が取れるかというと、無理。

static void AddExpression()
{
	Expression<Func<double, double>> e1 = x => x * x;
	Expression<Func<double, double>> e2 = x => x;
	var e = Add(e1, e2);

	// e は Expression としては問題なく使える。
	Console.Write("{0}\n", e);

	// でも、Compile した瞬間にエラーに。
	var f = e.Compile();
	Console.Write("{0}\n", f(10));
}

static Expression<T> Add<T>(
	Expression<T> e1,
	Expression<T> e2)
{
	return Expression.Lambda<T>(
		Expression.Add(e1.Body, e2.Body),
		e1.Parameters);
}

これで Compile できるようにしようと思うと、e2 の中の ParameterExpression をことごとく全部 e1 の Paramters で置換してまわらないとダメっぽい。