Вы можете использовать PowerPack-х Eval
оценить только аргументы в Call
выражения:
match e with
| Call(_,mi,[arg1;arg2]) ->
let arg1Value, arg2Value = arg1.Eval(), arg2.Eval()
...
и аналогична для Lambda
выражений и т.д. Заметил это освобождает вас от перечисляющих перестановок Value
, Property
и других выражений аргументов ,
Update
Так как вы хотите, чтобы избежать использования Eval
(по уважительной причине, если вы реализуете производительности сознательное приложение), вам нужно реализовать свою собственную функцию Eval с помощью отражения (который до сих пор не осветления быстрый, но должен быть быстрее, чем у PowerPack's Eval
, который включает в себя промежуточный перевод котировок F # на выражения Linq). Вы можете начать, поддерживая базовый набор выражений и расширяясь оттуда по мере необходимости. Рекурсия является ключевой, следующим могут помочь вам начать работу:
open Microsoft.FSharp.Quotations
open System.Reflection
let rec eval expr =
match expr with
| Patterns.Value(value,_) -> value //value
| Patterns.PropertyGet(Some(instance), pi, args) -> //instance property get
pi.GetValue(eval instance, evalAll args) //notice recursive eval of instance expression and arg expressions
| Patterns.PropertyGet(None, pi, args) -> //static property get
pi.GetValue(null, evalAll args)
| Patterns.Call(Some(instance), mi, args) -> //instance call
mi.Invoke(eval instance, evalAll args)
| Patterns.Call(None, mi, args) -> //static call
mi.Invoke(null, evalAll args)
| _ -> failwith "invalid expression"
and evalAll exprs =
exprs |> Seq.map eval |> Seq.toArray
А затем оборачивать это в Активном шаблоне улучшит синтаксис:
let (|Eval|) expr =
eval expr
match e with
| Patterns.Call(_, mi, [Eval(arg1Value); Eval(arg2Value)]) -> ...
Update 2
OK, это нить заставила меня мотивироваться, чтобы попытаться реализовать надежное решение на основе отражения, и я сделал это с хорошими результатами, которые теперь являются частью Unquote с версии 2.0.0.
Это оказалось не таким сложным, как я думал, в настоящее время я поддерживаю все цитаты за исключением для AddressGet, AddressSet и NewDelegate. Это уже лучше, чем оценка PowerPack, которая не поддерживает PropertySet, VarSet, FieldSet, WhileLoop, ForIntegerRangeLoop и Quote, например.
Некоторые примечательные детали реализации с VarSet и VarGet, где мне нужно передать список поиска имени/переменной окружения для каждого рекурсивного вызова. Это действительно отличный пример красоты функционального программирования с неизменяемыми структурами данных.
Также следует обратить особое внимание на проблемы, связанные с исключениями: чередование объектов TargetInvokationException, создаваемых отражением, когда оно улавливает исключения, исходящие из методов, которые он вызывает (это очень важно для правильной оценки TryWith, а также для лучшей обработки пользователем исключения, которые вылетают из оценки котировок.
Возможно, самой «сложной» деталью реализации или действительно самой изнурительной была необходимость реализовать все основные операторы (ну, как я мог бы обнаружить: числовые и операторы преобразования, проверенные версии), так как большинство из них не получают динамических реализаций в библиотеке F # (они реализуются с использованием тестов статического типа без отложенных динамических реализаций), bu t также означает серьезное увеличение производительности при использовании этих функций.
Неформальный бенчмаркинг Я наблюдаю увеличение производительности до 50 раз по сравнению с PowerPack (не скомпилированным) eval.
Я также уверен, что мое решение на основе отражения будет меньше подвержено ошибкам, чем PowerPack, просто потому, что оно менее сложно, чем подход PowerPack (не говоря уже о том, что я поддержал его примерно с 150 модульными тестами, должным образом укрепленными Unquotes дополнительных 200+ модульных тестов, которые теперь обусловлены этой реализацией eval).
Если вы хотите заглянуть в исходный код, основными модулями являются Evaluation.fs и DynamicOperators.fs (я заблокировал ссылки в ревизии 257). Не стесняйтесь брать и использовать исходный код для своих целей, он лицензируется в соответствии с Apache License 2.0! Или вы можете подождать неделю или около того, когда я выпущу Unquote 2.0.0, который будет публиковаться операторами оценки и расширениями.
@Stephen, да, это сработает .. но мне все еще интересно, повлияет ли это на производительность моего приложения с тех пор Я слышал немало неприятностей о функции '.Eval()' и ее производительности. – ebb
Я использую 'Eval' много в Unquote и да, это не так быстро, как могло бы быть, но это, безусловно, достаточно для работы Unquote. Но для вашего проекта, перевод выражений F # в запросы базы данных, я был бы осторожен. –
@ebb - Я обновил свой ответ с помощью основанной на отражении функции eval, которая должна работать лучше, чем Power Evolution, и может быть достаточной для ваших нужд. –