web-dev-qa-db-ja.com

Rubyでの文字列連結

Rubyで文字列を連結するためのもっとエレガントな方法を探しています。

次の行があります。

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

これを行うより良い方法はありますか?

それでは<<+の違いは何ですか?

334
dagda1

それにはいくつかの方法があります。

  1. <<で示したように、それは いつも wayではありません
  2. 文字列補間あり

    source = "#{ROOT_DIR}/#{project}/App.config"
    
  3. +と一緒に

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

2番目の方法は、私が見たものからメモリ/速度の面でより効率的であるようです(ただし測定されていません)。 ROOT_DIRがnilの場合、3つのメソッドすべてが未初期化定数エラーをスローします。

パス名を扱うときは、パス名の区切り文字を間違えないようにするためにFile.joinを使用することをお勧めします。

結局、それは好みの問題です。

527
Keltia

+演算子は通常の連結の選択であり、おそらく文字列を連結するための最速の方法です。

+<<の違いは、<<は左側のオブジェクトを変更し、+は変更しないことです。

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"
90
Matt Burke

パスを連結するだけの場合は、Ruby独自のFile.joinメソッドを使用できます。

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

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)
22
Danny

これはパスなので、おそらく配列と結合を使用します。

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

これは、 this Gist からヒントを得た別のベンチマークです。動的ストリングと事前定義ストリングについて、連結(+)、追加(<<)、および補間(#{})を比較します。

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)

結論:MRIにおける内挿は重い。

7
Adobe

私のすべての経験をあなたに見せましょう。

32kのレコードを返すクエリがありました。レコードごとに、そのデータベースレコードをフォーマットされた文字列にフォーマットし、それを連結してこのプロセスの最後にディスク内のファイルに変換するメソッドを呼び出しました。

私の問題は、レコードが24kあたりになると、文字列を連結するプロセスが苦痛になったことです。

私は普通の '+'演算子を使ってそれをしていました。

私が '<<'に変わった時は魔法のようでした。本当に速かったです。

そのため、1998年のようにJavaを使用して「+」を使用してStringを連結し、StringからStringBufferに変更しました(そして今やJava開発者はStringBuilderを持っています)。

Rubyの世界での+/<<のプロセスは、Javaの世界での+/StringBuilder.appendと同じであると思います。

1つ目はメモリ内のオブジェクト全体を再割り当てし、もう1つは新しいアドレスを指すだけです。

6
Marcio Mangar

パス名の使用を希望します:

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

Rubyドキュメントの<<および+について:

+:strに連結されたother_strを含むnew文字列を返します

<<:指定されたオブジェクトをstrに連結します。オブジェクトが0〜255のFixnumである場合、連結の前に文字に変換されます。

そのため、第1オペランドが何になるか(<<が所定の位置に変更を加え、+が新しい文字列を返すのでメモリが大きくなります)と第1オペランドがFixnumの場合(<<その番号に等しいコードを持つ文字であるかのように追加すると、+はエラーを発生させます)

6
tig

連結あなたが言う? #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<<としてエイリアスされています。

5
Boris Stitnicky

これを行うより多くの方法があります:

"String1" + "String2"

"#{String1} #{String2}"

String1<<String2

等々 ...

5
Imran Alavi

+または<<演算子を使用することもできますが、Rubyでは.concat関数が最も望ましいものです。他の演算子よりもはるかに速いからです。あなたはそれを好きに使うことができます。

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

以下のように%を使用することもできます。

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

このアプローチは'(単一)引用符でも同様に機能します。

1
Mark

文字列定義で直接連結することができます。

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

状況の問題、たとえば

# 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オブジェクトは更新されませんが、2番目の例では、<<演算子は反復ごとにoutputオブジェクトを更新します。したがって、上記のような状況では<<が優れています。

0
Affan Khan