String-Verkettung vs. Interpolation in Ruby

74

Ich fange gerade an, Ruby zu lernen (erstmaliges Programmieren) und habe eine grundlegende syntaktische Frage in Bezug auf Variablen und verschiedene Arten, Code zu schreiben.

Chris Pines "Learn to Program" brachte mir bei, ein grundlegendes Programm wie dieses zu schreiben ...

num_cars_again= 2
puts 'I own ' + num_cars_again.to_s + ' cars.'

Das ist in Ordnung, aber dann bin ich über das Tutorial auf ruby.learncodethehardway.com gestolpert und habe gelernt, genau das gleiche Programm wie dieses zu schreiben ...

num_cars= 2
puts "I own #{num_cars} cars."

Beide geben dasselbe aus, aber Option 2 ist offensichtlich ein viel kürzerer Weg, dies zu tun.

Gibt es einen bestimmten Grund, warum ich ein Format über dem anderen verwenden sollte?

Jeff H.
quelle
5
Pfui. Ich hasse es, wie oft Anfängerbücher Ihnen eine unnatürliche Art und Weise beibringen, Dinge zu tun, ohne Ihnen zumindest zu sagen, dass es Alternativen gibt. +1 für eine berechtigte Frage, die nicht positiv bewertet wurde.
Andrew Grimm
Es gibt weitere Optionen, die unter stackoverflow.com/questions/377768/…
Sameers

Antworten:

72

Wann immer TIMTOWTDI (es gibt mehr als einen Weg, dies zu tun), sollten Sie nach den Vor- und Nachteilen suchen. Verwenden von "String-Interpolation" (die zweite) anstelle von "String-Verkettung" (die erste):

Vorteile:

  • Ist weniger tippen
  • Ruft automatisch to_snach Ihnen
  • Idiomatischer in der Ruby-Community
  • Schneller zur Laufzeit

Nachteile:

  • Ruft automatisch to_snach Ihnen (vielleicht dachten Sie, Sie hätten eine Zeichenfolge, und die to_sDarstellung entspricht nicht Ihren Wünschen und verbirgt die Tatsache, dass es sich nicht um eine Zeichenfolge handelt).
  • Erfordert die Verwendung "zur Begrenzung Ihrer Zeichenfolge anstelle von '(möglicherweise haben Sie die Gewohnheit ', eine Zeichenfolge zu verwenden, oder Sie haben zuvor eine Zeichenfolge damit eingegeben und müssen sie erst später verwenden, um die Zeichenfolgeninterpolation zu verwenden)
Phrogz
quelle
24
Vergessen Sie nicht den Aspekt "es ist schneller". Die Verkettung von Zeichenfolgen muss in diesem Beispiel insgesamt 3 Zeichenfolgen erstellen, während die Zeichenfolgeninterpolation nur eine erstellt.
Dominik Honnef
4
Wenn Sie sich für Benchmarks interessieren, ist es auch schneller: Versuchen Sie mich in REPL
Mrlee
2
Vielen Dank für die Antworten. Schnelle Frage. Warum sollte Chris Pines Buch den längeren Weg dazu lehren? Vielleicht ist es für einen Anfänger besser, es länger zu lernen? Sein Buch sagt die meiste Zeit fauler = besser, also frage ich mich, ob ich es vielleicht aus irgendeinem Grund (da ich gerade lerne) auf seine Weise fortsetzen oder auf diese bessere Weise weitermachen sollte. Irgendwelche Ideen?
Jeff H.
6
Meine Vermutung: Weil "Anhängen von Zeichenfolgen mit einem bekannten Operator" für einen neuen Programmierer ein einfacheres Konzept ist als "Verwenden einer benutzerdefinierten Syntax, um beliebigen Code auszuwerten, to_sdas Ergebnis aufzurufen und es in die Mitte einer Zeichenfolge einzufügen" . Wenn man etwas Neues lernt, gibt es oft Variationen zwischen "der einfach zu verstehenden Art" und "der Art und Weise, wie die Profis es tun".
Phrogz
7
Ich weiß, dass ich zu spät zu dieser Diskussion komme, aber es gibt zwei Hauptgründe, warum ich es so gemacht habe, wie ich es getan habe. Erstens aus den Gründen, die Phrogz angibt: Ich habe versucht, es so einfach wie möglich zu halten, indem ich Konzepte verwendet habe, die sie bereits kannten. Ich habe in der ersten Ausgabe nicht einmal doppelt zitierte Saiten behandelt! Das Letzte, was jemand beim Programmieren lernen möchte, sind sechs verschiedene Syntaxen zum Erstellen von Zeichenfolgen. Zweitens wegen des Impliziten to_s. Ja, für diejenigen von uns, die Typen und Konvertierungen verstehen, ist dies eine Annehmlichkeit. Für einen neuen Programmierer ist es jedoch sehr wichtig, diese Konzepte zu verstehen.
Chris Pine
9

Sowohl Interpolation als auch Konkatination haben ihre eigenen Stärken und Schwächen. Im Folgenden habe ich einen Benchmark angegeben, der deutlich zeigt, wo die Konkatination und die Interpolation verwendet werden sollen.

require 'benchmark'

iterations = 1_00_000
firstname = 'soundarapandian'
middlename = 'rathinasamy'
lastname = 'arumugam'

puts 'With dynamic new strings'
puts '===================================================='
5.times do
  Benchmark.bm(10) do |benchmark|
    benchmark.report('concatination') do
      iterations.times do
        'Mr. ' + firstname + middlename + lastname + ' aka soundar'
      end
    end

    benchmark.report('interpolaton') do
      iterations.times do
        "Mr. #{firstname} #{middlename} #{lastname} aka soundar"
      end
    end
  end
  puts '--------------------------------------------------'
end

puts 'With predefined strings'
puts '===================================================='
5.times do
  Benchmark.bm(10) do |benchmark|
    benchmark.report('concatination') do
      iterations.times do
        firstname + middlename + lastname
      end
    end

    benchmark.report('interpolaton') do
      iterations.times do
        "#{firstname} #{middlename} #{lastname}"
      end
    end
  end
  puts '--------------------------------------------------'
end

Und unten ist das Benchmark-Ergebnis

Without predefined strings
====================================================
                 user     system      total        real
concatination  0.170000   0.000000   0.170000 (  0.165821)
interpolaton  0.130000   0.010000   0.140000 (  0.133665)
--------------------------------------------------
                 user     system      total        real
concatination  0.180000   0.000000   0.180000 (  0.180410)
interpolaton  0.120000   0.000000   0.120000 (  0.125051)
--------------------------------------------------
                 user     system      total        real
concatination  0.140000   0.000000   0.140000 (  0.134256)
interpolaton  0.110000   0.000000   0.110000 (  0.111427)
--------------------------------------------------
                 user     system      total        real
concatination  0.130000   0.000000   0.130000 (  0.132047)
interpolaton  0.120000   0.000000   0.120000 (  0.120443)
--------------------------------------------------
                 user     system      total        real
concatination  0.170000   0.000000   0.170000 (  0.170394)
interpolaton  0.150000   0.000000   0.150000 (  0.149601)
--------------------------------------------------
With predefined strings
====================================================
                 user     system      total        real
concatination  0.070000   0.000000   0.070000 (  0.067735)
interpolaton  0.100000   0.000000   0.100000 (  0.099335)
--------------------------------------------------
                 user     system      total        real
concatination  0.060000   0.000000   0.060000 (  0.061955)
interpolaton  0.130000   0.000000   0.130000 (  0.127011)
--------------------------------------------------
                 user     system      total        real
concatination  0.090000   0.000000   0.090000 (  0.092136)
interpolaton  0.110000   0.000000   0.110000 (  0.110224)
--------------------------------------------------
                 user     system      total        real
concatination  0.080000   0.000000   0.080000 (  0.077587)
interpolaton  0.110000   0.000000   0.110000 (  0.112975)
--------------------------------------------------
                 user     system      total        real
concatination  0.090000   0.000000   0.090000 (  0.088154)
interpolaton  0.140000   0.000000   0.140000 (  0.135349)
--------------------------------------------------

Fazit

Wenn Zeichenfolgen bereits definiert sind und sicher sind, dass sie niemals keine Konkatination verwenden, verwenden Sie die Interpolation. Verwenden Sie eine geeignete, die zu einer besseren Leistung führt als eine, die leicht einzurücken ist.

Soundar Rathinasamy
quelle
Welche Ruby-Version haben Sie verwendet?
La-comadreja
1
Ich habe es in Ruby 2.5.0 versucht und das interpolationist schneller als concatenationin beiden Fällen. Ich konnte hier aufgrund der Längenbeschränkung des Kommentars keine Ergebnisse einfügen, aber Sie können es selbst versuchen.
Peter T.
Ich bin mir nicht sicher , dass dies völlig fair, dass "#{firstname} #{middlename} #{lastname}"wahrscheinlich verglichen werden sollten firstname + " " + middlename + " " + lastname, nicht firstname + middlename + lastname(Concat auf 5 Strings vs. 3 Strings)
Mickalot
Beachten Sie, dass "#{firstname} #{middlename} #{lastname}"die concatinationInterpolation immer erheblich schneller ist , wenn ich Ihren Benchmark ändere, um sie vergleichbar zu machen (indem Sie entweder die inneren Leerzeichen in den Fällen entfernen oder Leerzeichen zu den Fällen hinzufügen ) (zumindest unter Verwendung von Ruby 2.6.3 unter Mac OS X).
Mickalot
4

@ user1181898 - IMHO, weil es einfacher ist zu sehen, was passiert. Bei @ Phrogz ruft die String-Interpolation automatisch die to_s für Sie auf. Als Anfänger müssen Sie sehen, was "unter der Haube" passiert, damit Sie das Konzept lernen, anstatt nur auswendig zu lernen.

Stellen Sie sich vor, Sie lernen Mathematik. Sie lernen den "langen" Weg, um die Konzepte zu verstehen, sodass Sie Verknüpfungen verwenden können, sobald Sie tatsächlich wissen, was Sie tun. Ich spreche aus Erfahrung b / c Ich bin noch nicht so fortgeschritten in Ruby, aber ich habe genug Fehler gemacht, um die Leute zu beraten, was sie nicht tun sollen. Hoffe das hilft.

Martin Graham
quelle
2

Wenn Sie eine Zeichenfolge als Puffer verwenden, habe ich festgestellt, dass die Verwendung von concatenation ( String#concat) schneller ist.

require 'benchmark/ips'

puts "Ruby #{RUBY_VERSION} at #{Time.now}"
puts

firstname = 'soundarapandian'
middlename = 'rathinasamy'
lastname = 'arumugam'

Benchmark.ips do |x|
    x.report("String\#<<") do |i|
        buffer = String.new

        while (i -= 1) > 0
            buffer << 'Mr. ' << firstname << middlename << lastname << ' aka soundar'
        end
    end

    x.report("String interpolate") do |i|
        buffer = String.new

        while (i -= 1) > 0
            buffer << "Mr. #{firstname} #{middlename} #{lastname} aka soundar"
        end
    end

    x.compare!
end

Ergebnisse:

Ruby 2.3.1 at 2016-11-15 15:03:57 +1300

Warming up --------------------------------------
           String#<<   230.615k i/100ms
  String interpolate   234.274k i/100ms
Calculating -------------------------------------
           String#<<      2.345M (± 7.2%) i/s -     11.761M in   5.041164s
  String interpolate      1.242M (± 5.4%) i/s -      6.325M in   5.108324s

Comparison:
           String#<<:  2344530.4 i/s
  String interpolate:  1241784.9 i/s - 1.89x  slower

Vermutlich würde die Interpolation eine temporäre Zeichenfolge erzeugen, weshalb sie langsamer ist.

ioquatix
quelle
0

Hier ist ein vollständiger Benchmark, der auch vergleicht Kernel#formatund String#+wie ich weiß, alle Methoden zur Konstruktion dynamischer Strings in Rubin 🤔

require 'benchmark/ips'

firstname = 'soundarapandian'
middlename = 'rathinasamy'
lastname = 'arumugam'

FORMAT_STR = 'Mr. %<firstname>s %<middlename>s %<lastname>s aka soundar'
Benchmark.ips do |x|
  x.report("String\#<<") do |i|
    str = String.new
    str << 'Mr. ' << firstname << ' ' << middlename << ' ' << lastname << ' aka soundar'
  end

  x.report "String\#+" do
    'Mr. ' + firstname + ' ' + middlename + ' ' + lastname + ' aka soundar'
  end

  x.report "format" do
    format(FORMAT_STR, firstname: firstname, middlename: middlename, lastname: lastname)
  end

  x.report("String interpolate") do |i|
    "Mr. #{firstname} #{middlename} #{lastname} aka soundar"
  end

  x.compare!
end

Und Ergebnisse für Ruby 2.6.5

Warming up --------------------------------------
           String#<<
    94.597k i/100ms
            String#+    75.512k i/100ms
              format    73.269k i/100ms
  String interpolate   164.005k i/100ms
Calculating -------------------------------------
           String#<<     91.385B (±16.9%) i/s -    315.981B
            String#+    905.389k (± 4.2%) i/s -      4.531M in   5.013725s
              format    865.746k (± 4.5%) i/s -      4.323M in   5.004103s
  String interpolate    161.694B (±11.3%) i/s -    503.542B

Comparison:
  String interpolate: 161693621120.0 i/s
           String#<<: 91385051886.2 i/s - 1.77x  slower
            String#+:   905388.7 i/s - 178590.27x  slower
              format:   865745.8 i/s - 186768.00x  slower
mpospelov
quelle