Построение плоской котируемой лямбды

Я использую кавычки в F# для создания функции, которая проверяет, удовлетворяет ли один ввод любому из множества случаев. То есть функция, тело которой похоже на ... || ... || ..., где количество || определяется во время выполнения. Несколько упрощенно, что у меня сейчас есть

let vals = [|1..3|]
let currentfilter =
    vals
    |> Array.fold (fun acc i ->
        <@ fun j -> (%acc) j || j = i @>)
        <@ fun _ -> false @>

который генерирует дерево

val currentfilter : Expr<(int -> bool)> =
  Lambda (j,
        IfThenElse (Application (Lambda (j,
                                         IfThenElse (Application (Lambda (j,
                                                                          IfThenElse (Application (Lambda (_arg1,
                                                                                                           Value (false)),
                                                                                                   j),
                                                                                      Value (true),
                                                                                      Call (None,
                                                                                            op_Equality,
                                                                                            [j,
                                                                                             Value (1)]))),
                                                                  j),
                                                     Value (true),
                                                     Call (None, op_Equality,
                                                           [j, Value (2)]))), j),
                    Value (true), Call (None, op_Equality, [j, Value (3)])))

Оптимально то, что я хочу создать, больше похоже на

  Lambda (j,
        IfThenElse (IfThenElse (Call (None, op_Equality, [j, Value (1)]),
                                Value (true),
                                Call (None, op_Equality, [j, Value (2)])),
                    Value (true), Call (None, op_Equality, [j, Value (3)])))

(Это было создано <@ fun j -> j = 1 || j = 2 || j = 3 @>)

Есть ли простой способ сгладить первое выражение, чтобы оно больше походило на второе?


person torbonde    schedule 15.10.2015    source источник


Ответы (1)


Вы можете написать код так, чтобы он не возвращал функцию в кавычках, а вместо этого возвращал функцию, которая генерирует кавычки при вводе данных:

let vals = [|1..3|]

let currentfilter =
    vals |> Array.fold (fun acc i ->
        fun j -> <@ %(acc j) || %j = i @>)
        (fun _ -> <@ false @>)

В сгибе:

  • Начальное значение — это функция, которая возвращает false выражение
  • Агрегация объединяет сгенерированное на данный момент выражение с выражением, которое сравнивает входные данные (указанные в виде кавычек) со значением i.

Теперь, чтобы создать полную функцию в кавычках, мы хотели бы написать что-то вроде этого:

<@ fun j -> %(currentfilter <@ j @>) @>

К сожалению, это не работает — потому что компилятор F# здесь немного строг и не позволяет нам писать код, в котором переменная j могла бы выйти из области видимости (вполне разумно, но жаль).

Итак, вместо этого вы можете написать это, построив цитату вручную:

open Microsoft.FSharp.Quotations

let v = Var.Global("j", typeof<int>)
Expr.Lambda(v, currentfilter (Expr.Cast(Expr.Var(v))))
person Tomas Petricek    schedule 15.10.2015
comment
Блестящий. Спасибо. Это делает именно то, что я просил. - person torbonde; 15.10.2015