जब आप अपने विचारों को कोड में अनुवाद करते हैं, तो सबसे अधिक संभावना है कि आप उन विधियों का उपयोग करते हैं जिनसे आप सबसे अधिक परिचित हैं। ये वे विधियां हैं जो दिमाग में सबसे ऊपर हैं और स्वचालित रूप से आपके पास आती हैं: आप एक स्ट्रिंग देखते हैं जिसे सफाई की आवश्यकता है और आपकी उंगलियां उन विधियों को टाइप करती हैं जो परिणाम देंगी।
अक्सर, जिन विधियों को आप स्वचालित रूप से टाइप करते हैं वे सबसे सामान्य Ruby विधियां होती हैं, क्योंकि वे वही हैं जिन्हें हम दूसरों की तुलना में अधिक पढ़ते और लिखते हैं, उदाहरण के लिए #gsub स्ट्रिंग्स में वर्णों को प्रतिस्थापित करने के लिए एक सामान्य विधि है। लेकिन, Ruby के पास देने के लिए बहुत कुछ है, मानक संचालन के लिए अधिक विशिष्ट सुविधा विधियों के साथ।
मुझे Ruby का समृद्ध मुहावरा पसंद है क्योंकि यह कोड को अधिक सुंदर और पढ़ने में आसान बनाता है। यदि हम इस समृद्धि से लाभ उठाना चाहते हैं, तो हमें अपने कोड के सबसे सरल भागों को रीफैक्टर करने में समय बिताने की आवश्यकता है—उदाहरण के लिए, एक स्ट्रिंग को साफ करना—and अपने शब्दकोश का विस्तार करने में थोड़ा प्रयास लगता है। सवाल यह है: क्या अतिरिक्त प्रयास इसके लायक है?
स्पेस हटाने के चार तरीके
यहां एक स्ट्रिंग है जो क्रेडिट कार्ड नंबर का प्रतिनिधित्व करती है: "055 444 285"। इसके साथ काम करने के लिए, हम स्पेस हटाना चाहते हैं। #gsub यह कर सकता है; #gsub के साथ आप कुछ भी किसी भी चीज़ से प्रतिस्थापित कर सकते हैं। लेकिन अन्य विकल्प हैं।
string = "055 444 285"
string.gsub(/ /, '')
string.gsub(' ', '')
string.tr(' ', '')
string.delete(' ')
# => "055444285"
यह अभिव्यंजकता है जो मुझे सुविधा विधियों के बारे में सबसे ज्यादा पसंद है। अंतिम एक इसका एक अच्छा उदाहरण है: "स्पेस हटाओ" से ज्यादा स्पष्ट कुछ नहीं हो सकता। विकल्पों के बीच ट्रेड-ऑफ के बारे में सोचते हुए, पठनीयता मेरी पहली प्राथमिकता है, जब तक कि यह निश्चित रूप से प्रदर्शन समस्याओं का कारण न बने। तो, आइए देखें कि मेरा पसंदीदा समाधान, #delete वास्तव में कितना दर्द देता है।
मैंने उपरोक्त उदाहरणों को बेंचमार्क किया। आपको क्या लगता है कि इनमें से कौन सी विधि सबसे तेज़ है?
Benchmark.ips do |x|
x.config(time: 30, warmup: 2)
x.report('gsub') { string.gsub(/ /, '') }
x.report('gsub, no regex') { string.gsub(' ', '') }
x.report('tr') { string.tr(' ', '') }
x.report('delete') { string.delete(' ') }
x.compare!
end
सबसे अधिक से कम प्रदर्शनकारी के क्रम का अनुमान लगाएं। परिणाम देखने के लिए टॉगल खोलें
Comparison:
delete: 2326817.5 i/s
tr: 2121629.8 i/s - 1.10x slower
gsub, no regex: 868184.1 i/s - 2.68x slower
gsub: 474970.5 i/s - 4.90x slower
मुझे क्रम के बारे में आश्चर्य नहीं हुआ, लेकिन गति में अंतर ने अभी भी मुझे आश्चर्यचकित किया। #gsub न केवल धीमा है, बल्कि इसे पाठक के लिए आर्ग्युमेंट्स को 'डीकोड' करने के लिए अतिरिक्त प्रयास की भी आवश्यकता होती है। आइए देखें कि जब स्पेस से अधिक सफाई की बात आती है तो यह तुलना कैसे काम करती है।
अपने नंबर चुनें
निम्न फ़ोन नंबर लें: '(408) 974-2414'। मान लें कि हमें केवल संख्यात्मक चाहिए => 4089742414। मैंने एक #scan भी जोड़ा क्योंकि मुझे यह पसंद है कि यह अधिक स्पष्ट रूप से व्यक्त करता है कि हम कुछ विशेष चीजों का लक्ष्य रखते हैं, उन सभी चीजों को हटाने की कोशिश करने के बजाय जो हम नहीं चाहते।
Benchmark.ips do |x|
x.config(time: 30, warmup: 2)
x.report ('gsub') { string.gsub(/[^0-9] /, '') }
x.report('tr') { string.tr("^0-9", "") }
x.report('delete_chars') { string.delete("^0-9") }
x.report('scan') { string.scan(/[0-9]/).join }
x.compare!
end
फिर से, क्रम का अनुमान लगाएं, फिर उत्तर देखने के लिए टॉगल खोलें
Comparison:
delete_chars: 2006750.8 i/s
tr: 1856429.0 i/s - 1.08x slower
gsub: 523174.7 i/s - 3.84x slower
scan: 227717.4 i/s - 8.81x slower
रेगेक्स का उपयोग करने से चीजें धीमी हो जाती हैं, यह आश्चर्य की बात नहीं है। और #scan की इरादे-प्रकट करने वाली अभिव्यंजकता की हमें बहुत बड़ी कीमत चुकानी पड़ती है। लेकिन यह देखकर कि Ruby की विशिष्ट विधियां सफाई को कैसे संभालती हैं, मुझे और अधिक के लिए स्वाद दिया।
पैसे की बात
आइए स्ट्रिंग "€ 300" से सबस्ट्रिंग "€ " को हटाने के कुछ तरीके आजमाएं। निम्नलिखित कुछ समाधान सटीक सबस्ट्रिंग "€ " को निर्दिष्ट करते हैं, कुछ केवल सभी मुद्रा प्रतीकों या सभी गैर-संख्यात्मक वर्णों को हटा देंगे।
Benchmark.ips do |x|
x.config(time: 30, warmup: 2)
x.report('delete specific chars') { string.delete("€ ") }
x.report('delete non-numericals') { string.delete("^0-9") }
x.report('delete prefix') { string.delete_prefix("€ ") }
x.report('delete prefix, strip') { string.delete_prefix("€").strip }
x.report('gsub') { string.gsub(/€ /, '') }
x.report('gsub-non-nums') { string.gsub(/[^0-9]/, '') }
x.report('tr') { string.tr("€ ", "") }
x.report('slice array') { string.chars.slice(2..-1).join }
x.report('split') { string.split.last }
x.report('scan nums') { string.scan(/\d/).join }
x.compare!
end
आप उम्मीद कर सकते हैं, और सही भी हैं, कि विजेता #delete में से एक है। लेकिन #delete के किस संस्करण के सबसे तेज़ होने की उम्मीद करते हैं? साथ ही: अन्य विधियों में से एक कुछ #delete की तुलना में तेज़ है। कौन सी?
अनुमान लगाएं और फिर खोलें।
Comparison:
delete prefix: 4236218.6 i/s
delete prefix, strip: 3116439.6 i/s - 1.36x slower
split: 2139602.2 i/s - 1.98x slower
delete non-numericals: 1949754.0 i/s - 2.17x slower
delete specific chars: 1045651.9 i/s - 4.05x slower
tr: 951352.0 i/s - 4.45x slower
slice array: 681196.2 i/s - 6.22x slower
gsub: 548588.3 i/s - 7.72x slower
gsub-non-nums: 489744.8 i/s - 8.65x slower
scan nums: 418978.8 i/s - 10.11x slower
मुझे आश्चर्य हुआ कि एक सरणी को स्लाइस करना भी #gsub से तेज़ है और मुझे यह देखकर हमेशा खुशी होती है कि #split कितना तेज़ है। और ध्यान दें कि सभी गैर-संख्यात्मक को हटाना एक विशिष्ट सबस्ट्रिंग को हटाने से तेज़ है।
पैसे का पालन करें
आइए नंबर के बाद मुद्रा को हटाएं। (मैं धीमे #gsub संस्करणों को छोड़ रहा हूं।)
Benchmark.ips do |x|
x.config(time: 30, warmup: 2)
x.report('gsub') { string.gsub(/ USD/, '') }
x.report('tr') { string.tr(" USD", "") }
x.report('delete_chars') { string.delete("^0-9") }
x.report('delete_suffix') { string.delete_suffix(" USD") }
x.report('to_i.to_s') { string.to_i.to_s }
x.report("split") { string.split.first }
x.compare!
end
विजेताओं के बीच ड्रॉ है। आप किन 2 के सबसे तेज़ होने के लिए प्रतिस्पर्धा करने की उम्मीद करते हैं?
और: अनुमान लगाएं कि यहां `#gsub` _कितना_ धीमा है।
Comparison:
delete_suffix: 4354205.4 i/s
to_i.to_s: 4307614.6 i/s - same-ish: difference falls within error
split: 2870187.8 i/s - 1.52x slower
delete_chars: 1989566.1 i/s - 2.19x slower
tr: 1853957.1 i/s - 2.35x slower
gsub: 524080.6 i/s - 13.22x slower
हमेशा एक विशिष्ट विधि नहीं होती जो आपकी आवश्यकताओं के अनुरूप हो। आप #to_i का उपयोग नहीं कर सकते यदि आपको एक अग्रणी "0" रखने की आवश्यकता है। और #delete_suffix इस धारणा पर बहुत अधिक निर्भर करता है कि मुद्रा US डॉलर है।
विशिष्ट विधियां सटीक उपकरणों की तरह हैं—एक विशिष्ट संदर्भ में एक विशिष्ट कार्य के लिए उपयुक्त। इसलिए हमेशा ऐसे मामले होंगे जहां #gsub ठीक वही है जो हमें चाहिए। यह बहुमुखी है, और यह हमेशा दिमाग में सबसे ऊपर है। लेकिन इसे संसाधित करना थोड़ा कठिन हो सकता है और यह अक्सर धीमा होता है, यहां तक कि मेरी अपेक्षा से भी धीमा। मेरे लिए, Ruby की समृद्धि उन कारणों में से एक है जो इसे काम करने में इतना मजेदार बनाती है। गति की जीत एक अच्छा बोनस है।