Вы можете использовать PowerPack Eval
для оценки только аргументов выражения Call
:
match e with
| Call(_,mi,[arg1;arg2]) ->
let arg1Value, arg2Value = arg1.Eval(), arg2.Eval()
...
И аналогично для Lambda
выражений и т. Д. Заметим, что это освобождает вас от перечисления перестановок Value
, Property
и другие выражения аргументов.
Обновление
Поскольку вы хотите избегать использования Eval
(по уважительной причине, если вы реализуете производительностьсознательное приложение), вам нужно будет реализовать свою собственную функцию eval с использованием отражения (которая все еще не очень быстрая, но должна быть быстрее, чем Eval
PowerPack, которая включает в себя промежуточный перевод цитат 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)]) -> ...
Обновление 2
ОК, этот поток побудил меня попробовать и реализовать надежное решение, основанное на отражениях, и я добился хороших результатов, которые теперь являются частью Unquote начиная с версии 2.0.0.
Оказалось, что это не так сложно, как я думал, в настоящее время я поддерживаю все выражения кавычек , за исключением для AddressGet, AddressSet и NewDelegate.Это уже лучше, чем eval PowerPack, который не поддерживает PropertySet, VarSet, FieldSet, WhileLoop, ForIntegerRangeLoop и Quote, например.
Некоторые заслуживающие внимания подробности реализации касаются VarSet и VarGet, где мне нужно обойтисьимя среды / список поиска переменных для каждого рекурсивного вызова.Это действительно прекрасный пример красоты функционального программирования с неизменяемыми структурами данных.
Также следует отметить особую осторожность, связанную с проблемами, связанными с исключениями: удаление исключений TargetInvokationException, возникающих при отражении, когда оно ловит исключения, поступающие от вызываемых методов (это очень важно для правильной обработки оценки TryWith, а также для лучшего пользователя).обработка исключений, которые вытекают из оценки предложения.
Пожалуй, самой «сложной» деталью реализации или действительно самой изнурительной была необходимость реализовать все основные операторы (ну, как большинство из них я мог обнаружить: числовые операторы и операторы преобразования, также проверенные версии), поскольку большинство из них не имеют динамических реализаций в библиотеке F # (они реализованы с использованием статических тестов типов без резервных динамических реализаций), но также означают серьезное увеличение производительности при использовании этихфункций.
Некоторый неформальный бенчмаркинг Я наблюдаю увеличение производительности до 50 раз по сравнению с PowerPack (не скомпилированным) eval.
Ятакже уверен, что мое решение на основе отражений будет менее подвержено ошибкам, чем PowerPack, просто потому, что оно менее сложно, чем подход PowerPack (не говоря уже о том, что я подкрепил его примерно 150 модульными тестами, должным образом усиленными дополнительными модулями Unquotes более 200+)тесты, которые теперь управляются этой eval-реализацией).
Если вы хотите просмотреть исходный код, основными модулями являются Evaluation.fs и DynamicOperators.fs (Я заблокировал ссылки в ревизии 257).Не стесняйтесь захватывать и использовать исходный код для своих собственных целей, он лицензирован под Apache License 2.0!Или вы можете подождать неделю или около того, когда я выпущу Unquote 2.0.0, который будет публично включать в себя операторы оценки и расширения.