簡體   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