ダミーのための表現ツリー?[閉まっている]


83

このシナリオでは、私はダミーです。

私はこれらが何であるかをグーグルで読もうとしました、しかし私はそれを理解しません。誰かが私にそれらが何であるか、そしてなぜそれらが有用であるかについて簡単な説明を与えることができますか?

編集:私は.NetのLINQ機能について話している。


1
この投稿はかなり古いことは知っていますが、最近、式ツリーを調べています。Fluent NHibernateを使い始めてから、興味を持ちました。James Gregoryは、静的リフレクションと呼ばれるものを幅広く使用しており、イントロがあります。jagregory.com / writings / introduction- to-static-reflection静的リフレクションと式ツリーの動作を確認するには、Fluent NHibernateソースコード(fluentnhibernate.org)を確認してください。)。それはとてもきれいで、とてもクールなコンセプトです。
Jim Schubert

回答:


89

私が今まで読んだ式ツリーについての最も良い説明は、CharlieCalvertによるこの記事です。

要約すると、

式ツリーは実行方法ではなく、実行したいことを表します。

次の非常に単純なラムダ式について考えてみます。
Func<int, int, int> function = (a, b) => a + b;

このステートメントは、次の3つのセクションで構成されています。

  • 宣言: Func<int, int, int> function
  • 等号演算子: =
  • ラムダ式: (a, b) => a + b;

変数は、2つの数値を加算する方法を知っfunctionいる生の実行可能コードを指します

これは、デリゲートと式の最も重要な違いです。渡した2つの整数で何が行われるかを知らずにfunction、(a Func<int, int, int>)を呼び出します。それは2つかかり、1つを返します。これは、コードが知ることができる最も多くのことです。

前のセクションでは、生の実行可能コードを指す変数を宣言する方法を見ました。式ツリーは実行可能コードはなく、データ構造の形式です。

これで、デリゲートとは異なり、コード式ツリーが何を意味するのか知ることができます。

LINQは、コードを式ツリーと呼ばれるデータ構造に変換するための単純な構文を提供します。最初のステップは、Linq.Expressions名前空間を導入するためのusingステートメントを追加することです。

using System.Linq.Expressions;

これで、式ツリーを作成できます。
Expression<Func<int, int, int>> expression = (a, b) => a + b;

前の例で示した同一のラムダ式は、タイプが宣言された式ツリーに変換されExpression<T>ます。識別子expression 実行可能コードではありません。これは、式ツリーと呼ばれるデータ構造です。

つまり、デリゲートを呼び出すように式ツリーを呼び出すだけでなく、それを分析することもできます。では、変数を分析することで、コードは何を理解できるexpressionでしょうか。

// `expression.NodeType` returns NodeType.Lambda.
// `expression.Type` returns Func<int, int, int>.
// `expression.ReturnType` returns Int32.

var body = expression.Body;
// `body.NodeType` returns ExpressionType.Add.
// `body.Type` returns System.Int32.

var parameters = expression.Parameters;
// `parameters.Count` returns 2.

var firstParam = parameters[0];
// `firstParam.Name` returns "a".
// `firstParam.Type` returns System.Int32.

var secondParam = parameters[1].
// `secondParam.Name` returns "b".
// `secondParam.Type` returns System.Int32.

ここでは、式から取得できる情報がたくさんあることがわかります。

しかし、なぜそれが必要なのでしょうか?

式ツリーは、実行可能コードを表すデータ構造であることを学びました。しかし、これまでのところ、なぜそのような変換をしたいのかという中心的な質問には答えていません。これは、この投稿の冒頭で私たちが尋ねた質問であり、今度はそれに答える時が来ました。

LINQ to SQLクエリは、C#プログラム内では実行されません。代わりに、SQLに変換され、ネットワークを介して送信され、データベースサーバーで実行されます。つまり、次のコードがプログラム内で実際に実行されることはありません。
var query = from c in db.Customers where c.City == "Nantes" select new { c.City, c.CompanyName };

最初に次のSQLステートメントに変換されてから、サーバーで実行されます。
SELECT [t0].[City], [t0].[CompanyName] FROM [dbo].[Customers] AS [t0] WHERE [t0].[City] = @p0

クエリ式で見つかったコードは、文字列として別のプロセスに送信できるSQLクエリに変換する必要があります。この場合、そのプロセスはたまたまSQLサーバーデータベースです。生のILまたは実行可能コードをSQLに変換するよりも、式ツリーなどのデータ構造をSQLに変換する方がはるかに簡単であることは明らかです。問題の難しさをいくらか誇張するために、一連の0と1をSQLに変換しようとしているところを想像してみてください。

クエリ式をSQLに変換するときは、前のセクションで単純なラムダ式ツリーを分解したのと同じように、クエリを表す式ツリーを分解して分析します。確かに、LINQ to SQL式ツリーを解析するためのアルゴリズムは、使用したものよりもはるかに洗練されていますが、原理は同じです。式ツリーの一部を分析すると、LINQはそれらを熟考し、要求されたデータを返すSQLステートメントを作成するための最良の方法を決定します。

式ツリーは、クエリ式などのコードを他のプロセスに渡してそこで実行できる文字列に変換するタスクを作成するために作成されました。とても簡単です。ここには大きな謎はなく、手を振る必要のある魔法の杖もありません。コードを取得してデータに変換し、データを分析して、別のプロセスに渡すことができる文字列に変換される構成要素を見つけるだけです。

クエリは、そのような抽象的なデータ構造にカプセル化されたコンパイラに送られるため、コンパイラは、ほぼすべての方法でクエリを自由に解釈できます。特定の順序または特定の方法でクエリを実行する必要はありません。代わりに、式ツリーを分析し、実行したいことを見つけて、それを実行する方法を決定できます。少なくとも理論的には、現在のネットワークトラフィック、データベースの負荷、利用可能な現在の結果セットなど、任意の数の要因を考慮する自由があります。実際には、LINQ toSQLはこれらすべての要因を考慮しません。 、しかし理論的には、やりたいことをほぼ自由に行うことができます。さらに、この式ツリーを手動で記述したカスタムコードに渡して、それを分析し、LINQ toSQLによって生成されるものとは非常に異なるものに変換することができます。

繰り返しになりますが、式ツリーを使用すると、実行したいことを表現(表現?)できることがわかります。また、表現の使用方法を決定するトランスレータを使用します。


2
より良い答えの1つ。
ジョニー・

4
優れた答え。この素晴らしい説明に追加する小さな側面の1つは、式ツリーのもう1つの使用法は、実行時に式ツリーをフィードして実行する前に適切と思われる場合に、実行時にその場で式ツリーを変更できることです。これは非常に便利です。
ヤンD

41

式ツリーは、実行可能コードをデータに変換するメカニズムです。式ツリーを使用して、プログラムを表すデータ構造を作成できます。

C#では、Expression<T>クラスを使用して、ラムダ式によって生成された式ツリーを操作できます。


従来のプログラムでは、次のようなコードを記述します。

double hypotenuse = Math.Sqrt(a*a + b*b);

このコードにより、コンパイラーは割り当てを生成します。それだけです。ほとんどの場合、気にするのはそれだけです。

従来のコードでは、アプリケーションはさかのぼって戻っhypotenuseて、Math.Sqrt()呼び出しを実行することによって生成されたものであるかどうかを確認することはできません。この情報は、含まれているものの一部ではありません。

ここで、次のようなラムダ式について考えてみます。

Func<int, int, double> hypotenuse = (a, b) => Math.Sqrt(a*a + b*b);

これは以前とは少し異なります。これhypotenuseは、実際には実行可能コードのブロックへの参照です。あなたが電話する場合

hypotenuse(3, 4);

返される値を取得します5

式ツリーを使用して、生成された実行可能コードのブロックを探索できます。代わりにこれを試してください:

Expression<Func<int, int, int>> addTwoNumbersExpression = (x, y) => x + y;
BinaryExpression body = (BinaryExpression) addTwoNumbersExpression.Body;
Console.WriteLine(body);

これにより、次のものが生成されます。

(x + y)

式ツリーを使用すると、より高度な手法と操作が可能になります。


7
OK私は最後まであなたと一緒にいましたが、なぜこれが大したことなのかまだよくわかりません。アプリケーションについて考えるのに苦労しています。

1
彼は単純化された例を使用していました。真の力は、式ツリーを探索するコードが、それを解釈し、式に意味的な意味を適用する責任を負わせることができるという事実にあります。
ピエールテン2010年

2
はい、(x + y)が実際に私たちに役立つ理由を彼/彼女が説明したなら、この答えはもっと良かったでしょう。なぜ(x + y)を探索したいのですか?それをどのように行うのですか?
ポールマシューズ

あなたはちょうどあなたのクエリと何がSQLにその場合のいくつかの他の言語に翻訳されているかを確認するためにそれを行う、それを探求する必要はありません
stanimirsp

15

式ツリーは、算術式やブール式など、式のメモリ内表現です。たとえば、算術式について考えてみましょう。

a + b*2

*は+よりも演算子の優先順位が高いため、式ツリーは次のように作成されます。

    [+]
  /    \
 a     [*]
      /   \
     b     2

このツリーがあれば、aとbの任意の値を評価できます。さらに、たとえば式を導出するために、それを他の式ツリーに変換できます。

式ツリーを実装するときは、基本クラスのを作成することをお勧めします それから派生して、クラスBinaryExpressionは、+や*などのすべてのバイナリ式に使用されます。次に、VariableReferenceExpressionを参照変数(aやbなど)に導入し、別のクラスConstantExpression(例の2 )を導入できます。

多くの場合、式ツリーは、(ユーザーから直接、またはファイルから)入力を解析した結果として作成されます。式ツリーを評価するには、Visitorパターンを使用することをお勧めします


15

簡単な答え:同じ種類のLINQクエリを記述して、任意のデータソースを指すことができるのは素晴らしいことです。それなしでは「統合言語クエリ」を作成することはできません。

長い答え:ご存知かもしれませんが、ソースコードをコンパイルすると、ある言語から別の言語に変換されます。通常、高級言語(C#)から下のレバーオン(IL)まで。

これを行うには、基本的に2つの方法があります。

  1. 検索と置換を使用してコードを翻訳できます
  2. コードを解析し、解析ツリーを取得します。

後者は、「コンパイラ」として知られているすべてのプログラムが行うことです。

解析ツリーができたら、それを他の言語に簡単に翻訳できます。これが、式ツリーでできることです。コードはデータとして保存されているので、やりたいことは何でもできますが、おそらく他の言語に翻訳したいと思うでしょう。

現在、LINQ to SQLでは、式ツリーがSQLコマンドに変換されてから、ネットワーク経由でデータベースサーバーに送信されます。私の知る限り、彼らはコードを翻訳するときに本当に特別なことは何もしませんが、できます。たとえば、クエリプロバイダーは、ネットワークの状態に応じて異なるSQLコードを作成できます。


6

IIUC、式ツリーは抽象構文ツリーに似ていますが、式は通常単一の値を生成しますが、ASTはプログラム全体(クラス、パッケージ、関数、ステートメントなど)を表すことができます。

とにかく、式(2 + 3)* 5の場合、ツリーは次のようになります。

    *
   / \ 
  +   5
 / \
2   3

各ノードを再帰的に(ボトムアップで)評価して、ルートノードの値、つまり式の値を取得します。

もちろん、単項(否定)または三項(if-then-else)演算子、および式言語で許可されている場合は関数(n-ary、つまり任意の数の演算)を使用できます。

タイプの評価とタイプ制御の実行は、同様のツリーに対して実行されます。


5

DLR
式ツリーは、動的言語ランタイム(DLR)をサポートするためのC#への追加です。DLRは、変数を宣言する「var」メソッドを提供する役割も果たします。((var objA = new Tree();

DLRの詳細

基本的に、MicrosoftはLISP、SmallTalk、Javascriptなどの動的言語用にCLRを開放したいと考えていました。そのためには、式をその場で解析および評価できる必要がありました。DLRが登場する前はそれは不可能でした。

最初の文に戻ると、式ツリーはC#への追加であり、DLRを使用する機能を開きます。これ以前は、C#ははるかに静的な言語でした。すべての変数型を特定の型として宣言し、すべてのコードをコンパイル時に記述する必要がありました。

データでの使用
式ツリーで、動的コードへの水門が開かれます。

たとえば、不動産サイトを作成しているとします。設計段階では、適用できるすべてのフィルターを知っています。このコードを実装するには、2つの選択肢があります。各データポイントを一連のIf-Thenチェックと比較するループを作成できます。または、動的言語(SQL)でクエリを作成し、それを検索を実行できるプログラム(データベース)に渡すこともできます。

式ツリーを使用すると、プログラム内のコードをその場で変更して検索を実行できるようになりました。具体的には、LINQを介してこれを行うことができます。

(詳細:MSDN:方法:式ツリーを使用して動的クエリを作成する)。

データを超えて
て式ツリーの主な用途は、の管理です。ただし、動的に生成されたコードにも使用できます。したがって、動的に定義される関数(Javascript)が必要な場合は、式ツリーを作成してコンパイルし、結果を評価できます。

もう少し詳しく説明しますが、このサイトの方がはるかに優れています。

コンパイラとしての式ツリー

リストされている例には、変数タイプのジェネリック演算子の作成、ラムダ式の手動ローリング、高性能のシャロークローニング、およびあるオブジェクトから別のオブジェクトへの読み取り/書き込みプロパティの動的コピーが含まれます。

要約
式ツリーは、実行時にコンパイルおよび評価されるコードの表現です。それらは動的型を可能にし、データ操作と動的計画法に役立ちます。


ええ、私はゲームに遅れていることを知っていますが、私はそれを自分で理解する方法としてこの答えを書きたかったのです。(この質問は私のインターネット検索で高くなりました。)
リチャード

良くやった。それは良い答えです。
リッチブライアント

5
「var」キーワードはDLRとは何の関係もありません。あなたはそれを「ダイナミック」と混同しています。
Yarik 2016

これは、ここでのvarに関する良い小さな答えであり、Yarikが正しいことを示しています。しかし、残りの答えに感謝します。 quora.com/…–
johnny

1
これはすべて間違っています。varはコンパイル時の構文糖衣構文です。式ツリー、DLR、またはランタイムとは何の関係もありません。var i = 0を作成したかのようにコンパイルされるint i = 0ためvar、コンパイル時に不明な型を表すために使用することはできません。式ツリーは「DLRをサポートするための追加」ではなく、LINQを可能にするために.NET3.5で導入されました。一方、DLRは.NET 4.0で導入され、動的言語(IronRubyなど)とdynamicキーワードを使用できるようになりました。式ツリーは、実際には相互運用を提供するためにDLRによって使用されますが、その逆ではありません。
ŞafakGUR

-3

参照している式ツリーは式評価ツリーですか?

はいの場合、それはパーサーによって構築されたツリーです。パーサーは、レクサー/トークナイザーを使用して、プログラムからトークンを識別しました。パーサーは、トークンからバイナリツリーを構築します。

詳細な説明はこちら


OPが参照する式ツリーが解析ツリーと同様に機能し、基本的な概念が同じであることは事実ですが、実行時にコードを使用して動的に実行されますが、Roslynコンパイラの導入に注意してください。完全に削除されない場合、2つの間の分割は本当にぼやけました。
yoel halb 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.