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 で置換してまわらないとダメっぽい。