Почему у Ruby нет настоящего StringBuffer или StringIO?

Недавно я прочитал хороший почтовый об использовании StringIO в Ruby. Однако автор не упоминает, что StringIO - это просто «я». Нет "О." Вы не могу делаете это, например:

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 немного ржавая.

"Мега горничная?" Никогда о ней не слышал. Никогда особо не верил в StringBuffers, но я всегда использовал их, опасаясь, что кто-то увидит мой код. Но действительно ли это когда-нибудь складывается?

Dan Rosenstark 06.05.2009 05:53

Наверное, отсылка к SpaceBalls.

Stephen Eilert 30.08.2010 20:59

Mega maid была удалена как побочный ущерб от избавления от ненормативной лексики.

Andrew Grimm 09.05.2011 07:52

Ваш пример соединения строк не эквивалентен коду Java. Как вы упомянули, строки Ruby изменяемы, поэтому в Ruby вы просто делаете: stuff.inject('') { |res, s| res << s.to_s }. Вы можете смело полагаться на то, что строки Ruby изменчивы, это не изменится, поскольку это нарушит все существующие приложения Ruby.

Theo 09.05.2011 09:01

Я действительно не понимаю, почему у StringIO нет метода to_s. Это класс, который управляет строкой, поэтому, если вам нужна эта строка, вы должны специально ее запросить. У него должен быть метод to_s, поскольку это соглашение ruby, но его нет. (Кто-то может меня поправить, если я ошибаюсь)

hcarreras 16.07.2014 20:10

@Theo В Ruby 3 строковые литералы были бы неизменяемыми. Однако мы все еще можем использовать изменяемый String.new или +''.

Franklin Yu 14.08.2016 08:45
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
55
6
27 662
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ну, StringBuffer не так необходим в Ruby, в основном потому, что строки в Ruby изменяемы ... таким образом, вы можете создать строку, изменив существующую строку вместо создания новых строк с каждым concat.

В качестве примечания вы также можете использовать специальный строковый синтаксис, в котором вы можете построить строку, которая ссылается на другие переменные внутри строки, что обеспечивает очень удобочитаемую конструкцию строки. Учитывать:

first = "Mike"
last = "Stone"
name = "#{first} #{last}"

Эти строки также могут содержать выражения, а не только переменные ... например:

str = "The count will be: #{count + 1}"
count = count + 1

Это, конечно, правда, и это отлично подходит для коротких интерполяций. Однако это плохо для создания длинных строк, таких как HTML-страницы. См. en.wikipedia.org/wiki/Schlemiel_the_painter%27s_Algorithm

James A. Rosen 09.06.2009 16:15

Конечно, но почему бы для создания HTML-страниц не использовать что-то, что построено для этой функции, например HAML или ERB?

Earl Jenkins 03.11.2011 02:23

re: «Алгоритм Шлемиля-художника». Если вы используете StringIO, как указано выше, критика Джоэла Спольски к вам неприменима. StringIO имеет указатель поиска, как и файл. Нет необходимости каждый раз пересчитывать конец строки. А для более длинных строк вы можете использовать% {} или библиотеку, подобную той, которую предложил Эрл.

CJ. 04.12.2013 09:38
Ответ принят как подходящий

Я посмотрел документацию по ruby ​​для StringIO, и похоже, что вам нужен StringIO#string, а не StringIO#to_s

Таким образом, измените свой код на:

s = StringIO.new
s << 'foo'
s << 'bar'
s.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 - это просто выводит идентификатор объекта.

Как и другие объекты типа ввода-вывода в Ruby, когда вы пишете в объект ввода-вывода, указатель символа перемещается.

>> s = StringIO.new
=> #<StringIO:0x3659d4>
>> s << 'foo'
=> #<StringIO:0x3659d4>
>> s << 'bar'
=> #<StringIO:0x3659d4>
>> s.pos
=> 6
>> s.rewind
=> 0
>> s.read
=> "foobar"

Я на самом деле не необходимость, потому что есть StringIO#read, но я всегда был поклонником знания более чем одного способа что-то сделать. +1

James A. Rosen 10.08.2009 00:36

Кхм, я имел ввиду StringIO#string

James A. Rosen 10.08.2009 00:36

Я провел несколько тестов, и самый быстрый подход - использовать метод 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)

Поэтому я думаю, что правильный ответ заключается в том, что эквивалент StringBuffer в Java просто использует String#<< в Ruby.

Какая версия Ruby использовалась для этого теста, пожалуйста?

Jared Beck 17.03.2014 07:03

Вау. Так что это должен быть правильный ответ, поскольку String самый быстрый. Проверено на Ruby 2.1.5, результаты такие же.

Nikkolasg 17.12.2014 21:05

и что происходит, когда рубин делает строки неизменными? Такие микрооптимизации в конце - это ад.

akostadinov 15.12.2016 19:39

Что произойдет, если вы захотите добавить несколько символов в очень длинную строку? Думаю StringIO тогда будет шустрее

nothing-special-here 13.02.2017 01:01

Решение для конкатенации строк не будет работать с замороженными строковыми литералами, но StringIO работает.

KARASZI István 15.03.2020 12:00

Другие вопросы по теме