2010-08-05 13 views
6

मान लीजिए कि हम एक सरल एफ # उद्धरण करते हैं:जनरेट कर रहा है पैरामिट्रीकृत एफ # कोटेशन

 
type Pet = { Name : string } 
let 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, [])) 

अब मैं इसे सामान्यीकरण करने के लिए चाहते हैं, इसलिए लिखने के बजाय मैं "पालतू पशु "और संपत्ति" नाम "मैं उस पर परिभाषित एक मनमाना प्रकार और विधि/संपत्ति का उपयोग कर सकता हूं। यहाँ है कि मैं क्या करने के लिए कोशिश कर रहा हूँ:

 
let exprGeneric<'T, 'R> f = <@@ System.Func<'T, 'R>(%f) @@> 
let 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]], 
      delegateArg, 
      Application (Lambda (x, 
            PropertyGet (Some (x), System.String Name, [])), 
          delegateArg)) 

आप देख सकते हैं, पहली और दूसरी अभिव्यक्ति के बीच अंतर यह है कि पहले मामले में शीर्ष स्तर NewDelegate अभिव्यक्ति में PropertyGet है, जबकि दूसरी अभिव्यक्ति एक आवेदन/Lambda अभिव्यक्ति में PropertyGet लपेटती है। और जब मैं इस अभिव्यक्ति को बाहरी कोड में पास करता हूं तो यह ऐसी अभिव्यक्ति संरचना की अपेक्षा नहीं करता है और विफल रहता है।

इसलिए मुझे उद्धरण के सामान्यीकृत संस्करण को बनाने के लिए कुछ तरीका चाहिए, इसलिए जब यह विशिष्ट हो जाए, तो परिणामी उद्धरण < @@ System.Func (मजेदार (x: पालतू) -> x.Name का सटीक मिलान है। @@ >। क्या यह संभव है? या यह केवल जेनरेट किए गए उद्धरण के लिए पैटर्न मिलान मैन्युअल रूप से लागू करने का विकल्प है और इसे मेरी आवश्यकता के अनुसार बदल सकता है?

अद्यतन। समाधान के लिए मैं निम्नलिखित एडाप्टर लागू किया:

 
let convertExpr (expr : Expr) = 
    match expr with 
    | NewDelegate(t, darg, appl) -> 
     match (darg, appl) with 
     | (delegateArg, appl) -> 
      match appl with 
      | Application(l, ldarg) -> 
       match (l, ldarg) with 
       | (Lambda(x, f), delegateArg) -> 
        Expr.NewDelegate(t, [x], f) 
       | _ -> expr 
      | _ -> expr 
    | _ -> expr 

यह काम करता है - मैं अब 2 फार्म के लिए 1 से अभिव्यक्ति बदल सकते हैं। लेकिन मुझे यह जानने में दिलचस्पी है कि क्या इसे अभिव्यक्ति के पेड़ों के बिना, सरल तरीके से हासिल किया जा सकता है।

उत्तर

6

मुझे नहीं लगता कि ऐसा करना संभव होगा; दूसरे मामले में, आप <@ (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 पहचानकर्ता के पास Var list है जबकि आंतरिक में Expr टाइप किया गया है! हालांकि, संकलक द्वारा उत्पन्न अभिव्यक्ति रूपों की सीमित सीमा को देखते हुए आपका टूटा संस्करण अभ्यास में समस्याग्रस्त नहीं हो सकता है।

संपादित

अपना फ़ॉलो सवालों के बारे में, अगर मैं तुम्हें सही ढंग से समझ यह संभव नहीं प्राप्त करने के लिए आप क्या चाहते हो सकता है। सी # के विपरीत, जहां x => x + 1 को Func<int,int> या Expression<Func<int,int>> के प्रकार के रूप में व्याख्या किया जा सकता है, F # fun x -> x + 1 में हमेशा int->int टाइप किया जाता है।यदि आप Expr<int->int> प्रकार का मान प्राप्त करना चाहते हैं तो आपको आमतौर पर उद्धरण ऑपरेटर (<@ @>) का उपयोग करने की आवश्यकता है।

हालांकि, एक विकल्प है जो उपयोग में हो सकता है। आप [<ReflectedDefinition>] विशेषता का उपयोग अपने उद्धरणों को भी उपलब्ध कराने के लिए चलो बाध्य कार्यों पर कर सकते हैं। यहां एक उदाहरण दिया गया है:

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) @> 
+0

उत्तर और एक महान सुझाव के लिए धन्यवाद। हालांकि मैं अभी भी संबंधित मुद्दे से फंस गया हूं: क्या एक सामान्य एफ # प्रतिनिधि (जैसे मजेदार एक्स -> x.Name) को एक सामान्य उद्धरण में प्लग करना संभव है जो वास्तविक प्रकार से स्वतंत्र है, जैसे ऊपर उद्धरण # 2। यह मॉकिंग ढांचे के समान है: वे कंक्रीट इंटरफेस को जानने और कंक्रीट प्रकार इंजेक्ट किए बिना कुछ अभिव्यक्तियों को परिभाषित करते हैं। मैं इसे एफ # में प्राप्त करने में सक्षम नहीं प्रतीत होता। –

+0

@ वाजिफ़ - मुझे यकीन नहीं है कि मैं आपका प्रश्न समझता हूं। क्या आप जो कुछ करने की कोशिश कर रहे हैं उस पर आप थोड़ा और विस्तार से जा सकते हैं? – kvb

+0

मुझे एफ # और सी # के बीच इंटरऑप भेजने की जरूरत है। सी # एक निश्चित प्रकार की LINQ अभिव्यक्ति की अपेक्षा करता है। मैं इसे एफ # में हार्ड-कोडेड तरीके से लिख सकता हूं, लेकिन मैं इसके लिए एक सामान्य एफ # रैपर बनाना चाहता हूं। यह रैपर "मजेदार एक्स -> x.Name" जैसे इनपुट लैम्ब्डा के रूप में लेने में सक्षम होना चाहिए और उन्हें कोटेशन में परिवर्तित करना चाहिए। मुझे संदेह है कि सामान्य मामले में यह संभव नहीं है। –

संबंधित मुद्दे