我在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 @>
生成的)
有没有简单的方法来展平第一个表达式,以使它看起来更像第二个?
答案 0 :(得分:4)
您可以编写代码,使其不返回引用函数,而是返回一个函数,在给定输入时生成引号:
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))))