繁体   English   中英

F#引号:变量可能会超出范围

[英]F# quotations: variable may escape scope

我有这段代码:

let rec h n z = if n = 0 then z
                else <@ (fun x -> %(h (n - 1) <@ x + %z @>)) n @>

http://www.cs.rice.edu/~taha/publications/journal/dspg04a.pdf中的MetaOcaml示例转换而来

在本文中,解释了上面的示例将使用参数3.<1>.得出以下结果.<1>. (以MetaOcaml表示法):

.<(fun x_1 -> (fun x_2 -> (fun x_3 -> x_3 + (x_2 + (x_1 + 1))) 1) 2) 3>.

如您所见, xx_1x_2等替换,因为x否则只会在最内层的fun引用x

但是在F#中这是不允许的。 我收到编译时错误:“变量'x'用引号引起来,但被用作拼接表达式的一部分。这是不允许的,因为它可能逃避其范围。” 所以问题是:如何更改它,使其可以编译并具有与MetaOcaml输出相同的语义?

更新以评论:我使用PowerPack实际评估报价。 但是我认为这与它无关,因为错误发生在编译时。 到目前为止,报价评估工作正常。 但是,我知道这可能不是最有效的实现。

更新为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是答案。

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#代码比生成代码更有用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM