webentwicklung-frage-antwort-db.com.de

String-Verkettung in Ruby

Ich suche nach einer eleganteren Art, Strings in Ruby zu verketten.

Ich habe folgende Zeile:

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

Gibt es einen schöneren Weg, dies zu tun?

Und was ist der Unterschied zwischen << und +?

323
dagda1

Sie können dies auf verschiedene Arten tun:

  1. Wie Sie mit << gezeigt haben, ist das nicht der übliche Weg
  2. Mit Stringinterpolation

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

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

Die zweite Methode scheint in Bezug auf Speicher/Geschwindigkeit effizienter zu sein, was ich gesehen habe (obwohl nicht gemessen). Alle drei Methoden geben einen nicht initialisierten konstanten Fehler aus, wenn ROOT_DIR gleich Null ist.

Wenn Sie sich mit Pfadnamen befassen, sollten Sie File.join verwenden, um zu vermeiden, dass das Trennzeichen für den Pfadnamen verwendet wird.

Am Ende ist es Geschmackssache.

518
Keltia

Der +-Operator ist die normale Verkettungsoption und ist wahrscheinlich die schnellste Möglichkeit, Zeichenfolgen zu verketten.

Der Unterschied zwischen + und << besteht darin, dass << das Objekt auf der linken Seite ändert und + nicht.

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

Wenn Sie nur Pfade verketten, können Sie die eigene File.join-Methode von Ruby verwenden.

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

von http://greyblake.com/blog/2012/09/02/Ruby-perfomance-tricks/

Die Verwendung von << aka concat ist weitaus effizienter als +=, da letztere ein zeitliches Objekt erstellt und das erste Objekt mit dem neuen Objekt überschreibt.

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

ausgabe:

____________________________________________________________
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)
21
Danny

Da dies ein Pfad ist, würde ich wahrscheinlich Array und Join verwenden:

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

Hier ist ein weiterer Benchmarking von diesem Gist . Es vergleicht die Verkettung (+), das Anhängen (<<) und die Interpolation (#{}) für dynamische und vordefinierte Zeichenfolgen.

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

ausgabe:

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)

Schlussfolgerung: Die Interpolation in der MRT ist schwer.

7
Adobe

Lassen Sie mich all meine Erfahrungen damit zeigen.

Ich hatte eine Abfrage, die 32k Datensätze zurückgab. Für jeden Datensatz habe ich eine Methode aufgerufen, um diesen Datenbankdatensatz in einen formatierten String zu formatieren und diesen dann zu einem String zu verketten, der am Ende dieses Prozesses in eine Datei auf der Festplatte umgewandelt wird.

Mein Problem war, dass der Rekordverkettung des Strings nach 24k bei einem Rekord einen Schmerz auslöste.

Ich habe das mit dem regulären '+' Operator gemacht.

Als ich zum '<<' wechselte, war das wie ein Zauber. War echt schnell.

Also erinnerte ich mich an meine alten Zeiten - eine Art von 1998 -, als ich Java verwendete und String mit '+' verkettete und von String zu StringBuffer wechselte (und nun haben wir als Java-Entwickler den StringBuilder). 

Ich glaube, dass der Prozess von +/<< in der Ruby-Welt der gleiche ist wie +/StringBuilder.append in der Java-Welt. 

Die erste ordnen das gesamte Objekt im Speicher neu zu und die anderen zeigen einfach auf eine neue Adresse.

6
Marcio Mangar

Ich würde lieber Pfadname verwenden:

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

über << und + aus Ruby-Dokumenten:

+: Gibt einen new String zurück, der other_str enthält, der an str gebunden ist

<<: Verkettet das angegebene Objekt mit str. Wenn das Objekt ein Fixnum zwischen 0 und 255 ist, wird es vor der Verkettung in ein Zeichen konvertiert.

der Unterschied besteht also darin, was zum ersten Operanden wird (<< nimmt Änderungen vor, + gibt einen neuen String zurück, damit der Speicher schwerer wird) und was wird, wenn der erste Operand Fixnum ist (<< fügt sich hinzu, als ob es ein Zeichen wäre, dessen Code dieser Zahl entspricht , + wird Fehler auslösen)

6
tig

Verkettung, sagst du? Wie wäre es dann mit der #concat-Methode?

a = 'foo'
a.object_id #=> some number
a.concat 'bar' #=> foobar
a.object_id #=> same as before -- string a remains the same object

In aller Fairness ist concat alias als <<.

5
Boris Stitnicky

Hier sind weitere Möglichkeiten, dies zu tun:

"String1" + "String2"

"#{String1} #{String2}"

String1<<String2

Und so weiter ... 

5
Imran Alavi

Sie können den +- oder <<-Operator verwenden, aber in Ruby ist die .concat-Funktion am meisten zu bevorzugen, da sie viel schneller als andere Operatoren ist. Sie können es gerne verwenden.

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

Sie können % auch wie folgt verwenden:

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

Dieser Ansatz funktioniert auch mit ' (einfaches) Anführungszeichen.

1
Mark

Sie können direkt in der String-Definition verketten:

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

Situation zählt zum Beispiel:

# 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

Im ersten Beispiel wird durch das Verketten mit dem +-Operator das output-Objekt nicht aktualisiert. Im zweiten Beispiel aktualisiert der <<-Operator jedoch das output-Objekt bei jeder Iteration. Für die obige Situation ist << besser.

0
Affan Khan