2

我试图弄清楚修改报价,然后评估它们。在这里,我从基础开始,只是尝试使用 Quotations api 创建报价。报价绑定OK,但在评估时出现错误。

#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>.$FSI_0009.main@()
Stopped due to error
4

3 回答 3

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这是一个不同的变量(它们按名称类型索引)。
于 2010-11-18T15:17:25.570 回答
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
于 2010-11-18T11:07:56.360 回答
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")
于 2011-07-08T19:17:54.787 回答