2014-11-04 5 views
22

यह वास्तव में कुछ आसान होना चाहिए। लेकिन मैं इसे किसी भी तरह से पूछने जा रहा हूं, क्योंकि मुझे लगता है कि अन्य इसके साथ भी संघर्ष करेंगे। निम्नलिखित सरल LINQ क्वेरी को हमेशा पहले उपयोग करने के बजाय हमेशा नए चर-मूल्य के साथ निष्पादित नहीं किया जाता है?LINQ के स्थगित निष्पादन, लेकिन कैसे?

static void Main(string[] args) 
{ 
    Console.WriteLine("Enter something:"); 
    string input = Console.ReadLine();  // for example ABC123 
    var digits = input.Where(Char.IsDigit); // 123 
    while (digits.Any()) 
    { 
     Console.WriteLine("Enter a string which doesn't contain digits"); 
     input = Console.ReadLine();   // for example ABC 
    } 
    Console.WriteLine("Bye"); 
    Console.ReadLine(); 
} 

टिप्पणी की नमूने में यह बाद से इनपुट ABC123 अंक हैं पाश में प्रवेश करेंगे। लेकिन digits से 123 के बाद से ABC जैसे कुछ दर्ज करने पर भी यह कभी नहीं छोड़ेगा।

तो LINQ क्वेरी नए input -value का मूल्यांकन क्यों नहीं करती है लेकिन हमेशा पहले? और अधिक सुरुचिपूर्ण - - पाश में सीधे क्वेरी का उपयोग करके

while (digits.Any()) 
{ 
    Console.WriteLine("Enter a string which doesn't contain digits"); 
    input = Console.ReadLine();   
    digits = input.Where(Char.IsDigit); // now it works as expected 
} 

या:

while (input.Any(Char.IsDigit)) 
{ 
    // ... 
} 
+5

जब आप किसी चरम पर पैरामीटर के रूप में एक चर को पास करते हैं, तो यह मान द्वारा पारित किया जाता है। –

+1

इस तरह के जटिल साइड इफेक्ट्स के साथ कोड का एक साधारण प्रतीत होता है। –

+2

पवित्र गाय, आपको वास्तविक रेमंड चेन ™ का जवाब मिला! –

उत्तर

40

अंतर यह है कि आप input चर के मूल्य बदल रहे हैं, बल्कि सामग्री वस्तु के से उस चर को संदर्भित करता है ... इसलिए digits अभी भी मूल संग्रह को संदर्भित करता है।

तुलना करें कि इस कोड के साथ:

List<char> input = new List<char>(Console.ReadLine()); 
var digits = input.Where(Char.IsDigit); // 123 
while (digits.Any()) 
{ 
    Console.WriteLine("Enter a string which doesn't contain digits"); 
    input.Clear(); 
    input.AddRange(Console.ReadLine()); 
} 

इस बार, हम संग्रह है कि input को संदर्भित करता है के सामग्री संशोधित कर रहे हैं - और digits के रूप में प्रभावी ढंग से कि संग्रह के दृश्य है, हम करने के लिए मिल परिवर्तन देखें।

+0

क्या मैं सही हूं - स्ट्रिंग की अपरिवर्तनीयता के कारण यह प्रश्न उदाहरण में हुआ है? – fex

+1

@fex: तारों की अपरिवर्तनीयता इस मुद्दे का कारण नहीं थी, लेकिन यह मेरे भ्रम का कारण था। यदि स्ट्रिंग 'संग्रह ' जैसे संग्रह था, तो मैं इसे सीधे संशोधित करता हूं और चर को नई सूची निर्दिष्ट नहीं करता हूं। –

+1

@fex: सीधे नहीं। यदि स्ट्रिंग्स म्यूटेबल थे तो 'इनपुट' के मान को बदलना * अभी भी * अंकों को प्रभावित नहीं करेगा ... लेकिन 'इनपुट' द्वारा संदर्भित सामग्री को बदले में बदल दिया जा सकता था। –

10

आप एक नया मान निर्दिष्ट कर रहे हैं

मैं मैं इस अतिरिक्त लाइन के साथ इसे ठीक कर सकता है पता input पर, लेकिन digits अनुक्रम अभी भी input के आरंभिक मान से लिया गया है। दूसरे शब्दों में, जब आप digits = input.Where(Char.IsDigit) करते हैं, यह input चर के वर्तमान मान को कैप्चर करता है, वैरिएबल स्वयं नहीं। input पर एक नया मान असाइन करने पर digits पर कोई प्रभाव नहीं पड़ता है।

4

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

याद रखें कि Where एक स्थिर विस्तार विधि है, और उस ऑब्जेक्ट को स्वीकार करता है जिसे आप इसे पैरामीटर के रूप में आमंत्रित कर रहे हैं।

6

यह पंक्ति:

Enumerable.Where(input, Char.IsDigit) 

इस प्रकार, input का मूल्य .Where क्वेरी का स्रोत है, न कि संदर्भinput के रूप में पारित किया जा रहा है:

input.Where(Char.IsDigit) 

बराबर है।

आपके द्वारा प्रस्तावित पहला फ़िक्स काम करता है क्योंकि यह पहले से लाइन पर input के ताजा-निर्दिष्ट मान का उपयोग करता है।

+0

हां। कोई कह सकता है कि 'इनपुट' एक ByValue पैरामीटर है, एक ByRef पैरामीटर नहीं है (यह' ref' या 'out' नहीं कहता है)। –

+0

@JeppeStigNielsen: यह वास्तव में सच नहीं है। स्ट्रिंग्स संदर्भ प्रकार हैं (यही कारण है कि आप एक शून्य स्ट्रिंग हो सकता है)। जिसका अर्थ है कि चर 'इनपुट' वास्तव में स्ट्रिंग के संदर्भ में है। यदि 'स्ट्रिंग' उत्परिवर्तनीय था, तो आप एक सामान्य पैरामीटर के रूप में एक स्ट्रिंग को एक स्ट्रिंग पास कर सकते हैं, और अंदर स्ट्रिंग को संशोधित करने से मूल स्ट्रिंग में भी संशोधन होगा। लेकिन चूंकि आप स्ट्रिंग्स को संशोधित नहीं कर सकते हैं, तो कुछ परिदृश्यों में इसका उपरोक्त पैरामीटर के रूप में पैराव्यू के रूप में समान प्रभाव पड़ता है। –

+0

@tomp मुझे पता है कि 'स्ट्रिंग' एक संदर्भ प्रकार है। यही वह नहीं था जो मैं ले रहा था। मैं इस बारे में ले रहा था कि पैरामीटर एक ByRef पैरामीटर था (या तो 'रेफ स्ट्रिंग' या 'स्ट्रिंग'') या नहीं। तो हम सहमत हैं। यह दुर्भाग्यपूर्ण है कि दो अलग-अलग विचार "संदर्भ प्रकार" (उदाहरण के लिए 'कक्षा' (आदि), 'संरचना' नहीं) और "रेफ पैरामीटर द्वारा" (या तो 'रेफरी' या 'आउट') के समान नाम हैं। यह कई गलतफहमी का कारण बनता है। मैं वास्तव में इसके बारे में जागरूक था और अपना शब्द सटीक बनाने की कोशिश कर रहा था, लेकिन फिर भी मुझे गलत समझा गया था ... –

2

मैं स्थगित निष्पादन के बारे में अन्य अच्छे उत्तरों के लिए सटीकता जोड़ने के लिए उत्तर दे रहा हूं।

यहां तक ​​कि अगर LINQ क्वेरी के रूप में अभी तक (.Any() उपयोग करते हुए) नहीं मूल्यांकन किया गया है, क्वेरी आंतरिक रूप से हमेशा चर की प्रारंभिक सामग्री को दर्शाता है। यहां तक ​​कि अगर LINQ क्वेरी के बाद कुछ नया चर करने के लिए प्रभावित हुआ है मूल्यांकन किया जाता है, प्रारंभिक सामग्री नहीं बदलता है और आस्थगित निष्पादन प्रारंभिक सामग्री क्वेरी हमेशा की चर्चा करते हुए किया गया है का उपयोग करेगा:

var input = "ABC123"; 
var digits = input.Where(Char.IsDigit); 
input = "NO DIGIT"; 
var result = digits.ToList(); // 3 items 
+1

बस चेतावनी दीजिये कि 'प्रारंभिक सामग्री' एक परिवर्तनीय संरचना को इंगित कर सकती है। जैसे 'इनपुट = नई सूची {1}; var भी = input.where (x => x% 2 == 0); input.Add (2); var result = even.ToList(); ' – NPSF3000

+2

@ NPSF3000: यदि यह एक उत्परिवर्तनीय संग्रह होता तो मैं किसी समस्या की कमी के कारण इस प्रश्न से नहीं पूछा होता ;-) –

4

यह लगभग एक टिप्पणी है, लेकिन संरचित कोड है, इसलिए मैं इसे एक उत्तर के रूप में प्रस्तुत करता हूं।

अपने कोड की निम्न मामूली संशोधन काम करेगा:

Console.WriteLine("Enter something:"); 
    string input = Console.ReadLine();  // for example ABC123 
    Func<bool> anyDigits =() => input.Any(Char.IsDigit); // will capture 'input' as a field 
    while (anyDigits()) 
    { 
    Console.WriteLine("Enter a string which doesn't contain digits"); 
    input = Console.ReadLine();   // for example ABC 
    } 
    Console.WriteLine("Bye"); 
    Console.ReadLine(); 

यहाँ input प्रकार Func<bool> के प्रतिनिधि द्वारा (बंद) पर कब्जा कर लिया है।

+0

रीशेपर whines," संशोधित बंद करने के लिए उपयोग " 'इनपुट' के पहले उपयोग के लिए। सुझाव दें कि आप 'Func ' में बदलें और 'while (anyDigits (इनपुट)) '(जो तर्कसंगत रूप से पठनीयता में सुधार करता है) का उपयोग करके कॉल करें। – onedaywhen

+0

@onedaywhen हाँ! यह कोड लिखने का सबसे अच्छा तरीका नहीं था। यह वास्तव में मूल कोड (प्रश्न से) का "न्यूनतम" परिवर्तन होना था जो वास्तव में काम करता था। मैं ऊपर की तरह कोड करने के लिए लोगों को प्रोत्साहित या अनुशंसा नहीं करता हूं। ReSharper आपको अलर्ट करने का कारण यह है कि क्लोजर सेमेन्टिक्स कोड के पाठक को भ्रमित कर सकता है। वांछित कार्यक्षमता प्राप्त करने के लिए, हमें संशोधित बंद करने की आवश्यकता नहीं है (और पूछताछकर्ता प्रश्नों में पहले से ही काम करने के बेहतर तरीके जानता है)। –

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