रूबी में स्ट्रिंग संघनन


364

मैं रूबी में कंसट्रेटिंग स्ट्रिंग्स के अधिक सुरुचिपूर्ण तरीके की तलाश में हूं।

मेरी निम्न पंक्ति है:

source = "#{ROOT_DIR}/" << project << "/App.config"

क्या ऐसा करने का एक अच्छा तरीका है?

और उस बात के लिए बीच का अंतर क्या है <<और +?


3
यह प्रश्न stackoverflow.com/questions/4684446/… से संबंधित है।
नेत्र

<< यह समवर्ती करने के लिए अधिक कुशल तरीका है।
तैमूर चंगेज़

जवाबों:


574

आप इसे कई तरीकों से कर सकते हैं:

  1. जैसा कि आपने दिखाया <<लेकिन वह सामान्य तरीका नहीं है
  2. स्ट्रिंग प्रक्षेप के साथ

    source = "#{ROOT_DIR}/#{project}/App.config"
  3. साथ में +

    source = "#{ROOT_DIR}/" + project + "/App.config"

दूसरी विधि स्मृति / गति की अवधि से अधिक कुशल लगती है जो मैंने देखा है (हालांकि मापा नहीं गया है)। ROOT_DIR के शून्य होने पर सभी तीन विधियां एक अनइंस्टाल्यूटेड निरंतर त्रुटि को फेंक देगी।

जब पाथनाम के साथ काम करते हैं, तो आप File.joinपाथनाम विभाजक के साथ खिलवाड़ से बचने के लिए उपयोग करना चाह सकते हैं ।

अंत में, यह स्वाद का मामला है।


7
मैं रूबी के साथ बहुत अनुभवी नहीं हूं। लेकिन आम तौर पर ऐसे मामलों में जहां आप बहुत सारे स्ट्रिंग्स को बदलते हैं, आप अक्सर स्ट्रिंग को एक सरणी में जोड़कर प्रदर्शन प्राप्त कर सकते हैं और फिर अंत में स्ट्रिंग को एक साथ रखा जा सकता है। तब << उपयोगी हो सकता है?
PEZ

1
आपको मेमोरी को कॉपी करना होगा, वैसे भी इसमें लंबे स्ट्रिंग को जोड़ना होगा। << अधिक या कम समान है + सिवाय इसके कि आप << किसी एकल वर्ण के साथ <<
काल्टिया

9
<< किसी सरणी के तत्वों पर << का उपयोग करने के बजाय, Array # join का उपयोग करें, यह बहुत तेज़ है।
हचिन्स

94

+ऑपरेटर सामान्य संयोजन पसंद है, और शायद CONCATENATE तार करने के लिए सबसे तेज़ तरीका है।

के बीच का अंतर +और <<वह यह है कि <<अपने बाएं हाथ की ओर वस्तु बदलता है, और +नहीं करता है।

irb(main):001:0> s = 'a'
=> "a"
irb(main):002:0> s + 'b'
=> "ab"
irb(main):003:0> s
=> "a"
irb(main):004:0> s << 'b'
=> "ab"
irb(main):005:0> s
=> "ab"

32
+ ऑपरेटर निश्चित रूप से तारों को समतल करने का सबसे तेज़ तरीका नहीं है। हर बार जब आप इसका उपयोग करते हैं, तो यह एक प्रतिलिपि बनाता है, जबकि << जगह में समाप्‍त हो जाता है और बहुत अधिक प्रदर्शन करने वाला होता है।
ईविल ट्राउट

5
अधिकांश उपयोगों के लिए, प्रक्षेप, +और <<उसी के बारे में होने जा रहे हैं। यदि आप बहुत सारे तार, या वास्तव में बड़े लोगों के साथ काम कर रहे हैं, तो आप अंतर देख सकते हैं। मैं हैरान था कि उन्होंने कैसा प्रदर्शन किया। gist.github.com/2895311
मैट बर्क

8
जल्दी-जल्दी चलने वाले JVM ओवरलोड द्वारा आपके जुराब परिणामों को प्रक्षेप के विरुद्ध तिरछा कर दिया जाता है। यदि आप परीक्षण सूट को कई बार चलाते हैं (उसी प्रक्रिया में - तो 5.times do ... endप्रत्येक दुभाषिया के लिए सब कुछ एक ब्लॉक में लपेटें ), आप अधिक सटीक परिणामों के साथ समाप्त करेंगे। मेरे परीक्षण ने दिखाया है कि रूबी दुभाषियों के बीच प्रक्षेप सबसे तेज़ विधि है। मुझे उम्मीद <<है कि यह सबसे तेज होगा, लेकिन इसीलिए हम बेंचमार्क हैं।
वोमबेल

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

79

यदि आप सिर्फ पथ का उपयोग कर रहे हैं तो आप रूबी की अपनी File.join विधि का उपयोग कर सकते हैं।

source = File.join(ROOT_DIR, project, 'App.config')

5
ऐसा लगता है कि यह रास्ता तब से है जब माणिक विभिन्न पथ विभाजकों के साथ सिस्टम पर सही स्ट्रिंग बनाने का ध्यान रखेगा।
PEZ

26

से http://greyblake.com/blog/2012/09/02/ruby-perfomance-tricks/

<<उर्फ concatका उपयोग करना कहीं अधिक कुशल है +=, क्योंकि उत्तरार्द्ध एक लौकिक वस्तु बनाता है और नई वस्तु के साथ पहली वस्तु को ओवरराइड करता है।

require 'benchmark'

N = 1000
BASIC_LENGTH = 10

5.times do |factor|
  length = BASIC_LENGTH * (10 ** factor)
  puts "_" * 60 + "\nLENGTH: #{length}"

  Benchmark.bm(10, '+= VS <<') do |x|
    concat_report = x.report("+=")  do
      str1 = ""
      str2 = "s" * length
      N.times { str1 += str2 }
    end

    modify_report = x.report("<<")  do
      str1 = "s"
      str2 = "s" * length
      N.times { str1 << str2 }
    end

    [concat_report / modify_report]
  end
end

उत्पादन:

____________________________________________________________
LENGTH: 10
                 user     system      total        real
+=           0.000000   0.000000   0.000000 (  0.004671)
<<           0.000000   0.000000   0.000000 (  0.000176)
+= VS <<          NaN        NaN        NaN ( 26.508796)
____________________________________________________________
LENGTH: 100
                 user     system      total        real
+=           0.020000   0.000000   0.020000 (  0.022995)
<<           0.000000   0.000000   0.000000 (  0.000226)
+= VS <<          Inf        NaN        NaN (101.845829)
____________________________________________________________
LENGTH: 1000
                 user     system      total        real
+=           0.270000   0.120000   0.390000 (  0.390888)
<<           0.000000   0.000000   0.000000 (  0.001730)
+= VS <<          Inf        Inf        NaN (225.920077)
____________________________________________________________
LENGTH: 10000
                 user     system      total        real
+=           3.660000   1.570000   5.230000 (  5.233861)
<<           0.000000   0.010000   0.010000 (  0.015099)
+= VS <<          Inf 157.000000        NaN (346.629692)
____________________________________________________________
LENGTH: 100000
                 user     system      total        real
+=          31.270000  16.990000  48.260000 ( 48.328511)
<<           0.050000   0.050000   0.100000 (  0.105993)
+= VS <<   625.400000 339.800000        NaN (455.961373)

11

चूंकि यह एक ऐसा मार्ग है, जिसका उपयोग मैं संभवतः सरणी का उपयोग करूंगा और इसमें शामिल होऊंगा:

source = [ROOT_DIR, project, 'App.config'] * '/'

9

यहाँ एक और बेंचमार्क है जो इस जिस्ट से प्रेरित है । यह गतिशील और पूर्वनिर्धारित तारों के लिए संघनन ( +), संलग्न ( <<) और प्रक्षेप ( #{}) की तुलना करता है ।

require 'benchmark'

# we will need the CAPTION and FORMAT constants:
include Benchmark

count = 100_000


puts "Dynamic strings"

Benchmark.benchmark(CAPTION, 7, FORMAT) do |bm|
  bm.report("concat") { count.times { 11.to_s +  '/' +  12.to_s } }
  bm.report("append") { count.times { 11.to_s << '/' << 12.to_s } }
  bm.report("interp") { count.times { "#{11}/#{12}" } }
end


puts "\nPredefined strings"

s11 = "11"
s12 = "12"
Benchmark.benchmark(CAPTION, 7, FORMAT) do |bm|
  bm.report("concat") { count.times { s11 +  '/' +  s12 } }
  bm.report("append") { count.times { s11 << '/' << s12 } }
  bm.report("interp") { count.times { "#{s11}/#{s12}"   } }
end

उत्पादन:

Dynamic strings
              user     system      total        real
concat    0.050000   0.000000   0.050000 (  0.047770)
append    0.040000   0.000000   0.040000 (  0.042724)
interp    0.050000   0.000000   0.050000 (  0.051736)

Predefined strings
              user     system      total        real
concat    0.030000   0.000000   0.030000 (  0.024888)
append    0.020000   0.000000   0.020000 (  0.023373)
interp    3.160000   0.160000   3.320000 (  3.311253)

निष्कर्ष: एमआरआई में प्रक्षेप भारी है।


चूंकि तार अब अपरिवर्तनीय होने लगे हैं, इसलिए मुझे इसके लिए एक नया बेंचमार्क देखना अच्छा लगेगा।
बिबस्थ

7

मैं पथनाम का उपयोग करना पसंद करूंगा:

require 'pathname' # pathname is in stdlib
Pathname(ROOT_DIR) + project + 'App.config'

रूबी डॉक्स के बारे में <<और +:

+: एक नया लौटाता है स्ट्रिंग जिसमें str के साथ अन्य_स्ट्रैट सम्‍मिलित है

<<: दिए गए ऑब्जेक्ट को str से सम्‍मिलित करता है। यदि ऑब्जेक्ट 0 और 255 के बीच एक फ़िक्नम है, तो इसे कॉन्सेप्टेशन से पहले एक कैरेक्टर में बदल दिया जाता है।

इतना अंतर पहले ऑपरेंड में क्या होता है (<< स्थान में परिवर्तन करता है, +नई स्ट्रिंग लौटाता है इसलिए यह मेमोरी भारी होती है) और क्या होगा यदि पहला ऑपरेंड फिक्सनम है ( <<जैसे कि यह उस नंबर के बराबर कोड वाला वर्ण था, +बढ़ा देगा त्रुटि)


2
मैं बस पता चला कि '+' एक पथ नाम पर फोन खतरनाक है क्योंकि अगर आर्ग एक पूर्ण पथ है, रिसीवर पथ नजरअंदाज कर दिया है हो सकता है: Pathname('/home/foo') + '/etc/passwd' # => #<Pathname:/etc/passwd>। यह डिजाइन द्वारा है, रूबिडोक उदाहरण पर आधारित है। लगता है कि File.join सुरक्षित है।
केल्विन

(Pathname(ROOT_DIR) + project + 'App.config').to_sयदि आप एक स्ट्रिंग ऑब्जेक्ट वापस करना चाहते हैं तो आपको कॉल करने की भी आवश्यकता है ।
२०:२६ पर लैकोस्टेनकोडर

6

मुझे उस के साथ अपने सभी अनुभव दिखाते हैं।

मेरे पास एक क्वेरी थी जो 32k रिकॉर्ड्स को लौटाती थी, प्रत्येक रिकॉर्ड के लिए मैंने उस फॉर्मेट को रिकॉर्ड करने के लिए एक फॉर्मेटेड स्ट्रिंग में और कॉन्कैटनेट के बजाय एक स्ट्रिंग में फॉर्मेट करने के लिए कहा था कि इस सारी प्रक्रिया के अंत में डिस्क में एक फाइल में बदल जाता है।

मेरी समस्या यह थी कि रिकॉर्ड के अनुसार, 24k के आसपास, स्ट्रिंग को सुगम बनाने की प्रक्रिया दर्द में बदल गई।

मैं नियमित '+' ऑपरेटर का उपयोग कर रहा था।

जब मैं बदलकर '<<' जादू की तरह था। सचमुच तेज था।

इसलिए, मैंने अपने पुराने समय को याद किया - 1998 का ​​प्रकार - जब मैं जावा का उपयोग कर रहा था और स्ट्रिंग को '+' का उपयोग कर रहा था और स्ट्रिंग से स्ट्रिंगबफ़र में बदल गया (और अब हम, जावा डेवलपर के पास स्ट्रिंगब्यूलर है)।

मेरा मानना ​​है कि रूबी दुनिया में + / << की प्रक्रिया जावा दुनिया में + / StringBuilder.append जैसी ही है।

पहला पूरे ऑब्जेक्ट को मेमोरी में पुनः लोड करता है और दूसरा सिर्फ एक नए पते पर इंगित करता है।


5

कॉनटेनटेशन आप कहते हैं? #concatतो विधि के बारे में कैसे ?

a = 'foo'
a.object_id #=> some number
a.concat 'bar' #=> foobar
a.object_id #=> same as before -- string a remains the same object

सभी निष्पक्षता में, के concatरूप में अलियास है <<


7
दूसरों द्वारा उल्लेख नहीं किया गया है, और एक साथ तारों का एक और तरीका है, और वह केवल "foo" "bar" 'baz" #=> "foobarabaz"
रस है

दूसरों पर ध्यान दें: यह एक एकल उद्धरण नहीं माना जाता है, लेकिन बाकी की तरह एक डबल। नीट विधि!
जोशुआ पिंटर


2

आप %निम्नानुसार भी उपयोग कर सकते हैं :

source = "#{ROOT_DIR}/%s/App.config" % project

यह दृष्टिकोण '(एकल) उद्धरण चिह्न के साथ भी काम करता है ।


2

आप उपयोग +या <<संचालक कर सकते हैं , लेकिन रूबी .concatसमारोह में सबसे बेहतर है, क्योंकि यह अन्य ऑपरेटरों की तुलना में बहुत तेज है। आप इसका उपयोग कर सकते हैं।

source = "#{ROOT_DIR}/".concat(project.concat("/App.config"))

मुझे लगता है कि .आपके आखिरी concatनंबर के बाद आपके पास एक अतिरिक्त है ?
23

1

उदाहरण के लिए स्थिति,

# this will not work
output = ''

Users.all.each do |user|
  output + "#{user.email}\n"
end
# the output will be ''
puts output

# this will do the job
output = ''

Users.all.each do |user|
  output << "#{user.email}\n"
end
# will get the desired output
puts output

पहले उदाहरण में, +ऑपरेटर के साथ सहमति करने से outputऑब्जेक्ट अपडेट नहीं होगा , हालांकि, दूसरे उदाहरण में, <<ऑपरेटर outputप्रत्येक पुनरावृत्ति के साथ ऑब्जेक्ट को अपडेट करेगा । तो, उपरोक्त प्रकार की स्थिति के लिए, <<बेहतर है।


1

आप सीधे स्ट्रिंग परिभाषा में संक्षिप्त कर सकते हैं:

nombre_apellido = "#{customer['first_name']} #{customer['last_name']} #{order_id}"

0

अपने विशेष मामले के लिए आप Array#joinस्ट्रिंग के पथ पथ प्रकार का निर्माण करते समय भी उपयोग कर सकते हैं :

string = [ROOT_DIR, project, 'App.config'].join('/')]

यह विभिन्न प्रकारों को स्वचालित रूप से स्ट्रिंग में परिवर्तित करने का सुखद पक्ष प्रभाव रखता है:

['foo', :bar, 1].join('/')
=>"foo/bar/1"

0

कठपुतली के लिए:

$username = 'lala'
notify { "Hello ${username.capitalize}":
    withpath => false,
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.