De ce Ruby nu are un adevărat StringBuffer sau StringIO?

Am citit recent un frumos post pe utilizând StringIO în Ruby. Ceea ce autorul nu menționează însă este că StringIO este doar un "I." Nu există "O." Nu puteți face acest lucru , de exemplu:

s = StringIO.new
s << 'foo'
s << 'bar'
s.to_s
# => should be "foo\nbar"
# => really is ''`

Ruby are nevoie de un StringBuffer la fel ca Java. StringBuffers servesc două scopuri importante. În primul rând, vă permit să testați jumătate de ieșire din ceea ce face Rubin's StringIO. În al doilea rând, ele sunt utile pentru construirea de șiruri lungi din părți mici - ceva ce ne aduce aminte de noi, de multe ori, foarte lent.

Există un înlocuitor bun?

Este adevărat că Strings in ruby sunt mutable, dar asta nu înseamnă că trebuie să ne bazăm întotdeauna pe acea funcționalitate. Dacă stuff este mare, cerințele de performanță și de memorie ale acestui lucru, de exemplu, sunt foarte rele.

result = stuff.map(&:to_s).join(' ')

Modul "corect" de a face acest lucru în Java este:

result = StringBuffer.new("")
for(String s : stuff) {
  result.append(s);
}

Deși Java-ul meu este puțin ruginit.

0
fr hi bn
Exemplul de îmbinare a șirului nu este echivalent cu codul Java. Așa cum spui, șirurile ruby sunt mutabile, deci în ruby faci doar: stuff.inject ('') {| res, s | res << s.to_s} . Puteți să vă bazați în siguranță pe șirurile ruby care pot fi mutați, nu se va schimba, deoarece ar rupe orice aplicație ruby existentă.
adăugat autor Theo, sursa
Mega servitoare a fost șters ca daune colaterale de a scăpa de profanitate.
adăugat autor Andrew Grimm, sursa
Chiar nu înțeleg de ce StringIO nu are o metodă to_s. Este o clasă care administrează un șir, deci dacă doriți ca șirul să fie necesar să îl cereți în mod special. Ar trebui să aibă o metodă to_s, deoarece este convenția rubinie, dar nu. (Cineva mă poate corecta dacă mă înșel)
adăugat autor hcarreras, sursa
"Mega Maid?" N-am auzit niciodata de ea. Niciodată nu credeam niciodată în StringBuffers, dar le-am folosit mereu de frica că cineva mi-a văzut codul. Dar într-adevăr, chestiile astea se adaugă vreodată?
adăugat autor Dan Rosenstark, sursa
Probabil o referință "SpaceBalls".
adăugat autor Stephen Eilert, sursa
@Theo În ruby 3, literalul de șir ar fi imuabil. Cu toate acestea, putem folosi în continuare codul String.new sau + "" .
adăugat autor Franklin Yu, sursa

5 răspunsuri

Ei bine, un StringBuffer nu este la fel de necesar în Ruby, mai ales pentru că șirurile din ruby sunt mutabile ... deci poți construi un șir prin modificarea șirului existent în loc de a construi noi șiruri cu fiecare concat.

Ca o notă, puteți utiliza, de asemenea, o sintaxă de șir specială în care puteți construi un șir care face trimitere la alte variabile din cadrul șirului, ceea ce duce la construirea unor șiruri foarte lizibile. Considera:

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

Aceste șiruri pot conține și expresii, nu doar variabile ... cum ar fi:

str = "The count will be: #{count + 1}"
count = count + 1
0
adăugat
Asta este cu siguranță adevărat și este minunat pentru interpolări scurte. Este inutil pentru construirea de siruri de caractere lungi, cum ar fi paginile HTML. Vedeți en.wikipedia.org/wiki/Schlemiel_the_painter%27s_Algorithm
adăugat autor James A. Rosen, sursa
Sigur, dar pentru a construi pagini HTML, de ce să nu folosiți ceva construit pentru această funcție, cum ar fi HAML sau ERB?
adăugat autor Earl Jenkins, sursa
re: "Schlemiel algoritmul Pictorului" Dacă utilizați StringIO ca în cele de mai sus, critica lui Joel Spolsky nu se aplică. StringIO are un pointer caută ca un fișier. Nu este nevoie să recompuneți sfârșitul șirului de fiecare dată. Și pentru șiruri mai lungi poți folosi% {} sau o bibliotecă ca cele sugerate de Earl.
adăugat autor CJ., sursa

I looked at the ruby documentation for StringIO, and it looks like what you want is StringIO#string, not StringIO#to_s

Astfel, modificați codul dvs. la:

s = StringIO.new
s << 'foo'
s << 'bar'
s.string
0
adăugat

Exemplul dvs. funcționează în ruby - tocmai am încercat.

irb(main):001:0> require 'stringio'
=> true
irb(main):002:0> s = StringIO.new
=> #
irb(main):003:0> s << 'foo'
=> #
irb(main):004:0> s << 'bar'
=> #
irb(main):005:0> s.string
=> "foobar"

Unless I'm missing the reason you're using to_s - that just outputs the object id.

0
adăugat

I did some benchmarks and the fastest approach is using the String#<< method. Using StringIO is a little bit slower.

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)

Concatenarea șirurilor folosind metoda String # + este cea mai lentă abordare de mai multe ordini de mărime:

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

So I think the right answer is that the equivalent to Java's StringBuffer is simply using String#<< in Ruby.

0
adăugat
Ce se întâmplă dacă doriți să adăugați câteva caractere la un șir foarte lung? Cred că StringIO va fi mai rapid
adăugat autor nothing-special-here, sursa
Cale să-mi suflet mintea aici.
adăugat autor drewish, sursa
și ce se întâmplă atunci când rubinul face șiruri de caractere imuabile? Asemenea micro-optimizări sunt un iad la sfârșit.
adăugat autor akostadinov, sursa
Ce versiune de rubin a fost folosită pentru acest punct de referință, vă rog?
adăugat autor Jared Beck, sursa
Wow. Deci, acesta ar trebui să fie răspunsul corect, deoarece String-ul este cel mai rapid. Testat pe ruby ​​2.1.5 și aceleași rezultate.
adăugat autor Nikkolasg, sursa

Ca și alte obiecte de tip IO din Ruby, când scrieți la un IO, pointerul caracterului avansează.

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

Another way to skin this cat.

0
adăugat
Nu prea am nevoie de acest lucru, deoarece există StringIO # read , dar eu sunt întotdeauna un fan al cunoașterii mai mult decât un mod de a face ceva. +1
adăugat autor James A. Rosen, sursa
Ahem, am vrut să spun că StringIO # string
adăugat autor James A. Rosen, sursa