3

次の作業を行うにはどうすればよいですか?

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 の値を追加するために DataObject 型を作成しました。私が受け取るメッセージは次のとおりです。

変数 'arg' は引用符で囲まれていますが、結合された式の一部として使用されています。これは、その範囲を逸脱する可能性があるため、許可されていません。

引数に明示的にアクセスすると (args.[0]、args.[1]、...)、解決策は機能しますが、繰り返しを追加しようとするとすぐに問題が発生します。args のリストは長さが柔軟であるため、これは実行可能な解決策ではありません。

さまざまなアプローチを試みましたが、うまくいきませんでした。何か解決策はありますか?

[編集]

私のソリューションにトーマスのフィードバックを追加すると、次のようになります。

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 構造を取り除きたいということです。何か案は?

4

1 に答える 1

7

これはトリッキーな質問です!コードが機能しない理由を理解するには、2 つのレベルを明確に区別する必要があります。一方のレベル ( meta ) では引用を作成し、もう一方のレベル ( base ) ではデータ オブジェクトを使用して何らかのコードを実行しています。

コードが機能しない理由はargs、メタレベルの式のリストであり、ベースレベルでそれを反復しようとしているためです。反復はメタレベルで行う必要があります。

これを解決する 1 つの方法は、メタレベルで反復を行い、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 @@>
于 2013-11-09T19:20:26.297 に答える