developer tip

인형을위한 표현 트리?

copycodes 2020. 11. 1. 18:15
반응형

인형을위한 표현 트리?


나는이 시나리오에서 더미입니다.

나는 이것이 무엇인지 Google에서 읽으려고했지만 이해하지 못합니다. 누군가 나에게 그들이 무엇이며 왜 유용한 지에 대한 간단한 설명을 줄 수 있습니까?

편집 : .Net의 LINQ 기능에 대해 이야기하고 있습니다.


내가 읽은 표현 트리에 대한 가장 좋은 설명은 Charlie Calvert 의이 기사 입니다.

그것을 요 ​​약하기;

식 트리를 나타내는 것을 당신이하지,하고 싶은 방법 당신이 그것을 할 싶어.

다음과 같은 매우 간단한 람다 식을 고려하십시오.
Func<int, int, int> function = (a, b) => a + b;

이 명령문은 세 섹션으로 구성됩니다.

  • 선언 : Func<int, int, int> function
  • 같음 연산자 : =
  • 람다 식 : (a, b) => a + b;

변수 는 두 숫자를 더하는 방법을 알고function 있는 원시 실행 코드를 가리 킵니다 .

이것이 델리게이트와 표현의 가장 중요한 차이점입니다. 전달한 두 정수로 무엇을하는지 알지 못한 채 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 to SQL은 이러한 요소를 모두 고려하지 않습니다. ,하지만 이론상 원하는 것을 거의하는 것은 무료입니다. 또한이 식 트리를 직접 작성한 사용자 지정 코드에 전달할 수 있으며이를 분석하여 LINQ to SQL에서 생성 된 것과 매우 다른 것으로 변환 할 수 있습니다.

다시 한 번 표현 트리를 통해 우리 가 원하는 것을 표현 (표현?) 수 있음을 알 수 있습니다. 그리고 우리 는 표현이 어떻게 사용 되는지 를 결정 하는 번역자를 사용합니다.


표현식 트리는 실행 코드를 데이터로 변환하는 메커니즘입니다. 표현식 트리를 사용하여 프로그램을 나타내는 데이터 구조를 생성 할 수 있습니다.

C #에서는 Expression<T>클래스 를 사용하여 람다 식으로 생성 된 식 트리로 작업 할 수 있습니다 .


전통적인 프로그램에서는 다음과 같은 코드를 작성합니다.

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

이 코드는 컴파일러가 할당을 생성하도록합니다. 대부분의 경우 그게 당신이 관심을 갖는 전부입니다.

기존 코드를 사용하면 애플리케이션이 소급하여 되돌아 가서 호출 hypotenuse을 수행하여 생성되었는지 확인할 수 없습니다 Math.Sqrt(). 이 정보는 포함 된 내용의 일부가 아닙니다.

이제 다음과 같은 람다 식을 고려하십시오.

Func<int, int, int> 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)

표현식 트리를 사용하면보다 고급 기술과 조작이 가능합니다.


Expression trees are an in-memory representation of an expression, e.g. an arithmetic or boolean expression. For example, consider the arithmetic expression

a + b*2

Since * has a higher operator precedence than +, the expression tree is built like that:

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

Having this tree, it can be evaluated for any values of a and b. Additionally, you can transform it into other expression trees, for example to derive the expression.

When you implement an expression tree, I would suggest to create a base class Expression. Derived from that, the class BinaryExpression would be used for all binary expressions, such as + and * . Then you could introduce a VariableReferenceExpression to reference variables (such as a and b), and another class ConstantExpression (for the 2 from the example).

The expression tree is in many cases built as the result of parsing an input (from the user directly, or from a file). For evaluating the expression tree, I would suggest to use the Visitor pattern.


Short answer: It's nice to be able to write the same kind of LINQ query and point it at any data source. You couldn't have a "Language Integrated" query without it.

Long answer: As you probably know, when you compile source code, you're transforming it from one language to another. Usually from a high level language (C#) to a lower lever on (IL).

There are basically two ways you can do this:

  1. You can translate the code using find and replace
  2. You parse the code and get a parse tree.

The latter is what all the programs we know as 'compilers' do.

Once you have a parse tree you can easily translate it into any other language and this is what expression trees allow us to do. Since the code is stored as data you can do anything you want to it but probably you'll just want to translate it into some other language.

Now, in LINQ to SQL the expression trees get turned into a SQL command and then are sent over the wire to the database server. As far as I know they don't do anything really fancy when translating the code but they could. For instance, the query provider could create different SQL code depending on the network conditions.


IIUC, an expression tree is similar to an Abstract Syntax Tree, but an expression usually yiels a single value, whereas an AST can represent an entire program (with classes, packages, function, statements, etc.)

Anyway, for an the expression (2 + 3) * 5, the tree is:

    *
   / \ 
  +   5
 / \
2   3

Evaluate each node recursively (bottom-up) to get the value at the root node, i.e. the value of the expression.

You can of course have unary (negation) or trinary (if-then-else) operators too, and functions (n-ary, i.e. any number of ops) if your expression language allows it.

Evaluating types and doing type-control is done over similar trees.


The DLR
Expression trees are an addition to C# to support the Dynamic Language Runtime (DLR). The DLR is also what is responsible for giving us the "var" method of declaring variables. (var objA = new Tree();)

More on the DLR.

Essentially, Microsoft wanted to open up the CLR for dynamic languages, such as LISP, SmallTalk, Javascript, etc. To do that, they needed to be able to parse and evaluate expressions on the fly. That was not possible before the DLR came about.

Back to my first sentence, Expression trees are an addition to C# that opens up the ability to use the DLR. Prior to this, C# was a much more static language--all variable types had to be declared as a specific type and all code had to be written at compile time.

Using it with Data
Expression trees opens the flood gates to dynamic code.

Let's say, for example, that you are creating a real-estate site. During the design phase, you know all of the filters that you can apply. To implement this code, you have two choices: you can write a loop that compares each data point to a series of If-Then checks; or you can try to build a query in a dynamic language (SQL) and pass that off to a program that can perform the search for you (the database).

With Expression trees, you can now change the code in your program--on the fly--and perform the search. Specifically, you can do this through LINQ.

(See more: MSDN: How to: Use Expression Trees to Build Dynamic Queries).

Beyond data
The primary uses for Expression Trees are for managing data. However, they can also be used for dynamically generated code. So, if you wanted a function that is defined dynamically (ala Javascript), you can create an Expression Tree, compile it and evaluate the results.

I would go a bit more in depth, but this site does a much better job:

Expression Trees as a Compiler

The examples listed include creating generic operators for variable types, hand-rolling lambda expressions, high performance shallow cloning, and dynamically copying read/write properties from one object to another.

Summary
Expression Trees are representations of code that is compiled and evaluated at runtime. They allow for dynamic types, which is useful for data manipulation and dynamic programming.


Is the expression tree that you are refering is Expression Evaluation tree?

If yes then it is tree constructed by the parser. Parser used the Lexer/Tokenizer to identify the Tokens from the program. Parser constructs the Binary tree from the tokens.

Here is the detailed explanation

참고URL : https://stackoverflow.com/questions/623413/expression-trees-for-dummies

반응형