Почему в Ruby нет настоящего StringBuffer или StringIO?
Вопрос
Недавно я прочитал хороший почта по использованию StringIO
в Руби.Но вот чего автор не упоминает, так это того, что StringIO
это просто "I." Там нет "О." Ты не мочь сделайте это, например:
s = StringIO.new
s << 'foo'
s << 'bar'
s.to_s
# => should be "foo\nbar"
# => really is ''`
Ruby действительно нужен StringBuffer, такой же, как тот, который есть в Java.StringBuffers служат двум важным целям.Во-первых, они позволяют вам протестировать выходную половину того, что делает Ruby StringIO.Во-вторых, они полезны для создания длинных струн из мелких частей — то, о чем Джоэл напоминает нам снова и снова, в противном случае происходит очень-очень медленно.
Есть ли хорошая замена?
Это правда, что строки в Ruby изменяемы, но это не значит, что мы всегда должны полагаться на эту функциональность.Если stuff
большой, требования к производительности и памяти здесь, например, очень плохие.
result = stuff.map(&:to_s).join(' ')
«Правильный» способ сделать это в Java:
result = StringBuffer.new("")
for(String s : stuff) {
result.append(s);
}
Хотя моя Java немного заржавела.
Решение
Я просмотрел документацию Ruby для StringIO
, и похоже, что вы хотите StringIO#string
, нет StringIO#to_s
Таким образом, измените свой код на:
s = StringIO.new
s << 'foo'
s << 'bar'
s.string
Другие советы
Как и другие объекты типа ввода-вывода в Ruby, когда вы записываете в объект ввода-вывода, указатель символа перемещается.
>> s = StringIO.new
=> #<StringIO:0x3659d4>
>> s << 'foo'
=> #<StringIO:0x3659d4>
>> s << 'bar'
=> #<StringIO:0x3659d4>
>> s.pos
=> 6
>> s.rewind
=> 0
>> s.read
=> "foobar"
Я провел несколько тестов, и самый быстрый подход — использовать String#<<
метод.С использованием StringIO
немного медленнее.
s = ""; Benchmark.measure{5000000.times{s << "some string"}}
=> 3.620000 0.100000 3.720000 ( 3.970463)
>> s = StringIO.new; Benchmark.measure{5000000.times{s << "some string"}}
=> 4.730000 0.120000 4.850000 ( 5.329215)
Объединение строк с помощью String#+
метод является самым медленным подходом на многие порядки:
s = ""; Benchmark.measure{10000.times{s = s + "some string"}}
=> 0.700000 0.560000 1.260000 ( 1.420272)
s = ""; Benchmark.measure{10000.times{s << "some string"}}
=> 0.000000 0.000000 0.000000 ( 0.005639)
Поэтому я думаю, что правильный ответ заключается в том, что эквивалент Java StringBuffer
просто использует String#<<
в Руби.
Ваш пример работает в Ruby — я только что попробовал.
irb(main):001:0> require 'stringio'
=> true
irb(main):002:0> s = StringIO.new
=> #<StringIO:0x2ced9a0>
irb(main):003:0> s << 'foo'
=> #<StringIO:0x2ced9a0>
irb(main):004:0> s << 'bar'
=> #<StringIO:0x2ced9a0>
irb(main):005:0> s.string
=> "foobar"
Если я не упускаю причину, по которой вы используете to_s - это просто выводит идентификатор объекта.
Что ж, StringBuffer не так уж необходим в Ruby, главным образом потому, что строки в Ruby изменяемы...таким образом, вы можете создать строку, изменив существующую строку, вместо того, чтобы создавать новые строки с каждым объединением.
Обратите внимание: вы также можете использовать специальный строковый синтаксис, в котором вы можете построить строку, которая ссылается на другие переменные внутри строки, что обеспечивает очень удобочитаемую конструкцию строки.Учитывать:
first = "Mike"
last = "Stone"
name = "#{first} #{last}"
Эти строки также могут содержать выражения, а не только переменные...такой как:
str = "The count will be: #{count + 1}"
count = count + 1