构建平面引用 lambda

Building flat quoted lambda

我在 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 @>生成)

是否有任何简单的方法可以使第一个表达式变平,使其看起来更像第二个?

您可以编写代码,使其不 return 引用函数 而是 return 一个 函数 在给定输入时生成报价:

let vals = [|1..3|]

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

折叠:

  • 初始值是returns 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))))