2011-11-12 6 views
26

क्यों यह संभव string पर धाराप्रवाह भाषा का उपयोग नहीं है?स्ट्रिंग करने के लिए IENumerable <char> को परिवर्तित करने का सबसे अच्छा तरीका?

उदाहरण के लिए:

var x = "asdf1234"; 
var y = new string(x.TakeWhile(char.IsLetter).ToArray()); 

वहाँ एक बेहतर तरीका string को IEnumerable<char> कन्वर्ट करने के लिए नहीं है?

यहाँ एक परीक्षण मैं लगा रहे:

class Program 
{ 
    static string input = "asdf1234"; 
    static void Main() 
    { 
    Console.WriteLine("1000 times:"); 
    RunTest(1000, input); 
    Console.WriteLine("10000 times:"); 
    RunTest(10000,input); 
    Console.WriteLine("100000 times:"); 
    RunTest(100000, input); 
    Console.WriteLine("100000 times:"); 
    RunTest(100000, "ffff57467"); 


    Console.ReadKey(); 

    } 

    static void RunTest(int times, string input) 
    { 

    Stopwatch sw = new Stopwatch(); 

    sw.Start(); 
    for (int i = 0; i < times; i++) 
    { 
     string output = new string(input.TakeWhile(char.IsLetter).ToArray()); 
    } 
    sw.Stop(); 
    var first = sw.ElapsedTicks; 

    sw.Restart(); 
    for (int i = 0; i < times; i++) 
    { 
     string output = Regex.Match(input, @"^[A-Z]+", 
     RegexOptions.IgnoreCase).Value; 
    } 
    sw.Stop(); 
    var second = sw.ElapsedTicks; 

    var regex = new Regex(@"^[A-Z]+", 
     RegexOptions.IgnoreCase); 
    sw.Restart(); 
    for (int i = 0; i < times; i++) 
    { 
     var output = regex.Match(input).Value; 
    } 
    sw.Stop(); 
    var third = sw.ElapsedTicks; 

    double percent = (first + second + third)/100; 
    double p1 = (first/percent)/ 100; 
    double p2 = (second/percent)/100; 
    double p3 = (third/percent )/100; 


    Console.WriteLine("TakeWhile took {0} ({1:P2}).,", first, p1); 
    Console.WriteLine("Regex took {0}, ({1:P2})." , second,p2); 
    Console.WriteLine("Preinstantiated Regex took {0}, ({1:P2}).", third,p3); 
    Console.WriteLine(); 
    } 
} 

परिणाम:

1000 times: 
TakeWhile took 11217 (62.32%)., 
Regex took 5044, (28.02%). 
Preinstantiated Regex took 1741, (9.67%). 

10000 times: 
TakeWhile took 9210 (14.78%)., 
Regex took 32461, (52.10%). 
Preinstantiated Regex took 20669, (33.18%). 

100000 times: 
TakeWhile took 74945 (13.10%)., 
Regex took 324520, (56.70%). 
Preinstantiated Regex took 172913, (30.21%). 

100000 times: 
TakeWhile took 74511 (13.77%)., 
Regex took 297760, (55.03%). 
Preinstantiated Regex took 168911, (31.22%). 

निष्कर्ष: मैं पर शक कर रहा हूँ क्या पसंद करते हैं करने के लिए बेहतर है, मुझे लगता है कि मैं TakeWhile पर जाना चाहता हूँ जो केवल पहले रन पर सबसे धीमा है।

वैसे भी, मेरा सवाल यह है कि TakeWhile फ़ंक्शन के परिणाम को पुन: प्रयास करके प्रदर्शन को अनुकूलित करने का कोई तरीका है।

+1

कृपया बताएं कि आपका क्या मतलब है "सर्वश्रेष्ठ": सबसे तेज़? कम स्मृति-भूखे? समझने के लिए सबसे आसान है? – LukeH

+0

@LukeH मैंने पहले से ही अपना निर्णय लिया है कि क्या चुनना है: उपवास। अगर वहाँ 'नई स्ट्रिंग (x.TakeWhile (पी) .ToArray) की तुलना में एक अच्छे तरीका है मेरा प्रश्न है' – Shimmy

+2

@LukeH: अपने समाधान हटाना चाहते हैं सकता है: यह एक बहुत बड़े अंतर – BrokenGlass

उत्तर

13

यह मानते हुए कि आप मुख्य रूप से प्रदर्शन के लिए देख रहे हैं, तो कुछ इस तरह काफी तेजी से अपने उदाहरण में से किसी की तुलना में होना चाहिए:

string x = "asdf1234"; 
string y = x.LeadingLettersOnly(); 

// ... 

public static class StringExtensions 
{ 
    public static string LeadingLettersOnly(this string source) 
    { 
     if (source == null) 
      throw new ArgumentNullException("source"); 

     if (source.Length == 0) 
      return source; 

     char[] buffer = new char[source.Length]; 
     int bufferIndex = 0; 

     for (int sourceIndex = 0; sourceIndex < source.Length; sourceIndex++) 
     { 
      char c = source[sourceIndex]; 

      if (!char.IsLetter(c)) 
       break; 

      buffer[bufferIndex++] = c; 
     } 
     return new string(buffer, 0, bufferIndex); 
    } 
} 
+0

हम्म, बस ध्यान दिया कि आपको केवल स्ट्रिंग की शुरुआत से अक्षरों की आवश्यकता है, जिस स्थिति में मैं अपेक्षा करता हूं [ब्रोकनग्लस का जवाब] (http://stackoverflow.com/questions/8108313/best-way-to-convert-ienumerablechar -to-string/8108584 # 8108584) सबसे तेज़ होने के लिए। (फिर से, मैंने वास्तव में पुष्टि करने के लिए बेंचमार्क नहीं किया है।) – LukeH

+1

+1 बफर को प्री-आवंटित करना शायद यह तेज़ बनाता है, लेकिन यह केवल एक अनुमान है - सीमित परीक्षण 'सबस्ट्रिंग()' – BrokenGlass

9

आप बहुत बार कर सकते हैं बेहतर प्रदर्शन के लिहाज से। लेकिन वह आपको क्या खरीदता है? यह मैं Linq TakeWhile() संस्करण के लिए छड़ी होगा होने के लिए जब तक यह वास्तव में आपके आवेदन के लिए बोतल गर्दन है और आप मापा है: यह सबसे पठनीय और पोषणीय समाधान है, और कि क्या सभी आवेदनों के अधिकांश के लिए मायने रखता है है।

आप वास्तव में देख रहे हैं कच्चे प्रदर्शन के लिए आप रूपांतरण मैन्युअल कर सकता है - निम्नलिखित एक कारक 4 के आसपास थी मेरी परीक्षणों में TakeWhile() की तुलना में तेजी (इनपुट स्ट्रिंग लंबाई के आधार पर) - लेकिन मैं इसका इस्तेमाल नहीं होता व्यक्तिगत रूप से जब तक यह महत्वपूर्ण था:

int j = 0; 
for (; j < input.Length; j++) 
{ 
    if (!char.IsLetter(input[j])) 
     break; 
} 
string output = input.Substring(0, j); 
+3

+ का उपयोग करने से तेज़ तरीका दिखाता है + 1। और पुन: उपयोग के लिए किसी प्रकार की सहायक विधि में इसे लपेटने में कुछ भी गलत नहीं है। 'Source.LeadingLettersOnly() 'की तरह कुछ' नई स्ट्रिंग (स्रोत.TakeWhile (char.IsLetter) से अधिक पठनीय होगा। ToArray()) ', imo। – LukeH

+1

@LukeH: आपका समाधान तेजी से रास्ता है - कृपया मिटाएं! – BrokenGlass

+0

फ़ंक्शन को कुछ हज़ार (100000) स्ट्रिंग के पहले वर्णों में एक खोज क्वेरी की तुलना करना चाहिए, इसलिए प्रदर्शन सभी महत्वपूर्ण है। – Shimmy

11

क्यों यह संभव स्ट्रिंग पर धाराप्रवाह भाषा का उपयोग नहीं है?

यह संभव है।

var y = new string(x.TakeWhile(char.IsLetter).ToArray()); 

वहाँ एक बेहतर तरीका है स्ट्रिंग के लिए IEnumerable<char> कन्वर्ट करने के लिए नहीं है: आप सवाल अपने आप में यह क्या किया?

(मेरे धारणा :) है

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

इसका एकमात्र समाधान बैकिंग सरणी या StringBuilder को पहले धक्का देना है, और इनपुट बढ़ने के साथ पुन: आवंटित करना है। एक स्ट्रिंग के रूप में निम्न स्तर के रूप में कुछ के लिए, यह शायद एक तंत्र को छुपाया जाना चाहिए। यह भी लोगों के लिए एक तंत्र है कि के रूप में तेजी से के रूप में संभव नहीं हो सकता है का उपयोग करने के लिए प्रोत्साहित करके स्ट्रिंग वर्ग में नीचे पर्फ़ समस्याओं धक्का होगा।

उपयोगकर्ता को ToArray एक्सटेंशन विधि का उपयोग करने के लिए आसानी से हल किया जाता है।

के रूप में अन्य लोगों ने बताया है, तो आप को प्राप्त क्या आप (पर्फ़ और अर्थपूर्ण कोड) यदि आप समर्थन कोड लिखने चाहते हैं, और एक विस्तार विधि में है कि समर्थन कोड लपेट एक साफ इंटरफ़ेस प्राप्त करने के लिए कर सकते हैं।

+0

बीटीडब्लू, इसे "धाराप्रवाह" करने के लिए सबसे अच्छी बात है, क्या मैं अपनी एक्सटेंशन लाइब्रेरी में 'जॉइन' अधिभार में जोड़ा गया है जो 'IEnumerable 'लेता है और' स्ट्रिंग 'देता है। – Shimmy

+6

बेनामी डाउनवॉटर कुछ भी मदद नहीं करते हैं। अपने कारण बताएं और मैं आपकी चिंताओं को संबोधित करूंगा। –

31

कैसे इस बारे में कन्वर्ट करने के लिए IEnumerable<char>string रहे हैं:

string.Concat(x.TakeWhile(char.IsLetter)); 
+3

+1 बहुत छोटा और इसकी आवश्यकता नहीं है। ToArray() – Alex

+0

मुझे लगता है कि स्ट्रिंग। कॉनकैट आंतरिक रूप से स्ट्रिंगबिल्डर का उपयोग करता है। अगर ऐसा नहीं होता तो बहुत अजीब होगा। तो इस समाधान को भी वास्तव में अच्छा प्रदर्शन करना चाहिए। –

+0

केवल नेट 4.0। यहां तक ​​कि यदि आप अपना खुद लिखते हैं। 3.5 में तब स्ट्रिंग। कॉनकैट (आईनेमेरेबल ) जो आप उम्मीद करते हैं वह नहीं करता है। –

13

मैं इस another question लेकिन अधिक से अधिक का विषय बना लिया है, कि इस सवाल का सीधा जवाब होता जा रहा है।

मैं एक string के लिए एक IEnumerable<char> परिवर्तित करने की 3 सरल तरीकों के कुछ निष्पादन परीक्षण किया है, उन तरीके हैं

नया स्ट्रिंग

return new string(charSequence.ToArray()); 

Concat

return string.Concat(charSequence) 

StringBuilder

var sb = new StringBuilder(); 
foreach (var c in charSequence) 
{ 
    sb.Append(c); 
} 

return sb.ToString(); 

मेरे परीक्षण में, कि, linked question में विस्तृत है की "Some reasonably small test data" मैं इस तरह के परिणाम प्राप्त 1000000 पुनरावृत्तियों के लिए,

1000000 "कंसट" के पुनरावृत्तियों ने 15 9 7 एमएमएस लिया।

1000000 "नई स्ट्रिंग" के पुनरावृत्तियों ने 869ms लिया।

"स्ट्रिंगबिल्डर" के 1000000 पुनरावृत्तियों ने 748ms लिया।

यह मुझे बताता है कि इस कार्य के लिए string.Concat का उपयोग करने का कोई अच्छा कारण नहीं है। यदि आप सादगी चाहते हैं तो नई स्ट्रिंग दृष्टिकोण का उपयोग करें और यदि प्रदर्शन चाहते हैं तो स्ट्रिंगबिल्डर का उपयोग करें।

मैं अपने दावे को चेतावनी हैं, अभ्यास इन सभी तरीकों ठीक से काम में, और यह सब अनुकूलन से अधिक हो सकता है।

+0

मैं 'स्ट्रिंगबिल्डर' का उपयोग करने के लिए कोड की तीन अतिरिक्त पंक्तियों को लिखने के स्थान पर 'नई स्ट्रिंग' का उपयोग करने के लिए 121 मिलीसेकंड का त्याग करना चाहता हूं। #cleanCode। – RBT

4

नई स्ट्रिंग लौटाएं (foo.Select (x => x) .ToArray());

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