Jestem manekinem w tym scenariuszu.
Próbowałem przeczytać w Google, co to jest, ale po prostu nie rozumiem. Czy ktoś może mi w prosty sposób wyjaśnić, czym są i dlaczego są przydatne?
edycja: mówię o funkcji LINQ w .Net.
Jestem manekinem w tym scenariuszu.
Próbowałem przeczytać w Google, co to jest, ale po prostu nie rozumiem. Czy ktoś może mi w prosty sposób wyjaśnić, czym są i dlaczego są przydatne?
edycja: mówię o funkcji LINQ w .Net.
Odpowiedzi:
Najlepszym wyjaśnieniem drzew ekspresji, jakie kiedykolwiek czytałem, jest ten artykuł Charliego Calverta.
Podsumowując;
Drzewo wyrażeń reprezentuje to , co chcesz zrobić, a nie sposób , w jaki chcesz to zrobić.
To jest najważniejsza różnica między delegatami a wyrażeniami. Wołasz
function
(aFunc<int, int, int>
), nigdy nie wiedząc, co zrobi z dwiema podanymi liczbami całkowitymi. Zajmuje dwa i zwraca jeden, czyli najwięcej, co Twój kod może wiedzieć.Teraz, w przeciwieństwie do delegatów, Twój kod może wiedzieć, do czego służy drzewo wyrażeń.
Oznacza to, że nie możesz po prostu wywołać drzewa wyrażeń, tak jak można wywołać delegata, ale możesz je przeanalizować. Co więc może zrozumieć Twój kod, analizując zmienną
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.
Tutaj widzimy, że jest wiele informacji, które możemy uzyskać z wyrażenia.
Ale po co nam to?
Po raz kolejny widzimy, że drzewa wyrażeń pozwalają nam przedstawić (wyrazić?) To , co chcemy zrobić. Korzystamy z usług tłumaczy, którzy decydują o tym, jak używane są nasze wyrażenia.
źródło
Drzewo wyrażeń to mechanizm służący do tłumaczenia kodu wykonywalnego na dane. Korzystając z drzewa wyrażeń, możesz utworzyć strukturę danych, która reprezentuje Twój program.
W języku C # można pracować z drzewem wyrażeń utworzonym przez wyrażenia lambda przy użyciu
Expression<T>
klasy.W tradycyjnym programie piszesz taki kod:
double hypotenuse = Math.Sqrt(a*a + b*b);
Ten kod powoduje, że kompilator generuje przypisanie i to wszystko. W większości przypadków to wszystko, na czym Ci zależy.
W przypadku tradycyjnego kodu aplikacja nie może cofnąć się wstecz i sprawdzić
hypotenuse
, czy została utworzona przez wykonanieMath.Sqrt()
wywołania; ta informacja po prostu nie jest częścią tego, co jest zawarte.Rozważmy teraz wyrażenie lambda, takie jak następujące:
Func<int, int, double> hypotenuse = (a, b) => Math.Sqrt(a*a + b*b);
To jest trochę inne niż wcześniej. Teraz
hypotenuse
jest właściwie odniesieniem do bloku kodu wykonywalnego . Jeśli zadzwoniszhypotenuse(3, 4);
otrzymasz
5
zwróconą wartość .Możemy użyć drzew wyrażeń do zbadania bloku kodu wykonywalnego, który został utworzony. Spróbuj tego zamiast tego:
Expression<Func<int, int, int>> addTwoNumbersExpression = (x, y) => x + y; BinaryExpression body = (BinaryExpression) addTwoNumbersExpression.Body; Console.WriteLine(body);
To daje:
Bardziej zaawansowane techniki i manipulacje są możliwe dzięki drzewom ekspresji.
źródło
Drzewa wyrażeń są reprezentacją wyrażenia w pamięci, np. Wyrażeniem arytmetycznym lub logicznym. Weźmy na przykład pod uwagę wyrażenie arytmetyczne
a + b*2
Ponieważ * ma wyższy priorytet operatorów niż +, drzewo wyrażeń jest zbudowane w następujący sposób:
[+] / \ a [*] / \ b 2
Mając to drzewo, można je ocenić dla dowolnych wartości a i b. Dodatkowo możesz przekształcić go w inne drzewa wyrażeń, na przykład w celu uzyskania wyrażenia.
Kiedy implementujesz drzewo wyrażeń, sugerowałbym utworzenie Expression klasy bazowej . Na tej podstawie klasa BinaryExpression byłaby używana dla wszystkich wyrażeń binarnych, takich jak + i *. Następnie można wprowadzić VariableReferenceExpression do zmiennych referencyjnych (takich jak a i b) oraz inną klasę ConstantExpression (dla 2 z przykładu).
Drzewo wyrażeń jest w wielu przypadkach budowane w wyniku analizy danych wejściowych (bezpośrednio od użytkownika lub z pliku). Do oceny drzewa wyrażeń sugerowałbym użycie wzorca Visitor .
źródło
Krótka odpowiedź: Fajnie jest móc napisać tego samego rodzaju zapytania LINQ i skierować je do dowolnego źródła danych. Bez niego nie byłoby zapytania „Language Integrated”.
Długa odpowiedź: Jak zapewne wiesz, kompilując kod źródłowy, przekształcasz go z jednego języka na inny. Zwykle od języka wysokiego poziomu (C #) do niższej dźwigni (IL).
Zasadniczo można to zrobić na dwa sposoby:
To ostatnie jest tym, co robią wszystkie programy, które znamy jako „kompilatory”.
Gdy masz już drzewo parsowania, możesz je łatwo przetłumaczyć na dowolny inny język i na to pozwalają drzewa wyrażeń. Ponieważ kod jest przechowywany jako dane, możesz z nim zrobić wszystko, co chcesz, ale prawdopodobnie będziesz chciał po prostu przetłumaczyć go na inny język.
Teraz w LINQ to SQL drzewa wyrażeń są zamieniane w polecenie SQL, a następnie przesyłane za pośrednictwem połączenia kablowego do serwera bazy danych. O ile wiem, tłumacząc kod nie robią nic wymyślnego, ale mogli . Na przykład dostawca zapytań może utworzyć inny kod SQL w zależności od warunków sieciowych.
źródło
IIUC, drzewo wyrażeń jest podobne do abstrakcyjnego drzewa składni, ale wyrażenie zwykle wyświetla pojedynczą wartość, podczas gdy AST może reprezentować cały program (z klasami, pakietami, funkcją, instrukcjami itp.)
W każdym razie dla wyrażenia (2 + 3) * 5 drzewo to:
* / \ + 5 / \ 2 3
Oceń każdy węzeł rekurencyjnie (od dołu do góry), aby uzyskać wartość w węźle głównym, tj. Wartość wyrażenia.
Możesz oczywiście mieć operatory jednoargumentowe (negacja) lub trójargumentowe (jeśli-to-jeszcze) oraz funkcje (n-ary, tj. Dowolna liczba operacji), jeśli pozwala na to język wyrażeń.
Ocena typów i kontrola typów odbywa się na podobnych drzewach.
źródło
Drzewa wyrażeń DLR są dodatkiem do języka C # w celu obsługi środowiska uruchomieniowego języka dynamicznego (DLR). DLR jest również tym, co jest odpowiedzialne za udostępnianie nam metody deklarowania zmiennych „var”. (
var objA = new Tree();
)Więcej o DLR .
Zasadniczo Microsoft chciał otworzyć środowisko CLR dla języków dynamicznych, takich jak LISP, SmallTalk, Javascript itp. Aby to zrobić, musieli mieć możliwość analizowania i oceniania wyrażeń w locie. Nie było to możliwe przed pojawieniem się DLR.
Wracając do mojego pierwszego zdania, drzewa wyrażeń są dodatkiem do C #, który otwiera możliwość korzystania z DLR. Wcześniej C # był językiem znacznie bardziej statycznym - wszystkie typy zmiennych musiały być zadeklarowane jako określony typ, a cały kod musiał być napisany w czasie kompilacji.
Używanie go z
drzewami wyrażeń danych otwiera bramy powodziowe dla kodu dynamicznego.
Załóżmy na przykład, że tworzysz witrynę z nieruchomościami. Na etapie projektowania znasz wszystkie filtry, które możesz zastosować. Aby zaimplementować ten kod, masz dwie możliwości: możesz napisać pętlę porównującą każdy punkt danych z serią testów Jeśli-To; lub możesz spróbować zbudować zapytanie w języku dynamicznym (SQL) i przekazać je do programu, który może przeprowadzić wyszukiwanie za Ciebie (baza danych).
Dzięki drzewom wyrażeń możesz teraz zmieniać kod w swoim programie - w locie - i przeprowadzać wyszukiwanie. W szczególności możesz to zrobić za pośrednictwem LINQ.
(Zobacz więcej: MSDN : instrukcje: używanie drzew wyrażeń do tworzenia zapytań dynamicznych ).
Więcej niż dane
Podstawowe zastosowania drzew wyrażeń to zarządzanie danymi. Można ich jednak używać również w przypadku kodu generowanego dynamicznie. Tak więc, jeśli potrzebujesz funkcji, która jest definiowana dynamicznie (ala Javascript), możesz utworzyć drzewo wyrażeń, skompilować je i ocenić wyniki.
Poszedłbym nieco bardziej dogłębnie, ale ta strona radzi sobie znacznie lepiej:
Drzewa wyrażeń jako kompilator
Wymienione przykłady obejmują tworzenie operatorów ogólnych dla typów zmiennych, ręcznie przewijane wyrażenia lambda, wydajne płytkie klonowanie i dynamiczne kopiowanie właściwości odczytu / zapisu z jednego obiektu do drugiego.
Drzewa wyrażeń podsumowania to reprezentacje kodu, który jest kompilowany i oceniany w czasie wykonywania. Pozwalają na typy dynamiczne, co jest przydatne przy manipulowaniu danymi i programowaniu dynamicznym.
źródło
var
jest cukrem składniowym czasu kompilacji - nie ma nic wspólnego z drzewami wyrażeń, DLR czy środowiskiem wykonawczym.var i = 0
jest kompilowany tak, jakbyś pisałint i = 0
, więc nie możesz użyć govar
do reprezentowania typu, który nie jest znany w czasie kompilacji. Drzewa wyrażeń nie są „dodatkiem do obsługi DLR”, zostały wprowadzone w .NET 3.5, aby umożliwić LINQ. Z drugiej strony DLR został wprowadzony w .NET 4.0, aby umożliwić języki dynamiczne (takie jak IronRuby) idynamic
słowo kluczowe. Drzewa wyrażeń są w rzeczywistości używane przez DLR do zapewniania współdziałania, a nie na odwrót.Czy drzewo wyrażeń, do którego się odwołujesz, jest drzewem oceny wyrażeń?
Jeśli tak, to jest to drzewo zbudowane przez parser. Parser użył Lexera / Tokenizera do identyfikacji tokenów z programu. Parser konstruuje drzewo binarne z tokenów.
Oto szczegółowe wyjaśnienie
źródło