F # — Извлечь параметр из выражения

#f#

#f#

Вопрос:

Есть ли способ извлечь параметр из Expr ?

Пример:

 let hasStringOption (e:Expr<string option>) =
    let myOption : string option = ..some code to get the string option from e
  

Как бы я получил string option внутри Expr и назначил его myOption ?

Комментарии:

1. Это зависит от того, если у вас есть <@ Some (f x) @> , хотели бы вы оценить вызов функции или нет? Если это так, используйте Eval, как упоминал wmeyer; В противном случае, решение Стивена — это правильный путь.

Ответ №1:

В вашем примере string option представляет возвращаемый тип выражения, поэтому само выражение может быть сколь угодно сложным и требует какой-либо стратегии вычисления, подобной PowerPack, как показал @wmeyer.

Но если у вас действительно есть string option выражение, вы можете использовать стандартные шаблоны цитирования библиотеки active и отражение для реализации вашей собственной стратегии оценки (общий вариант показан здесь):

 module P = Microsoft.FSharp.Quotations.Patterns

let extract (expr:Expr<'a option>) =
    match expr with
    | P.NewUnionCase (uci, args) ->
        if uci.Name = "Some" then
            match args.Head with
            | P.Value(value, ty) -> Some(value :?> 'a)
        else
            None:('a option)

let example1 = extract <@ None:int option @>
let example2 = extract <@ Some("hello world") @>
  

и действительно, такой подход может быть частью вашей собственной стратегии рекурсивного вычисления произвольных выражений с использованием отражения и активных шаблонов, а не медленной и ограниченной промежуточной стратегии LINQ PowerPack.

Комментарии:

1. спасибо за аккуратную функцию 🙂 — Но.. почему вы используете None:('a option) ? Что это: all about?

2.Привет, @ebb, всегда пожалуйста. :('a option) Часть None:('a option) — это просто аннотация типа, которую я изначально использовал, чтобы помочь компилятору определить возвращаемый тип extract . Но это оказывается ненужным, поскольку Some(value :?> 'a) ветви достаточно для подсказки (сначала я написал ветку None). С другой стороны, :int option это необходимо для того, чтобы помочь компилятору определить окончательный тип example1 . Но это также можно было бы сделать, поместив аннотацию типа в сам example1: let example1:int option = extract <@ None @>

Ответ №2:

С помощью FSharp.PowerPack.Linq.dll referenced вы можете сделать:

   open Microsoft.FSharp.Quotations 
  open Microsoft.FSharp.Linq.QuotationEvaluation


  let hasStringOption (e:Expr<string option>) =
      let myOption : string option = e.Eval()
      myOption.IsSome

  printfn "%A" (hasStringOption <@ Some "hello" @>)
  printfn "%A" (hasStringOption <@ None @>)
  

Однако, как сообщается, это довольно медленно и использует выражения LINQ в качестве промежуточного шага.

Ответ №3:

К сожалению (или нет), вы не можете оценить цитаты из F #. F # PowerPack имеет ограниченную возможность компиляции цитат к выражениям LINQ (которые могут быть вычислены).

Комментарии:

1. Я внедрил пользовательский оценщик котировок на основе отражения, который теперь является частью Unquote, code.google.com/p/unquote , начиная с версии 2.0.0. Я измерил, что он работает в 50 раз быстрее, чем вычислитель PowerPack, и он также поддерживает гораздо больше выражений.