F#引号:变量可能会逃脱作用域 [英] F# quotations: variable may escape scope

查看:97
本文介绍了F#引号:变量可能会逃脱作用域的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这段代码:

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

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

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

In the paper there is explained that the above example will yield the following with the parameters 3 and .<1>. (in MetaOcaml notation):

.<(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.

As you can see the x´s gets replaced by x_1, x_2 etc. because the x would otherwise only refer to the x in the innermost fun.

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

But in F# this isn't allowed. I get the compile-time error: "The variable 'x' is bound in a quotation but is used as part of a spliced expression. This is not permitted since it may escape its scope." So the question is: how can this be changed so it will compile and have the same semantic as the MetaOcaml output?

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

Update to comment: I use the PowerPack to actually evaluating the quotation. But I don't think this have anything to do with it because the error is at compile-time. So far QuotationEvaluation works. However, I do know it may not be the most efficient implementation.

更新为Tomas的答案: 我真的不希望x是全局的或逃避作用域.但我想要的是等同于

Update to Tomas´ answer: I really don't want the x to be global, or to escape scope. But I want is the equivalent to

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是答案.

with quotations. Your answer gives (h 3 <@ 1 @>).Eval() = 4 where the above yields h 3 1 = 7. And here, I want 7 to be the answer.

推荐答案

F#引用语法不支持可能逃脱作用域的变量,因此您需要使用Expr操作显式构造树.这样的事情应该可以解决问题:

F# quotation syntax doesn't support variables that could potentially escape the scope, so you'll need to construct the tree explicitly using the Expr operations. Something like this should do the trick:

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 + ...))这样的表达式.您可以通过编写如下内容来获得相同的结果:

However, this is quite artificial example (to demonstrate variable capturing in MetaOCaml, which isn't available in F#). It just generates expression like (2 + (1 + ...)). You can get the same result by writing something like this:

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

I also came accross this limitation in F# quotations and it would be nice if this was supported. However, I don't think it is such a big problem in practice, because F# quotations are not used for staged meta-programming. They are more useful for analyzing existing F# code than for generating code.

这篇关于F#引号:变量可能会逃脱作用域的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆