Rubyで文字列を連結するよりエレガントな方法を探しています。
次の行があります。
source = "#{ROOT_DIR}/" << project << "/App.config"
これを行うより良い方法はありますか?
そして、そのことについては何の違いである<<
とは+
?
Rubyで文字列を連結するよりエレガントな方法を探しています。
次の行があります。
source = "#{ROOT_DIR}/" << project << "/App.config"
これを行うより良い方法はありますか?
そして、そのことについては何の違いである<<
とは+
?
回答:
あなたはそれをいくつかの方法で行うことができます:
<<
、それは通常の方法ではありません文字列補間あり
source = "#{ROOT_DIR}/#{project}/App.config"
と +
source = "#{ROOT_DIR}/" + project + "/App.config"
2番目の方法は、私が見たもの(ただし測定されていません)から、メモリ/速度の点でより効率的です。ROOT_DIRがnilの場合、3つのメソッドすべてが初期化されていない定数エラーをスローします。
パス名を扱うときはFile.join
、パス名のセパレーターをめちゃくちゃにしないように使用したい場合があります。
結局、それは好みの問題です。
+
オペレータは、通常の連結上の選択であり、そしておそらく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"
+
と<<
ほぼ同じであることを行っています。多くの文字列、または本当に大きな文字列を扱っている場合、違いに気付くかもしれません。彼らのパフォーマンスがどれほど似ているかに驚いた。gist.github.com/2895311
5.times do ... end
インタープリターごとにテストスイートを数回(同じプロセスで-つまり、すべてをブロックでラップして)実行すると、より正確な結果が得られます。私のテストでは、補間がすべてのRubyインタープリターの中で最も高速な方法であることが示されています。私は<<
最速であると期待していましたが、それが私たちがベンチマークする理由です。
http://greyblake.com/blog/2012/09/02/ruby-perfomance-tricks/から
<<
aka を使用すると、が一時オブジェクトを作成し、最初のオブジェクトを新しいオブジェクトでオーバーライドする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)
これは、この要旨に触発された別のベンチマークです。動的文字列と事前定義文字列の連結(+
)、追加(<<
)、補間(#{}
)を比較します。
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の補間は重いです。
パス名を使用したい:
require 'pathname' # pathname is in stdlib
Pathname(ROOT_DIR) + project + 'App.config'
程度<<
と+
ルビーのドキュメントから:
+
:strに連結されたother_strを含む新しい文字列を返します
<<
:指定されたオブジェクトをstrに連結します。オブジェクトが0〜255のFixnumの場合、連結する前に文字に変換されます。
違いは、最初のオペランドになったものの中にあるので、(<<
場所の変更、なります+
(最初のオペランドがFixnumかの場合になりますし、何を返す新しい文字列それはメモリが重いように)<<
、それはコードの文字は、その数に等しかったかのように追加され、+
発生しますエラー)
Pathname('/home/foo') + '/etc/passwd' # => #<Pathname:/etc/passwd>
。これは、rubydocの例に基づいた仕様です。File.joinの方が安全であるようです。
(Pathname(ROOT_DIR) + project + 'App.config').to_s
、文字列オブジェクトを返したい場合にも呼び出す必要があります。
それについての私の経験をすべてお見せしましょう。
32kのレコードを返すクエリがありました。レコードごとに、データベースレコードをフォーマットされた文字列にフォーマットするメソッドを呼び出し、それを文字列に連結して、このすべてのプロセスの最後にディスク内のファイルに変換します。
私の問題は、記録によると、文字列を連結するプロセスが24k前後になるということでした。
私は通常の「+」演算子を使用してそれを行っていました。
私が「<<」に変わったとき、まるで魔法のようでした。本当に速かった。
それで、私はJavaを使用していて '+'を使用して文字列を連結し、StringからStringBufferに変更した(そして今、Java開発者はStringBuilderを使用している)昔のことを思い出しました。
Rubyの世界での+ / <<のプロセスは、Javaの世界での+ / StringBuilder.appendと同じだと思います。
最初はメモリ内のオブジェクト全体を再割り当てし、もう1つは新しいアドレスを指すだけです。
あなたが言う連結?それでは#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
はとしてエイリアス化され<<
ます。
"foo" "bar" 'baz" #=> "foobarabaz"
+
or <<
演算子を使用することもできますが、ruby .concat
関数は他の演算子よりもはるかに高速であるため、最も望ましい関数です。そのままお使いいただけます。
source = "#{ROOT_DIR}/".concat(project.concat("/App.config"))
.
あなたの最後concat
の後に余分があると思いますか?
状況の問題、例えば:
# 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
反復ごとにオブジェクトを更新します。したがって、上記のタイプの状況で<<
は、より良いです。
特定のケースArray#join
では、文字列のファイルパスタイプを作成するときにも使用できます。
string = [ROOT_DIR, project, 'App.config'].join('/')]
これには、さまざまな型を文字列に自動的に変換するという楽しい副作用があります。
['foo', :bar, 1].join('/')
=>"foo/bar/1"