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
パス名を扱うときは、パス名の区切り文字を台無しにするのを避けるために を使用したい場合があります。
結局のところ、それは好みの問題です。
演算子は通常の+
連結の選択であり、おそらく文字列を連結するための最速の方法です。
+
との違いは、左側のオブジェクト<<
を変更することと変更しないことです。<<
+
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/から
<<
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)
これはパスなので、おそらく配列と結合を使用します。
source = [ROOT_DIR, project, 'App.config'] * '/'
この要点に触発された別のベンチマークを次に示します。動的文字列と定義済み文字列の連結 ( +
)、追加 ( <<
)、および補間 ( ) を比較します。#{}
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'
ruby docsについて<<
とそこから:+
+
: str に連結された other_str を含む新しい文字列を返します
<<
: 指定されたオブジェクトを str に連結します。オブジェクトが 0 ~ 255 の Fixnum である場合、連結前に文字に変換されます。
したがって、違いは最初のオペランドになるもの(その<<
場で変更を行い、+
新しい文字列を返すため、メモリが重くなります)と、最初のオペランドがFixnumの場合はどうなるか(その<<
数値に等しいコードを持つ文字であるかのように追加され、発生します+
エラー)
それに関する私の経験をすべてお見せしましょう。
32k のレコードを返すクエリがありました。レコードごとに、そのデータベース レコードをフォーマットされた文字列にフォーマットし、それを連結して、このすべてのプロセスの最後にディスク内のファイルになる文字列に連結するメソッドを呼び出しました。
私の問題は、記録によると、約24kで、文字列を連結するプロセスが苦痛になったことです。
通常の「+」演算子を使用してそれを行っていました。
「<<」に変更したときは魔法のようでした。本当に速かった。
そのため、1998 年のような昔のことを思い出しました。Java を使用し、「+」を使用して文字列を連結し、文字列から StringBuffer に変更したときです (そして現在、Java 開発者は StringBuilder を持っています)。
Ruby の世界の + / << の処理は、Java の世界の + / StringBuilder.append と同じだと思います。
最初のオブジェクトはメモリ内のオブジェクト全体を再割り当てし、もう 1 つは新しいアドレスを指すだけです。
これを行うには、さらに次の方法があります。
"String1" + "String2"
"#{String1} #{String2}"
String1<<String2
等々 ...
Array#join
特定のケースでは、ファイルパスタイプの文字列を構築するときにも使用できます。
string = [ROOT_DIR, project, 'App.config'].join('/')]
これには、さまざまなタイプを文字列に自動的に変換するという嬉しい副作用があります。
['foo', :bar, 1].join('/')
=>"foo/bar/1"