Sto cercando un modo più elegante di concatenare le stringhe in Ruby.
Ho la seguente riga:
source = "#{ROOT_DIR}/" << project << "/App.config"
C'è un modo migliore per farlo?
E del resto qual è la differenza tra <<
e +
?
Sto cercando un modo più elegante di concatenare le stringhe in Ruby.
Ho la seguente riga:
source = "#{ROOT_DIR}/" << project << "/App.config"
C'è un modo migliore per farlo?
E del resto qual è la differenza tra <<
e +
?
Risposte:
Puoi farlo in diversi modi:
<<
ma che non è il solito modoCon interpolazione di stringhe
source = "#{ROOT_DIR}/#{project}/App.config"
con +
source = "#{ROOT_DIR}/" + project + "/App.config"
Il secondo metodo sembra essere più efficiente in termini di memoria / velocità rispetto a quello che ho visto (non misurato però). Tutti e tre i metodi genereranno un errore costante non inizializzato quando ROOT_DIR è zero.
Quando hai a che fare con i nomi dei percorsi, potresti voler usare File.join
per evitare di sbagliare con il separatore dei nomi di percorso.
Alla fine, è una questione di gusti.
L' +
operatore è la normale scelta di concatenazione ed è probabilmente il modo più veloce per concatenare le stringhe.
La differenza tra +
e <<
è che <<
cambia l'oggetto sul lato sinistro e +
non lo fa.
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"
+
e <<
saranno più o meno gli stessi. Se hai a che fare con molte stringhe, o molto grandi, potresti notare una differenza. Sono stato sorpreso da quanto simili si sono esibiti. gist.github.com/2895311
5.times do ... end
blocco) per ciascun interprete, otterrai risultati più accurati. I miei test hanno dimostrato che l'interpolazione è il metodo più veloce, tra tutti gli interpreti di Ruby. Mi sarei aspettato <<
di essere il più veloce, ma è per questo che facciamo un benchmark.
Se stai solo concatenando percorsi puoi usare il metodo File.join di Ruby.
source = File.join(ROOT_DIR, project, 'App.config')
da http://greyblake.com/blog/2012/09/02/ruby-perfomance-tricks/
L'uso di <<
aka concat
è molto più efficiente di +=
, poiché quest'ultimo crea un oggetto temporale e sovrascrive il primo oggetto con il nuovo oggetto.
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
produzione:
____________________________________________________________
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)
Dato che questo è un percorso, probabilmente userò array e join:
source = [ROOT_DIR, project, 'App.config'] * '/'
Ecco un altro punto di riferimento ispirato a questa sostanza . Confronta concatenazione ( +
), aggiunta ( <<
) e interpolazione ( #{}
) per stringhe dinamiche e predefinite.
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
produzione:
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)
Conclusione: l'interpolazione nella risonanza magnetica è pesante.
Preferirei usare Pathname:
require 'pathname' # pathname is in stdlib
Pathname(ROOT_DIR) + project + 'App.config'
su <<
e +
dai documenti rubini:
+
: Restituisce una nuova stringa contenente other_str concatenato a str
<<
: Concatena l'oggetto dato a str. Se l'oggetto è un Fixnum tra 0 e 255, viene convertito in un carattere prima della concatenazione.
quindi la differenza è in ciò che diventa il primo operando ( <<
apporta modifiche al posto, +
restituisce una nuova stringa in modo che sia memoria più pesante) e cosa sarà se il primo operando è Fixnum ( <<
aggiungerà come se fosse un carattere con codice uguale a quel numero, +
aumenterà errore)
Pathname('/home/foo') + '/etc/passwd' # => #<Pathname:/etc/passwd>
. Questo è di progettazione, basato sull'esempio di rubydoc. Sembra che File.join sia più sicuro.
(Pathname(ROOT_DIR) + project + 'App.config').to_s
se si desidera restituire un oggetto stringa.
Lascia che ti mostri tutta la mia esperienza con quello.
Ho avuto una query che ha restituito 32k di record, per ogni record ho chiamato un metodo per formattare il record del database in una stringa formattata e poi concatenarlo in una stringa che alla fine di tutto questo processo si trasformerà in un file sul disco.
Il mio problema era che, per la cronaca, il processo di concatenazione della stringa ha provocato un dolore.
Lo stavo facendo usando il normale operatore '+'.
Quando sono passato a '<<' era come per magia. È stato davvero veloce.
Quindi, mi sono ricordato dei miei vecchi tempi - una specie di 1998 - quando stavo usando Java e concatenando String usando '+' e sono passato da String a StringBuffer (e ora noi sviluppatori Java abbiamo StringBuilder).
Credo che il processo di + / << nel mondo Ruby sia lo stesso di + / StringBuilder.append nel mondo Java.
Il primo rialloca l'intero oggetto in memoria e l'altro punta semplicemente a un nuovo indirizzo.
Concatenazione dici? Che ne dici di #concat
metodo allora?
a = 'foo'
a.object_id #=> some number
a.concat 'bar' #=> foobar
a.object_id #=> same as before -- string a remains the same object
In tutta onestà, concat
è alias come <<
.
"foo" "bar" 'baz" #=> "foobarabaz"
Ecco altri modi per farlo:
"String1" + "String2"
"#{String1} #{String2}"
String1<<String2
E così via ...
È possibile utilizzare +
o l' <<
operatore, ma nella .concat
funzione rubino è la più preferibile, poiché è molto più veloce di altri operatori. Puoi usarlo come.
source = "#{ROOT_DIR}/".concat(project.concat("/App.config"))
.
dopo l'ultimo concat
no?
La situazione è importante, ad esempio:
# 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
Nel primo esempio, la concatenazione con l' +
operatore non aggiornerà l' output
oggetto, tuttavia, nel secondo esempio, l' <<
operatore aggiornerà l' output
oggetto ad ogni iterazione. Quindi, per il tipo di situazione di cui sopra, <<
è meglio.
Per il tuo caso particolare puoi usare anche Array#join
quando costruisci il tipo di stringa del percorso del file:
string = [ROOT_DIR, project, 'App.config'].join('/')]
Ciò ha un piacevole effetto collaterale di convertire automaticamente diversi tipi in stringa:
['foo', :bar, 1].join('/')
=>"foo/bar/1"