Rubyで文字列を連結するためのもっとエレガントな方法を探しています。
次の行があります。
source = "#{ROOT_DIR}/" << project << "/App.config"
これを行うより良い方法はありますか?
それでは<<
と+
の違いは何ですか?
それにはいくつかの方法があります。
<<
で示したように、それは いつも wayではありません文字列補間あり
source = "#{ROOT_DIR}/#{project}/App.config"
+
と一緒に
source = "#{ROOT_DIR}/" + project + "/App.config"
2番目の方法は、私が見たものからメモリ/速度の面でより効率的であるようです(ただし測定されていません)。 ROOT_DIRがnilの場合、3つのメソッドすべてが未初期化定数エラーをスローします。
パス名を扱うときは、パス名の区切り文字を間違えないようにするためにFile.join
を使用することをお勧めします。
結局、それは好みの問題です。
+
演算子は通常の連結の選択であり、おそらく文字列を連結するための最速の方法です。
+
と<<
の違いは、<<
は左側のオブジェクトを変更し、+
は変更しないことです。
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"
パスを連結するだけの場合は、Ruby独自のFile.joinメソッドを使用できます。
source = File.join(ROOT_DIR, project, 'App.config')
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)
これはパスなので、おそらく配列と結合を使用します。
source = [ROOT_DIR, project, 'App.config'] * '/'
これは、 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における内挿は重い。
私のすべての経験をあなたに見せましょう。
32kのレコードを返すクエリがありました。レコードごとに、そのデータベースレコードをフォーマットされた文字列にフォーマットし、それを連結してこのプロセスの最後にディスク内のファイルに変換するメソッドを呼び出しました。
私の問題は、レコードが24kあたりになると、文字列を連結するプロセスが苦痛になったことです。
私は普通の '+'演算子を使ってそれをしていました。
私が '<<'に変わった時は魔法のようでした。本当に速かったです。
そのため、1998年のようにJavaを使用して「+」を使用してStringを連結し、StringからStringBufferに変更しました(そして今やJava開発者はStringBuilderを持っています)。
Rubyの世界での+/<<のプロセスは、Javaの世界での+/StringBuilder.appendと同じであると思います。
1つ目はメモリ内のオブジェクト全体を再割り当てし、もう1つは新しいアドレスを指すだけです。
パス名の使用を希望します:
require 'pathname' # pathname is in stdlib
Pathname(ROOT_DIR) + project + 'App.config'
Rubyドキュメントの<<
および+
について:
+
:strに連結されたother_strを含むnew文字列を返します
<<
:指定されたオブジェクトをstrに連結します。オブジェクトが0〜255のFixnumである場合、連結の前に文字に変換されます。
そのため、第1オペランドが何になるか(<<
が所定の位置に変更を加え、+
が新しい文字列を返すのでメモリが大きくなります)と第1オペランドがFixnumの場合(<<
その番号に等しいコードを持つ文字であるかのように追加すると、+
はエラーを発生させます)
連結あなたが言う? #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
は<<
としてエイリアスされています。
これを行うより多くの方法があります:
"String1" + "String2"
"#{String1} #{String2}"
String1<<String2
等々 ...
+
または<<
演算子を使用することもできますが、Rubyでは.concat
関数が最も望ましいものです。他の演算子よりもはるかに速いからです。あなたはそれを好きに使うことができます。
source = "#{ROOT_DIR}/".concat(project.concat.("/App.config"))
以下のように%
を使用することもできます。
source = "#{ROOT_DIR}/%s/App.config" % project
このアプローチは'
(単一)引用符でも同様に機能します。
文字列定義で直接連結することができます。
nombre_apellido = "#{customer['first_name']} #{customer['last_name']} #{order_id}"
状況の問題、たとえば
# 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
オブジェクトを更新します。したがって、上記のような状況では<<
が優れています。