我有一个代码:
let rec h n z = if n = 0 then z
else <@ (fun x -> %(h (n - 1) <@ x + %z @>)) n @>从MetaOcaml示例转换而成的http://www.cs.rice.edu/~taha/publications/journal/dspg04a.pdf示例
在本文中,说明了上面的示例将得到参数3和.<1>. (以MetaOcaml符号表示)的如下结果:
.<(fun x_1 -> (fun x_2 -> (fun x_3 -> x_3 + (x_2 + (x_1 + 1))) 1) 2) 3>.如您所见,x被x_1、x_2等所取代,因为否则x只会引用最内部的fun中的x。
但在F#,这是不允许的。我得到编译时错误:“变量'x‘被绑定在一个引号中,但是用作一个拼接表达式的一部分。这是不允许的,因为它可能会脱离它的作用域。”因此,问题是:如何才能改变这种情况,使其能够编译并具有与MetaOcaml输出相同的语义?
更新到注释:我使用PowerPack实际评估报价。但我不认为这与它有任何关系,因为错误发生在编译时。到目前为止,QuotationEvaluation还能工作。然而,我知道这可能不是最有效的实施。
更新到Tomas的答案:我真的不希望x是全局的,或者逃避作用域。但我想要的是相当于
let rec h n z = if n = 0 then z
else (fun x -> (h (n - 1) (x + z))) n加上引文。您的答案给出了(h 3 <@ 1 @>).Eval() = 4,上面的结果是h 3 1 = 7。在这里,我希望7是答案。
发布于 2011-06-20 16:29:49
F#引号语法不支持可能逃避作用域的变量,因此需要使用Expr操作显式地构造树。像这样的事情应该能起作用:
open Microsoft.FSharp.Quotations
let rec h n (z:Expr<int>) =
if n = 0 then z
else
let v = new Var("x", typeof<int>)
let ve = Expr.Var(v)
Expr.Cast<int>
(Expr.Application( Expr.Lambda(v, h (n - 1) <@ %%ve + %z @>),
Expr.Value(n)))但是,这是一个非常人工的例子(演示在MetaOCaml中捕获变量,这在F#中是不可用的)。它只生成类似于(2 + (1 + ...))的表达式。通过编写这样的东西,您可以得到相同的结果:
let rec h n (z:Expr<int>) =
if n = 0 then z
else h (n - 1) <@ n + %z @>甚至更好:
[ 1 .. 4 ] |> List.fold (fun st n -> <@ n + %st @>) <@ 0 @>我也克服了F#报价中的这一限制,如果这得到支持,那就太好了。但是,我认为这在实践中并不是一个大问题,因为F#引用并不用于分阶段的元编程。它们在分析现有F#代码时比生成代码更有用。
https://stackoverflow.com/questions/6414185
复制相似问题