2010-11-18 39 views
2

我在試圖弄清楚修改報價並評估它們。在這裏,我從基礎開始,嘗試使用Quotations API創建報價。報價綁定OK,但評估時出現錯誤。從PowerPack評估合成報價表達式時出錯

#r @"FSharpPowerPack-2.0.0.0\bin\FSharp.PowerPack.dll" 
#r @"FSharpPowerPack-2.0.0.0\bin\FSharp.PowerPack.Linq.dll" 

open Microsoft.FSharp.Quotations 
open Microsoft.FSharp.Linq.QuotationEvaluation 
open Microsoft.FSharp.Linq 

let hardway = 
    Expr.Let(
     new Var("x", typeof<int>), 
     Expr.Value(10), 
     Expr.GlobalVar("x").Raw) 

hardway.EvalUntyped() 


Binding session to 'FSharp.PowerPack.Linq.dll'... 
System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary. 
    at Microsoft.FSharp.Collections.MapTreeModule.find[TValue,a](IComparer`1 comparer, TValue k, MapTree`2 m) 
    at Microsoft.FSharp.Linq.QuotationEvaluation.ConvExpr(ConvEnv env, FSharpExpr inp) in d:\codeplex\fspowerpack\May2010\src\FSharp.PowerPack.Linq\Linq.fs:line 459 
    at Microsoft.FSharp.Linq.QuotationEvaluation.ConvExpr(ConvEnv env, FSharpExpr inp) in d:\codeplex\fspowerpack\May2010\src\FSharp.PowerPack.Linq\Linq.fs:line 704 
    at Microsoft.FSharp.Linq.QuotationEvaluation.ConvExpr(ConvEnv env, FSharpExpr inp) in d:\codeplex\fspowerpack\May2010\src\FSharp.PowerPack.Linq\Linq.fs:line 677 
    at Microsoft.FSharp.Linq.QuotationEvaluation.CompileImpl[a](a e, Boolean eraseEquality) in d:\codeplex\fspowerpack\May2010\src\FSharp.PowerPack.Linq\Linq.fs:line 837 
    at Microsoft.FSharp.Linq.QuotationEvaluation.Expr.EvalUntyped(FSharpExpr) in d:\codeplex\fspowerpack\May2010\src\FSharp.PowerPack.Linq\Linq.fs:line 854 
    at <StartupCode$FSI_0009>[email protected]() 
Stopped due to error 

回答

3

得到這個工作使用全局變量,你需要把它寫這樣的:

let hardway = 
    Expr.Let( 
     Var.Global("x", typeof<int>), 
     Expr.Value(10), 
     (Expr.GlobalVar<int>("x"))) 

hardway.EvalUntyped() 

Var.GlobalExpr.Global使用一些共享的全局字典F#語句庫使用變量來獲得相同的變量實例而不顯式傳遞Var值(如在Stringer的解決方案中)。然而,我認爲創建Var值只有一次,然後保持對該對象的引用(並在表達式中使用相同的對象)會導致更易讀的代碼,所以我更喜歡Stringer的解決方案。

我的代碼有幾點:

  • 您需要使用Var.Global而不是new Var,因爲第二個選項不變量存儲在全局字典。
  • 您需要在Expr.GlobalVar中明確指定類型 - 如果您不這樣做,F#將使用obj,這是一個不同的變量(它們按名稱類型編入索引)。
+0

太好了,謝謝。你的第二點很重要,因爲如果沒有指定類型,則拋出相同的異常。 – yanta 2010-11-19 08:10:02

2

我不知道如何使用GlobalVar所以我讓別人回答這個問題。下面是在等待一個更好的解決方案解決方法:

let hardway = 
    let v = new Var("x", typeof<int>) 
    Expr.Let(
     v, 
     Expr.Value(10), 
     Expr.Var(v)) 

let res = hardway.EvalUntyped() // res is 10 
+0

很好的解決方法,謝謝。 – yanta 2010-11-19 08:10:52

0

Unquote有基於反射定製的評估引擎,它可以讓你通過傳遞一個變量的環境,而不需要使表達本身的變量綁定部分,以評估合成的報價。所以,你可以做到以下幾點:

open Swensen.Unquote 
open Microsoft.FSharp.Quotations 

let unquoteway = Expr.Var(Var("x", typeof<int>)) 
let environment = Map.ofList [("x", box 10)] 
unquoteway.Eval(environment) 

這很有趣,因爲你通過在環境中是用於整個表達式求值的所有變量綁定和決議非常環境,使變量範圍的規則很榮幸:

let unquoteway = 
    Expr.NewTuple(
     [Expr.Var(new Var("x", typeof<int>)) 
     Expr.Let(new Var("x", typeof<string>), Expr.Value("hello"), Expr.Var(new Var("x", typeof<string>)))]) 

let environment = Map.ofList [("x", box 10)] 
unquoteway.Eval(environment) 

//FSI output: 
val unquoteway : Expr = NewTuple (x, Let (x, Value ("hello"), x)) 
val environment : Map<string,obj> = map [("x", 10)] 
val it : obj = (10, "hello")