2010-11-18 37 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")