Конкатенація рядків проти інтерполяції в Ruby


74

Я тільки починаю вивчати Ruby (перший раз програмування) і маю базове синтаксичне запитання щодо змінних та різних способів написання коду.

Кріс Пайн "Навчись програмувати" навчив мене писати таку базову програму ...

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

Це добре, але потім я натрапив на підручник на ruby.learncodethehardway.com і мене навчили писати ту саму точну програму, подібну до цієї ...

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

Вони обидва видають одне і те ж, але, очевидно, варіант 2 - це набагато коротший спосіб це зробити.

Чи є якась особлива причина, чому я повинен використовувати один формат над іншим?


5
Тьфу. Я ненавиджу, як часто книги для початківців навчають вас неприродному способу робити щось, принаймні кажучи вам, що існують альтернативи. +1 за законне запитання, яке не було проголосовано.
Ендрю Грімм,

Є кілька варіантів, які обговорюються на stackoverflow.com/questions/377768 / ...
sameers

Відповіді:


72

Щоразу, коли TIMTOWTDI (існує кілька способів це зробити), слід шукати плюси і мінуси. Використання "інтерполяції рядків" (друга) замість "об'єднання рядків" (перша):

Плюси:

  • Менше набирає текст
  • Автоматично телефонує to_sвам
  • Більш ідіоматичний у межах спільноти Ruby
  • Швидше досягнення під час роботи

Мінуси:

  • Автоматично телефонує to_sвам (можливо, ви думали, що у вас є рядок, і to_sподання не те, що ви хотіли, і приховує той факт, що це не був рядок)
  • Потрібно використовувати "для обмеження рядка замість '(можливо, у вас є звичка використовувати 'або ви раніше ввели рядок, використовуючи це, і лише пізніше потрібно було використовувати інтерполяцію рядків)

24
Не забувайте про аспект "це швидше". В цьому прикладі конкатенація рядків повинна створити 3 рядки, тоді як інтерполяція рядків створює лише одну.
Домінік Гоннеф,

4
якщо ви дбаєте про тести, це також швидше: спробуйте мене у
REPL

2
Велике спасибі за відповіді. Швидке запитання. Чому книга Кріса Пайн навчить довшого шляху до цього? Може, новачкові краще навчитися робити це довше? У його книзі сказано, що час більше лінивий = краще, тому мені цікаво, чи, можливо, з якихось причин (оскільки я тільки вчуся), мені слід продовжувати робити це по-своєму або рухатися вперед цим кращим способом. Будь-які ідеї?
Джефф Х.

6
Я здогадуюсь: оскільки "приєднання рядків разом за допомогою відомого оператора" - концепція для нового програміста простіша, ніж "використовувати власний синтаксис для оцінки довільного коду, виклику to_sрезультату та введення його в середину рядка" . Вивчаючи будь-яку нову річ, часто трапляються варіації «простого для розуміння шляху» проти «способу, яким це роблять професіонали».
Phrogz,

7
Я знаю, що я спізнився на цю дискусію, але є дві основні причини, чому я зробив це так, як і раніше. По-перше, з причин, які називає Фрогц: я намагався зробити це якомога простішим, використовуючи вже відомі їм поняття. Я навіть не висвітлював рядки з подвійними лапками у першому виданні! Останнє, що хтось хоче, навчаючись програмувати, - це шість різних синтаксисів для створення рядків. По-друге, через неявне to_s. Так, для тих, хто розуміє типи та перетворення, це зручність. Але для нового програміста дуже важливо зрозуміти ці поняття.
Кріс Пайн

9

І інтерполяція, і конкатинація мають свої сили та слабкості. Нижче я дав орієнтир, який чітко демонструє, де використовувати конкатинацію, а де інтерполяцію.

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

А нижче наведено результати Бенчмарку

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)
--------------------------------------------------

Висновок

Якщо рядки вже визначені і впевнені, що вони ніколи не будуть нульовими, використовуйте конкатинацію, інакше використовуйте інтерполяцію. Використовуйте відповідну, яка призведе до кращої продуктивності, ніж та, яку легко відступити.


яку версію Ruby ви використовували?
La-comadreja

1
Я спробував це в ruby ​​2.5.0, і interpolationце швидше, ніж concatenationв обох випадках. Я не зміг вставити результати сюди через обмеження довжини коментаря, але ви можете спробувати самі.
Пітер Т.

Я не впевнений, що це цілком справедливо, у цьому, "#{firstname} #{middlename} #{lastname}"мабуть, слід порівнювати firstname + " " + middlename + " " + lastname, а не firstname + middlename + lastname(concat на 5 струнах проти 3 струн)
Міккалот

Зверніть увагу, що коли я змінюю ваш орієнтир, щоб зробити їх порівнянними (або видаляючи внутрішні пробіли, "#{firstname} #{middlename} #{lastname}"або додаючи пробіли до concatinationвипадків), інтерполяція завжди значно швидша (принаймні, за допомогою Ruby 2.6.3 на Mac OSX).
Міккалот

4

@ user1181898 - IMHO, це тому, що легше бачити, що відбувається. До точки @ Phrogz, інтерполяція рядків автоматично викликає to_s для вас. Як новачкові, вам потрібно бачити, що відбувається «під капотом», щоб ви вивчили цю концепцію на відміну від простого навчання навколо.

Думайте про це як про вивчення математики. Ви вивчаєте "довгий" шлях для того, щоб зрозуміти поняття, щоб ви могли скористатися ярликами, коли ви насправді знаєте, що робите. Я говорю з досвіду б / к. Я ще не такий просунутий у Ruby, але я зробив достатньо помилок, щоб порадити людям, чого не можна робити. Сподіваюся, це допомагає.


2

Якщо ви використовуєте рядок як буфер, я виявив, що використання конкатенації ( String#concat) буде швидшим.

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

Результати:

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

Припустимо, я б сказав, що інтерполяція породжує тимчасовий рядок, тому вона повільніша.


0

Ось повний орієнтир, який також порівнює, Kernel#formatі String#+оскільки це всі методи побудови динамічного рядка в рубіні, які я знаю 🤔

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

І результати для 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.