Ik ben op zoek naar een elegantere manier om snaren in Ruby samen te voegen.
Ik heb de volgende regel:
source = "#{ROOT_DIR}/" << project << "/App.config"
Is er een leukere manier om dit te doen?
En wat is trouwens het verschil tussen <<
en +
?
Antwoord 1, autoriteit 100%
Je kunt dat op verschillende manieren doen:
- Zoals je hebt laten zien met
<<
maar dat is niet de gebruikelijkemanier -
Met string-interpolatie
source = "#{ROOT_DIR}/#{project}/App.config"
-
met
+
source = "#{ROOT_DIR}/" + project + "/App.config"
De tweede methode lijkt efficiënter te zijn in termen van geheugen/snelheid van wat ik heb gezien (hoewel niet gemeten). Alle drie de methoden geven een niet-geïnitialiseerde constante fout wanneer ROOT_DIR nul is.
Als je met padnamen te maken hebt, wil je misschien File.join
gebruiken om te voorkomen dat je het scheidingsteken voor padnamen gebruikt.
Uiteindelijk is het een kwestie van smaak.
Antwoord 2, autoriteit 17%
De operator +
is de normale keuze voor aaneenschakeling en is waarschijnlijk de snelste manier om tekenreeksen samen te voegen.
Het verschil tussen +
en <<
is dat <<
het object aan de linkerkant verandert, en +
niet.
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"
Antwoord 3, autoriteit 13%
Als u alleen paden aaneenvoegt, kunt u Ruby’s eigen File.join-methode gebruiken.
source = File.join(ROOT_DIR, project, 'App.config')
Antwoord 4, autoriteit 5%
van http://greyblake.com/blog/2012/ 09/02/ruby-perfomance-tricks/
Het gebruik van <<
oftewel concat
is veel efficiënter dan +=
, aangezien de laatste een tijdelijk object maakt en de eerste overschrijft object met het nieuwe object.
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
uitvoer:
____________________________________________________________
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)
Antwoord 5, Autoriteit 2%
Aangezien dit een pad is, gebruik ik waarschijnlijk array en join:
source = [ROOT_DIR, project, 'App.config'] * '/'
Antwoord 6, Autoriteit 2%
Hier is een andere benchmark geïnspireerd op deze gist . Het vergelijkt aaneenheden (+
), toevoegen (<<
) en interpolatie (#{}
) voor dynamische en vooraf gedefinieerde snaren.
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
uitvoer:
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)
Conclusie: interpolatie in MRI is zwaar.
Antwoord 7
Ik gebruik liever Padnaam:
require 'pathname' # pathname is in stdlib
Pathname(ROOT_DIR) + project + 'App.config'
over <<
en +
van ruby docs:
+
: retourneert een nieuwetekenreeks met other_str samengevoegd tot str
<<
: voegt het gegeven object samen tot str. Als het object een Fixnum is tussen 0 en 255, wordt het geconverteerd naar een teken voordat het wordt samengevoegd.
het verschil zit dus in wat de eerste operand wordt (<<
brengt wijzigingen aan, +
geeft een nieuwe tekenreeks, zodat het geheugen zwaarder is) en wat als de eerste operand Fixnum is (<<
wordt toegevoegd alsof het een teken is met code gelijk aan dat getal, +
geeft een fout)
Antwoord 8
Laat me je al mijn ervaring daarmee laten zien.
Ik had een query die 32k aan records retourneerde, voor elk record heb ik een methode aangeroepen om dat databaserecord op te maken in een geformatteerde tekenreeks en dat vervolgens samen te voegen tot een tekenreeks die aan het einde van dit proces in een bestand zal veranderen in schijf.
Mijn probleem was dat volgens het record, rond de 24k, het proces van het aaneenschakelen van de String moeizaam verliep.
Ik deed dat met de gewone ‘+’-operator.
Toen ik veranderde naar de ‘<<‘ was als magie. Was erg snel.
Dus, ik herinnerde me mijn oude tijd – een soort van 1998 – toen ik Java gebruikte en String samenvoegde met ‘+’ en veranderde van String in StringBuffer (en nu hebben wij, Java-ontwikkelaars de StringBuilder).
Ik geloof dat het proces van + / << in de Ruby-wereld is hetzelfde als + / StringBuilder.append in de Java-wereld.
De eerste wijst het hele object in het geheugen opnieuw toe en de andere wijst gewoon naar een nieuw adres.
Antwoord 9
Aaneenschakeling zeg je? Hoe zit het dan met de #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 alle eerlijkheid, concat
is een alias als <<
.
Antwoord 10
Hier zijn meer manieren om dit te doen:
"String1" + "String2"
"#{String1} #{String2}"
String1<<String2
Enzovoort…
Antwoord 11
U kunt %
ook als volgt gebruiken:
source = "#{ROOT_DIR}/%s/App.config" % project
Deze aanpak werkt ook met '
(enkele) aanhalingstekens.
Antwoord 12
U kunt de operator +
of <<
gebruiken, maar in ruby heeft de functie .concat
de meeste voorkeur, aangezien veel sneller dan andere operators. Je kunt het gebruiken zoals.
source = "#{ROOT_DIR}/".concat(project.concat("/App.config"))
Antwoord 13
Situatie is belangrijk, bijvoorbeeld:
# 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
In het eerste voorbeeld zal het aaneenschakelen met de operator +
het object output
niet bijwerken, maar in het tweede voorbeeld zal de <<
operator zal het output
object bij elke iteratie updaten. Dus voor het bovenstaande type situatie is <<
beter.
Antwoord 14
Je kunt direct samenvoegen in de tekenreeksdefinitie:
nombre_apellido = "#{customer['first_name']} #{customer['last_name']} #{order_id}"
Antwoord 15
Voor uw specifieke geval kunt u ook Array#join
gebruiken bij het samenstellen van het bestandspadtype van tekenreeks:
string = [ROOT_DIR, project, 'App.config'].join('/')]
Dit heeft een prettig neveneffect van het automatisch converteren van verschillende typen naar string:
['foo', :bar, 1].join('/')
=>"foo/bar/1"
Antwoord 16
Voor Puppet:
$username = 'lala'
notify { "Hello ${username.capitalize}":
withpath => false,
}