390

Ruby で文字列を連結するよりエレガントな方法を探しています。

次の行があります。

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

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

<<さらに言えば、との違いは何+ですか?

4

16 に答える 16

606

いくつかの方法でそれを行うことができます:

  1. あなたが示したように、それは通常の方法<<ではありません
  2. 文字列補間あり

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

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

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

File.joinパス名を扱うときは、パス名の区切り文字を台無しにするのを避けるために を使用したい場合があります。

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

于 2008-12-18T13:09:06.457 に答える
104

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

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

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"
于 2008-12-18T15:50:08.363 に答える
80

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

source = File.join(ROOT_DIR, project, 'App.config')
于 2008-12-18T13:22:32.373 に答える
29

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)
于 2015-02-19T23:22:23.013 に答える
11

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

source = [ROOT_DIR, project, 'App.config'] * '/'
于 2008-12-18T13:14:23.000 に答える
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)

結論: MRI での補間は重い。

于 2016-09-15T09:13:35.390 に答える
7

パス名を使用することをお勧めします:

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

ruby docsについて<<とそこから:+

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

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

したがって、違いは最初のオペランドになるもの(その<<場で変更を行い、+新しい文字列を返すため、メモリが重くなります)と、最初のオペランドがFixnumの場合はどうなるか(その<<数値に等しいコードを持つ文字であるかのように追加され、発生します+エラー)

于 2009-12-26T06:27:20.067 に答える
6

それに関する私の経験をすべてお見せしましょう。

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

私の問題は、記録によると、約24kで、文字列を連結するプロセスが苦痛になったことです。

通常の「+」演算子を使用してそれを行っていました。

「<<」に変更したときは魔法のようでした。本当に速かった。

そのため、1998 年のような昔のことを思い出しました。Java を使用し、「+」を使用して文字列を連結し、文字列から StringBuffer に変更したときです (そして現在、Java 開発者は StringBuilder を持っています)。

Ruby の世界の + / << の処理は、Java の世界の + / StringBuilder.append と同じだと思います。

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

于 2012-08-03T20:11:33.363 に答える
5

これを行うには、さらに次の方法があります。

"String1" + "String2"

"#{String1} #{String2}"

String1<<String2

等々 ...

于 2013-03-06T20:11:41.827 に答える
0

Array#join特定のケースでは、ファイルパスタイプの文字列を構築するときにも使用できます。

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

これには、さまざまなタイプを文字列に自動的に変換するという嬉しい副作用があります。

['foo', :bar, 1].join('/')
=>"foo/bar/1"
于 2019-11-27T23:28:37.050 に答える