Aaneenschakeling van tekenreeksen in Ruby

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:

  1. Zoals je hebt laten zien met <<maar dat is niet de gebruikelijkemanier
  2. Met string-interpolatie

    source = "#{ROOT_DIR}/#{project}/App.config"
    
  3. 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.joingebruiken 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 concatis 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 #concatmethode?

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, concatis 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 .concatde 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 outputniet bijwerken, maar in het tweede voorbeeld zal de <<operator zal het outputobject 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#joingebruiken 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,
}

Other episodes