2013-11-09 25 views
2

我能做些什么来获得以下工作?fsharp报价Expr list - > Expr处理

我需要一个接受Expr列表并返回一个Expr(Expr列表 - > Epxr)的函数。

type DataObject() = 
    let data = System.Collections.Generic.Dictionary<int, obj>() 
    member this.AddValue propertyIndex value = data.Add(propertyIndex, value) 
    member this.GetValue propertyIndex = 
     match data.TryGetValue propertyIndex with 
     | (true, value) -> value 
     | (false, _) -> box "property not found" 

... 
(fun args -> 
    <@@ 
     let data = new DataObject(values) 
     args |> List.iteri (fun i arg -> data.AddValue i <@@ (%%arg) : string @@>) 
     data 
    @@>) 

我所做的数据对象类型添加在ARGS的值,但不知何故,我不能设法得到的代码通过不同的ARGS迭代(参数[0] .. ARGS。[1])。我得到的消息是:

变量'arg'绑定在引号中,但用作拼接表达式的一部分。这是不允许的,因为它可能会避开它的范围。

如果我显式地访问args(args。[0],args。[1],...)该解决方案有效,但只要我尝试添加迭代时遇到问题。由于参数列表的长度很灵活,这对我来说不是一个可行的解决方案。

我试过不同的方法,但没有成功。有一些解决方案吗?

[编辑]

添加托马斯的反馈在我的解决方案带来了我:

type DataObject(values: obj []) = 
    let propertyMap = new Map<int, obj>(values |> Seq.mapi (fun i value -> (i, value))) 
    member this.GetValue propertyIndex : obj = 
     match propertyMap.TryFind propertyIndex with 
     | Some(value) -> value 
     | None  -> box "property not found" 

(fun args -> 
    let boxedArgs = 
     args |> List.map (fun arg -> 
      match arg with 
      | Quotations.Patterns.Var var -> 
       if var.Type = typeof<int> then 
        <@@ (box (%%arg: int)) @@> 
       else if var.Type = typeof<string> then 
        <@@ (box (%%arg: string)) @@> 
       else if var.Type = typeof<System.Guid> then 
        <@@ (box (%%arg: System.Guid)) @@> 
       else 
        failwith ("Aha: " + var.Type.ToString()) 
      | _ -> failwith ("Unknown Expr as parameter")) 
     <@@ new DataObject(%%(Expr.NewArray(typeof<obj>, boxedArgs))) @@>)) 

而这个作品!唯一的一点是,我想摆脱if ... else的结构来获得正确的转换。有任何想法吗?

+0

关于编辑,我想你可以用'Expr.Coerce(ARG的typeof )'代替'if' - 这应该给你,代表其他表情'arg的拳击表达'。 –

回答

6

这是一个棘手的问题!要理解为什么您的代码不起作用,则需要两个层面明确区分 - 在一个级别(),您正在撰写的报价,而在另一级别(基地)你正在使用的数据对象中运行一些代码。

您的代码无法正常工作的原因是args是元级别的表达式列表,您正在尝试在基级上迭代它。迭代需要在元级进行。

解决此问题的一种方法是在元级执行迭代并生成一个函数列表,用于为所有参数调用AddValue。然后你可以编写这些功能:

(fun args -> 
    // Given arguments [a0; a1; ...] Generate a list of functions: 
    // 
    // [ fun data -> data.AddValue 0 a0; data ] 
    // [ fun data -> data.AddValue 1 a1; data ... ] 
    args 
    |> List.mapi (fun i arg -> 
     <@ fun (data:DataObject) -> data.AddValue i (%%arg : string); data @>) 

    // Compose all the functions just by calling them - note that the above functions 
    // take DataObject, mutate it and then return it. Given [f0; f1; ...] produce: 
    // 
    // ... (f1 (f0 (new DataObject()))) 
    // 
    |> List.fold (fun dobj fe -> <@ (%fe) (%dobj) @>) <@ new DataObject() @>) 

这很有趣写,但它变得非常复杂。在实践中,你可以通过添加AddValues方法对数据对象(以obj[]),并使用Expr.NewArray以创建单个阵列(以基础级)包含所有参数的值(从元级使事情更容易):

<@@ let d = new DataObject() 
    d.AddValues(%(Expr.NewArray(typeof<obj>, args))) 
    d @@> 
+0

嗨托马斯。感谢您的意见,它让我更进一步。到目前为止,我已经用解决方案编辑了这个问题。你是否也有一些关于if..then构造的想法? (请参阅编辑问题) – BBL