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 +
?
Sie können dies auf verschiedene Arten tun:
<<
gezeigt haben, ist das nicht der übliche WegMit Stringinterpolation
source = "#{ROOT_DIR}/#{project}/App.config"
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.
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"
Wenn Sie nur Pfade verketten, können Sie die eigene File.join-Methode von Ruby verwenden.
source = File.join(ROOT_DIR, project, 'App.config')
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)
Da dies ein Pfad ist, würde ich wahrscheinlich Array und Join verwenden:
source = [ROOT_DIR, project, 'App.config'] * '/'
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.
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.
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)
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 <<
.
Hier sind weitere Möglichkeiten, dies zu tun:
"String1" + "String2"
"#{String1} #{String2}"
String1<<String2
Und so weiter ...
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"))
Sie können %
auch wie folgt verwenden:
source = "#{ROOT_DIR}/%s/App.config" % project
Dieser Ansatz funktioniert auch mit '
(einfaches) Anführungszeichen.
Sie können direkt in der String-Definition verketten:
nombre_apellido = "#{customer['first_name']} #{customer['last_name']} #{order_id}"
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.