6

単純なF#引用があるとしましょう:

タイプPet={名前:文字列}
exprNonGeneric = <@@ System.Func(fun(x:Pet)-> x.Name)@@>

結果の引用は次のようになります。

val exprNonGeneri:Expr =
  NewDelegate(System.Func`2 [[FSI_0152 + Pet、FSI-ASSEMBLY、Version = 0.0.0.0、Culture = neutral、PublicKeyToken = null]、[System.String、mscorlib、Version = 4.0.0.0、Culture = neutral、PublicKeyToken = b77a5c561934e089]]、
             x、PropertyGet(Some(x)、System.String Name、[]))

ここで一般化したいので、タイプ「Pet」とプロパティ「Name」の代わりに、任意のタイプとメソッド/プロパティを定義して使用できます。これが私がやろうとしていることです:

exprGeneric <'T、' R> f = <@@ System.Func <'T、' R>(%f)@@>
exprSpecialized = exprGeneric <Pet、string> <@(fun(x:Pet)-> x.Name)@>

結果の式は次のようになります。

val exprSpecialized:Expr =
  NewDelegate(System.Func`2 [[FSI_0152 + Pet、FSI-ASSEMBLY、Version = 0.0.0.0、Culture = neutral、PublicKeyToken = null]、[System.String、mscorlib、Version = 4.0.0.0、Culture = neutral、PublicKeyToken = b77a5c561934e089]]、
             デリゲート引数、
             アプリケーション(ラムダ(x、
                                  PropertyGet(Some(x)、System.String Name、[]))、
                          デリゲートアーグ))

ご覧のとおり、最初の式と2番目の式の違いは、最初の場合、最上位のNewDelegate式にPropertyGetが含まれ、2番目の式がPropertyGetをApplication/Lambda式でラップすることです。そして、この式を外部コードに渡すと、そのような式の構造を予期せず、失敗します。

したがって、引用の一般化されたバージョンを作成する方法が必要です。したがって、それが特殊化されると、結果の引用は<@@ System.Func(fun(x:Pet)-> x.Name)@@>と完全に一致します。これは可能ですか?または、生成された見積もりにパターンマッチングを手動で適用し、それを必要なものに変換するという選択肢しかありませんか?

更新。回避策として、次のアダプターを実装しました。

convertExpr(expr:Expr)=
    exprと一致する
    | NewDelegate(t、darg、appl)->
        (darg、appl)と一致する
        | (delegateArg、appl)->
            applと一致する
            | Application(l、ldarg)->
                (l、ldarg)と一致する
                | (Lambda(x、f)、delegateArg)->
                    Expr.NewDelegate(t、[x]、f)
                | _-> expr
            | _-> expr
    | _-> expr

それは仕事をします-私は今、式を第1の形式から第2の形式に変換することができます。しかし、式ツリーを横断せずに、これを簡単な方法で実現できるかどうかを調べることに興味があります。

4

1 に答える 1

6

これは不可能だと思います。2番目のケースでは<@ (fun (x : Pet) -> x.Name) @>、ノードを使用して表されるLambda式を、他の式の穴に接続します。コンパイラーはこのプラグインプロセス中に式を単純化しないため、Lambda何をしてもノードは削除されません。

ただし、パターンマッチングの回避策は大幅に簡略化できます。

let convertExpr = function
| NewDelegate(t, [darg], Application(Lambda(x,f), Var(arg))) 
    when darg = arg -> Expr.NewDelegate(t, [x], f)
| expr -> expr

実際、より複雑なバージョンは正しくありません。これはdelegateArg、最も内側のパターンが、外側のパターンから以前にバインドdelegateArgされた識別子の値と一致していないためです。これは、新しくバインドされた識別子であり、たまたまと呼ばれdelegateArgます。実際、外側のdelegateArg識別子にはタイプVar listがあり、内側の識別子にはタイプがありExprます。ただし、コンパイラによって生成される式フォームの範囲が限られていることを考えると、壊れたバージョンは実際には問題にならない可能性があります。

編集

フォローアップの質問については、正しく理解していただければ、思い通りの成果が得られない場合があります。いずれかx => x + 1のタイプを持つと解釈できるC#とは異なり、F#では常にタイプです。タイプの値を取得する場合は、通常、引用符演算子を使用する必要があります。Func<int,int>Expression<Func<int,int>>fun x -> x + 1int->intExpr<int->int>(<@ @>)

ただし、役立つ可能性のある代替案が1つあります。[<ReflectedDefinition>]letバウンド関数の属性を使用して、それらの引用符も使用できるようにすることができます。次に例を示します。

open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.ExprShape
open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Quotations.DerivedPatterns

let rec exprMap (|P|_|) = function
| P(e) -> e
| ShapeVar(v) -> Expr.Var v
| ShapeLambda(v,e) -> Expr.Lambda(v, exprMap (|P|_|) e)
| ShapeCombination(o,l) -> RebuildShapeCombination(o, l |> List.map (exprMap (|P|_|)))


let replaceDefn = function
| Call(None,MethodWithReflectedDefinition(e),args) 
    -> Some(Expr.Applications(e, [args]))
| _ -> None


(* plugs all definitions into an expression *)
let plugDefs e = exprMap replaceDefn e

[<ReflectedDefinition>]
let f x = x + 1

(* inlines f into the quotation since it uses the [<ReflectedDefinition>] attribute *)
let example = plugDefs <@ fun y z -> (f y) - (f 2) @>
于 2010-08-05T16:22:58.787 に答える