Expression.Quote () क्या करता है जो Expression.Constant () पहले से नहीं कर सकता है?


97

नोट: मैं पहले के प्रश्न से अवगत हूँ " LINQ की अभिव्यक्ति का उद्देश्य क्या है। विधि को बताएं? " , लेकिन अगर आप पढ़ेंगे तो आप देखेंगे कि यह मेरे सवाल का जवाब नहीं देता है।

मैं समझता हूं कि कहा गया उद्देश्य क्या Expression.Quote()है। हालांकि, Expression.Constant()एक ही उद्देश्य के लिए इस्तेमाल किया जा सकता है (सभी उद्देश्यों के अतिरिक्त जो Expression.Constant()पहले से ही उपयोग किया जाता है)। इसलिए, मुझे समझ में नहीं आता कि क्यों Expression.Quote()आवश्यक है।

इसे प्रदर्शित करने के लिए, मैंने एक त्वरित उदाहरण लिखा है, जहां कोई भी कस्टमाइज Quote(विस्मयादिबोधक बिंदुओं के साथ चिह्नित रेखा देखें) का उपयोग करेगा , लेकिन मैंने Constantइसके बजाय उपयोग किया और यह समान रूप से अच्छी तरह से काम करता है:

string[] array = { "one", "two", "three" };

// This example constructs an expression tree equivalent to the lambda:
// str => str.AsQueryable().Any(ch => ch == 'e')

Expression<Func<char, bool>> innerLambda = ch => ch == 'e';

var str = Expression.Parameter(typeof(string), "str");
var expr =
    Expression.Lambda<Func<string, bool>>(
        Expression.Call(typeof(Queryable), "Any", new Type[] { typeof(char) },
            Expression.Call(typeof(Queryable), "AsQueryable",
                            new Type[] { typeof(char) }, str),
            // !!!
            Expression.Constant(innerLambda)    // <--- !!!
        ),
        str
    );

// Works like a charm (prints one and three)
foreach (var str in array.AsQueryable().Where(expr))
    Console.WriteLine(str);

expr.ToString()दोनों के लिए समान आउटपुट है, चाहे मैं उपयोग करूंConstant या नहीं Quote)।

उपरोक्त टिप्पणियों को देखते हुए, यह प्रतीत होता है कि Expression.Quote()बेमानी है। C # कंपाइलर नेस्टेड लैम्ब्डा एक्सप्रेशन को एक अभिव्यक्ति ट्री में शामिल Expression.Constant()करने के लिए बनाया जा सकता है Expression.Quote(), और कोई LINQ क्वेरी प्रोवाइडर जो किसी अन्य क्वेरी लैंग्वेज (जैसे SQL) में एक्सप्रेशन ट्री को प्रोसेस करना चाहता है, इसके बजाय ConstantExpressionटाइप के साथ देख सकता है Expression<TDelegate>। एक UnaryExpressionविशेष के साथ Quoteनोड प्रकार, और बाकी सब कुछ ही होगा।

मैं क्या खो रहा हूँ? आविष्कार के लिए Expression.Quote()विशेष Quoteनोड प्रकार क्यों और UnaryExpressionकैसे था?

जवाबों:


189

संक्षिप्त जवाब:

उद्धरण ऑपरेटर एक ऑपरेटर है जो अपने ऑपरेंड पर क्लोजर शब्दार्थों को प्रेरित करता है । निरंतरता केवल मूल्य हैं।

उद्धरण और स्थिरांक के अलग-अलग अर्थ होते हैं और इसलिए एक अभिव्यक्ति पेड़ में अलग-अलग प्रतिनिधित्व होते हैं । दो अलग-अलग चीजों के लिए समान प्रतिनिधित्व होना बेहद आवश्यक है भ्रमित और बग प्रवण होता है।

लंबा जवाब:

निम्नलिखित को धयान मे रखते हुए:

(int s)=>(int t)=>s+t

बाहरी लैम्ब्डा योजक के लिए एक कारखाना है जो बाहरी लैम्ब्डा के पैरामीटर के लिए बाध्य है।

अब, मान लीजिए कि हम इसे एक अभिव्यक्ति वृक्ष के रूप में प्रस्तुत करना चाहते हैं जिसे बाद में संकलित और निष्पादित किया जाएगा। अभिव्यक्ति पेड़ का शरीर क्या होना चाहिए? यह इस बात पर निर्भर करता है कि आप संकलित राज्य को एक प्रतिनिधि या एक अभिव्यक्ति ट्री वापस करना चाहते हैं।

आइए निर्बाध मामले को खारिज करके शुरू करें। यदि हम इसे किसी प्रतिनिधि को लौटाने की इच्छा रखते हैं, तो यह प्रश्न कि क्या कोट या कॉन्स्टेंट का उपयोग करना एक मुदित बिंदु है:

        var ps = Expression.Parameter(typeof(int), "s");
        var pt = Expression.Parameter(typeof(int), "t");
        var ex1 = Expression.Lambda(
                Expression.Lambda(
                    Expression.Add(ps, pt),
                pt),
            ps);

        var f1a = (Func<int, Func<int, int>>) ex1.Compile();
        var f1b = f1a(100);
        Console.WriteLine(f1b(123));

लैम्ब्डा में एक नेस्टेड लैम्ब्डा है; कंपाइलर आंतरिक लैम्ब्डा को एक फ़ंक्शन के प्रतिनिधि के रूप में उत्पन्न करता है जो बाहरी लैम्बडा के लिए उत्पन्न फ़ंक्शन की स्थिति पर बंद होता है। हमें इस मामले पर विचार करने की आवश्यकता है।

मान लें कि हम संकलित स्थिति को इंटीरियर के एक अभिव्यक्ति पेड़ को वापस करने की इच्छा रखते हैं । ऐसा करने के दो तरीके हैं: आसान तरीका और कठिन तरीका।

इसके बजाय कहना मुश्किल तरीका है

(int s)=>(int t)=>s+t

हम वास्तव में क्या मतलब है

(int s)=>Expression.Lambda(Expression.Add(...

और फिर के लिए अभिव्यक्ति पेड़ उत्पन्न कि उत्पादन इस गड़बड़ :

        Expression.Lambda(
            Expression.Call(typeof(Expression).GetMethod("Lambda", ...

ब्लाह ब्ला ब्ला, लैम्ब्डा बनाने के लिए प्रतिबिंब कोड की दर्जनों लाइनें। उद्धरण ऑपरेटर का उद्देश्य अभिव्यक्ति ट्री संकलक को यह बताना है कि हम चाहते हैं कि लैम्बडा को एक अभिव्यक्ति ट्री के रूप में माना जाए, न कि फ़ंक्शन के रूप में, अभिव्यक्ति ट्री जनरेशन कोड को स्पष्ट रूप से उत्पन्न किए बिना

आसान तरीका है:

        var ex2 = Expression.Lambda(
            Expression.Quote(
                Expression.Lambda(
                    Expression.Add(ps, pt),
                pt)),
            ps);

        var f2a = (Func<int, Expression<Func<int, int>>>)ex2.Compile();
        var f2b = f2a(200).Compile();
        Console.WriteLine(f2b(123));

और वास्तव में, यदि आप इस कोड को संकलित करते हैं और चलाते हैं तो आपको सही उत्तर मिलता है।

ध्यान दें कि उद्धरण ऑपरेटर वह ऑपरेटर होता है जो आंतरिक लंबो पर क्लोजर शब्दार्थों को प्रेरित करता है जो बाहरी चर का औपचारिक पैरामीटर का उपयोग करता है।

सवाल यह है कि क्यों नहीं उद्धरण को खत्म करें और इसे एक ही काम करें?

        var ex3 = Expression.Lambda(
            Expression.Constant(
                Expression.Lambda(
                    Expression.Add(ps, pt),
                pt)),
            ps);

        var f3a = (Func<int, Expression<Func<int, int>>>)ex3.Compile();
        var f3b = f3a(300).Compile();
        Console.WriteLine(f3b(123));

निरंतर बंद शब्दार्थ को प्रेरित नहीं करता है। क्यों करना चाहिए? आपने कहा कि यह एक निरंतर था । यह सिर्फ एक मूल्य है। यह कंपाइलर को सौंपने के लिए एकदम सही होना चाहिए; कंपाइलर को केवल उस मान का एक डंप उत्पन्न करने में सक्षम होना चाहिए जहां यह आवश्यक है।

चूंकि कोई क्लोजर प्रेरित नहीं है, अगर आप ऐसा करते हैं तो आपको "वैरिएबल 'का' प्रकार मिलेगा 'System.Int32' आह्वान पर अपवाद नहीं है"।

(एक तरफ: मैंने उद्धृत अभिव्यक्ति पेड़ों से प्रतिनिधि सृजन के लिए कोड जनरेटर की समीक्षा की है, और दुर्भाग्य से एक टिप्पणी जो मैंने 2006 में कोड में वापस रखी थी, अभी भी है। FYI करें, फहराया गया बाहरी पैरामीटर एक निरंतर में स्नैपशॉट है जब उद्धरण। अभिव्यक्ति ट्री को रनटाइम कंपाइलर द्वारा एक प्रतिनिधि के रूप में पुन: प्रस्तुत किया गया है। एक अच्छा कारण था कि मैंने कोड को इस तरह से लिखा था जो मुझे इस सटीक क्षण में याद नहीं है, लेकिन इसमें मूल्यों पर बंद होने का बुरा दुष्प्रभाव है बाहरी मापदंडों के चर पर बंद करने के बजाय। जाहिरा तौर पर उस कोड को विरासत में मिली टीम ने उस दोष को ठीक नहीं करने का फैसला किया, इसलिए यदि आप एक बंद किए गए बाहरी पैरामीटर के म्यूटेशन पर भरोसा कर रहे हैं, जो एक संकलित आंतरिक लैम्ब्डा में मनाया जा रहा है, तो आप निराश होने वाले हैं। हालाँकि, चूंकि यह दोनों के लिए एक बहुत बुरा प्रोग्रामिंग अभ्यास है (1) एक औपचारिक पैरामीटर को म्यूट करें और (2) एक बाहरी चर के उत्परिवर्तन पर भरोसा करते हैं, मैं आपको सलाह दूंगा कि आप इन दो खराब प्रोग्रामिंग प्रथाओं का उपयोग न करने के लिए अपने कार्यक्रम को बदलें। एक फिक्स के इंतजार में जो आगे दिखाई नहीं देता है। त्रुटि के लिए माफी।)

तो, सवाल दोहराने के लिए:

C # कंपाइलर नेस्टेड लैम्ब्डा एक्सप्रेशंस को अभिव्यक्ति ट्री में शामिल किया जा सकता है, जिसमें एक्सप्रेशन.Quote () के बजाय Expression.Constant () शामिल है, और कोई LINQ क्वेरी प्रोवाइडर जो कुछ क्वैरी लैंग्वेज (जैसे SQL) में एक्सप्रेशन ट्री प्रोसेस करना चाहता है ) एक कॉनटेंट एक्सप्रेशन के लिए एक विशेष उद्धरण नोड प्रकार के साथ एक UnaryExpression के बजाय टाइप एक्सप्रेशन के साथ देख सकता है, और बाकी सब समान होगा।

तुम सही हो। हम अर्थ संबंधी जानकारी को सांकेतिक शब्दों में बदल सकते हैं, जिसका अर्थ है " निरंतर मूल्य इस अर्थ को बंद करें" को एक ध्वज के रूप में स्थिर अभिव्यक्ति के प्रकार का उपयोग करके

"निरंतर" का अर्थ तब होगा "इस निरंतर मूल्य का उपयोग करें, जब तक कि प्रकार एक अभिव्यक्ति वृक्ष का प्रकार न हो और मान एक मान्य अभिव्यक्ति ट्री है, इस स्थिति में, इसके बजाय मान का उपयोग करें जो कि अभिव्यक्ति ट्री फिर से लिखने के परिणामस्वरूप होता है। दिए गए अभिव्यक्ति ट्री का इंटीरियर किसी भी बाहरी लंबोदा के संदर्भ में क्लोजर शब्दार्थ को प्रेरित करने के लिए है जो हम अभी हो सकते हैं।

लेकिन हम उस पागल चीज को क्यों करेंगे ? उद्धरण ऑपरेटर एक बिलकुल जटिल ऑपरेटर है , और यदि आप इसका उपयोग करने जा रहे हैं तो इसका उपयोग स्पष्ट रूप से किया जाना चाहिए । आप सुझाव दे रहे हैं कि पहले से ही कई दर्जन के बीच एक अतिरिक्त कारखाना विधि और नोड प्रकार को न जोड़ने के बारे में निंदनीय होने के लिए, कि हम स्थिरांक के लिए एक विचित्र कोने के मामले को जोड़ते हैं, ताकि स्थिरांक कभी-कभी तार्किक रूप से स्थिर होते हैं, और कभी-कभी उन्हें फिर से लिखा जाता है। बंद शब्दार्थ के साथ लंबोदर।

इसका कुछ हद तक अजीब प्रभाव भी होगा कि निरंतर का अर्थ "इस मूल्य का उपयोग" नहीं है। मान लीजिए कि कुछ विचित्र कारण के लिए आप ऊपर के तीसरे मामले को एक अभिव्यक्ति पेड़ को एक प्रतिनिधि में संकलित करना चाहते थे जो एक अभिव्यक्ति पेड़ को सौंपता है जिसमें एक बाहरी चर के लिए पुन: लिखित संदर्भ नहीं है? क्यों? शायद इसलिए कि आप अपने कंपाइलर का परीक्षण कर रहे हैं और केवल इस पर से निरंतर गुजरना चाहते हैं ताकि आप बाद में उस पर कुछ अन्य विश्लेषण कर सकें। आपका प्रस्ताव असंभव बना देगा; कोई भी स्थिरांक जो अभिव्यक्ति वृक्ष के प्रकार का होता है, उसकी परवाह किए बिना फिर से लिखा जाएगा। एक को एक उचित उम्मीद है कि "निरंतर" का अर्थ है "इस मूल्य का उपयोग करें"। "लगातार" एक "मैं जो कहता हूं वह करो" नोड है। निरंतर प्रोसेसर ' प्रकार के आधार पर कहने के लिए।

और ध्यान दें कि अब आप समझ का बोझ डाल रहे हैं (अर्थात, यह समझते हुए कि स्थिरांक में जटिल शब्दार्थ है, जिसका अर्थ है एक मामले में "स्थिर" और " प्रत्येक सिस्टम में एक ध्वज के आधार पर" बंद करने वाले शब्दार्थ को प्रेरित करता है ) प्रदाता जो केवल Microsoft प्रदाताओं पर नहीं, एक अभिव्यक्ति पेड़ का शब्दार्थ विश्लेषण करता है। उन तृतीय-पक्ष प्रदाताओं में से कितने इसे गलत पाएंगे?

"उद्धरण" एक बड़ा लाल झंडा लहरा रहा है जो कहता है "हे दोस्त, यहाँ देखो, मैं एक नेस्टेड लैम्ब्डा एक्सप्रेशन हूँ और अगर मैं एक बाहरी चर पर बंद हूँ तो मुझे निराला शब्दार्थ मिला है!" जबकि "कांस्टेंट" कह रहा है "मैं एक मूल्य से ज्यादा कुछ नहीं हूं, जैसा कि आप फिट देखते हैं, मेरा उपयोग करें।" जब कोई चीज जटिल और खतरनाक होती है तो हम उसे लहरदार लाल झंडे बनाना चाहते हैं, इस तथ्य को छिपाते हुए नहीं कि उपयोगकर्ता इस प्रकार की प्रणाली के माध्यम से खुदाई करता है ताकि यह पता लगाया जा सके कि यह मूल्य एक विशेष है या नहीं।

इसके अलावा, यह विचार कि अतिरेक से बचना भी एक लक्ष्य है, गलत है। निश्चित रूप से, अनावश्यक से बचना, अतिरेक को भ्रमित करना एक लक्ष्य है, लेकिन अधिकांश अतिरेक एक अच्छी बात है; अतिरेक स्पष्टता पैदा करता है। नए कारखाने के तरीके और नोड प्रकार सस्ते हैं । हम जितने चाहें उतने बना सकते हैं ताकि हर एक एक ऑपरेशन को साफ-सुथरा कर सके। हमें "इस बात का मतलब है कि जब तक इस क्षेत्र को इस चीज के लिए सेट नहीं किया जाता है, इस मामले में इसका मतलब कुछ और नहीं है"


11
मैं अब शर्मिंदा हूं क्योंकि मैंने शब्दार्थ को बंद करने के बारे में नहीं सोचा था और ऐसे मामले का परीक्षण करने में विफल रहा जहां नेस्टेड लैम्ब्डा एक बाहरी लैम्ब्डा से एक पैरामीटर कैप्चर करता है। अगर मैंने ऐसा किया होता, तो मुझे फर्क नजर आता। आपके उत्तर के लिए फिर से बहुत धन्यवाद।
तिवारी

19

इस प्रश्न का पहले ही एक उत्कृष्ट उत्तर मिल चुका है। मैं इसके अतिरिक्त एक ऐसे संसाधन की ओर संकेत करना चाहूंगा जो अभिव्यक्ति के पेड़ों के बारे में सवालों के साथ मददगार साबित हो सकता है:

वहाँ है Microsoft नाम से एक कोडप्लेक्स प्रोजेक्ट था गतिशील भाषा रनटाइम। इसके दस्तावेज़ में शीर्षक वाला दस्तावेज़ शामिल है,"अभिव्यक्ति पेड़ v2 कल्पना", जो कि ठीक है: .NET 4 में LINQ अभिव्यक्ति पेड़ों के लिए विनिर्देशन।

अपडेट: कोडप्लेक्स डिफेक्ट है। युक्ति (पीडीएफ) वी 2 अभिव्यक्ति पेड़ GitHub के लिए ले जाया गया है

उदाहरण के लिए, यह निम्नलिखित के बारे में कहता है Expression.Quote:

4.4.42 भाव

UnaryExpressions में उद्धरण का उपयोग करें एक अभिव्यक्ति का प्रतिनिधित्व करता है जिसमें "अभिव्यक्ति" का प्रकार अभिव्यक्ति है। एक निरंतर नोड के विपरीत, कोट नोड विशेष रूप से सम्‍मिलित पैरामीटर पैरामीटर संभालता है। यदि एक सम्‍मिलित ParameterExpression नोड एक स्थानीय घोषित करता है जो परिणामी अभिव्यक्ति में बंद हो जाएगा, तो उद्धरण अपने संदर्भ स्थानों में ParameterExpression को बदल देता है। रन समय में जब उद्धरण नोड का मूल्यांकन किया जाता है, तो यह ParameterExpression संदर्भ नोड्स के लिए बंद चर संदर्भों को प्रतिस्थापित करता है, और फिर उद्धृत अभिव्यक्ति देता है। […] (पृष्ठ ६३-६४)


1
सिखने-ए-इंसान-मछली की तरह का उत्कृष्ट जवाब। मैं यह जोड़ना चाहूंगा कि प्रलेखन स्थानांतरित हो गया है और अब docs.microsoft.com/en-us/dotnet/framework/… पर उपलब्ध है । उद्धृत दस्तावेज़, विशेष रूप से, GitHub पर है: github.com/IronLanguages/dlr/tree/master/Docs
अपेक्षाकृत_ब्रांड

3

यह वास्तव में उत्कृष्ट उत्तर के बाद, यह स्पष्ट है कि शब्दार्थ क्या हैं। यह इतना स्पष्ट नहीं है कि उन्हें इस तरह से क्यों बनाया गया है, विचार करें:

Expression.Lambda(Expression.Add(ps, pt));

जब इस लंबो को संकलित किया जाता है और आह्वान किया जाता है तो यह आंतरिक अभिव्यक्ति का मूल्यांकन करता है और परिणाम देता है। यहां आंतरिक अभिव्यक्ति एक अतिरिक्त है, इसलिए पीएस + पीटी का मूल्यांकन किया जाता है और परिणाम वापस किया जाता है। इस तर्क के बाद, निम्नलिखित अभिव्यक्ति:

Expression.Lambda(
    Expression.Lambda(
              Expression.Add(ps, pt),
            pt), ps);

बाहरी लैम्ब्डा को मंगाए जाने पर एक आंतरिक लैम्ब्डा संकलित विधि संदर्भ वापस करना चाहिए (क्योंकि हम कहते हैं कि लैम्ब्डा एक विधि संदर्भ के लिए संकलित करता है)। तो हम एक उद्धरण की आवश्यकता क्यों है ?! मामले को अलग करने के लिए जब विधि संदर्भ लौटाया जाता है बनाम उस संदर्भ मंगलाचरण का परिणाम।

विशेष रूप से:

let f = Func<...>
return f; vs. return f(...);

किसी कारण के कारण। नेट डिजाइनरों ने पहले मामले के लिए एक्सप्रेशन.काउंट (एफ) और दूसरे के लिए सादे एफ चुना । मेरे विचार से यह बहुत बड़ा भ्रम का कारण बनता है, क्योंकि अधिकांश प्रोग्रामिंग भाषाओं में एक मूल्य प्रत्यक्ष होता है ( उद्धरण या किसी अन्य ऑपरेशन की कोई आवश्यकता नहीं है ), लेकिन आह्वान के लिए अतिरिक्त लेखन (कोष्ठक + तर्क) की आवश्यकता होती है, जो किसी प्रकार का अनुवाद करता है MSIL स्तर पर आह्वान । .Net डिजाइनरों ने इसे अभिव्यक्ति के पेड़ों के विपरीत बनाया। कारण जानना दिलचस्प होगा।


-2

मुझे लगता है कि यहाँ बिंदु पेड़ की अभिव्यक्ति है। प्रतिनिधि के साथ निरंतर अभिव्यक्ति वास्तव में सिर्फ एक वस्तु होती है जो एक प्रतिनिधि होती है। यह एक एकता और द्विआधारी अभिव्यक्ति के लिए प्रत्यक्ष टूटने की तुलना में कम अभिव्यंजक है।


क्या यह? क्या अभिव्यक्ति को जोड़ता है, बिल्कुल? आप उस UnaryExpression के साथ "एक्सप्रेस" क्या कर सकते हैं (जो कि उपयोग करने के लिए एक अजीब तरह की अभिव्यक्ति है, भी) जिसे आप पहले से ही कॉन्स्टेंटएक्सप्रेशन के साथ व्यक्त नहीं कर सकते हैं?
टिमवी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.