I would make that

^ String new: gifts size * 10 streamContents: [ :stream |
    gifts allButLastDo: [:each |
      stream nextPutAll: each; nextPutAll: ', '].
    stream nextPutAll: 'and '; nextPutAll: gifts last ]

Your iteration is well done, clear and to the point.

> On 17 May 2019, at 02:44, Richard O'Keefe <rao...@gmail.com> wrote:
> 
> stream := WriteStream on: (String new: gifts size * "estimated size per gift" 
> 10).
> "The number after #new: is the *initial* allocation; the stream will 
> reallocate it
>  as needed."
> gifts allButLastDo: [:each |
>    stream nextPutAll: each; nextPutAll: ', '].
> stream nextPutAll: 'and '; nextPutAll: gifts last.
> ^stream contents
> Using #allButLastDo: saves making a copy of (most of) gifts.
> How could you find out about these things?
> 
> You want to join some strings, so you look for a "join" method in String and 
> you
> will find
> 
>     join: aCollection
>       ^ self class new: (aCollection size * self size) streamContents: 
> [:stream |
>             aCollection   
>               do: [:each | stream nextPutAll: each asString] 
>               separatedBy: [stream nextPutAll: self]]
> 
> 
> You might wonder if there is already something close to what you want, so you
> might enter "commas" into Spotter.  If you did that, you would find
> Collection>>asCommaStringAnd so that the whole thing is very nearly
>     gifts asCommaStringAnd
> 
> Just as the concatenation selector #, works with most kinds of sequences,
> so building sequences up using a WriteStream works with most kinds of 
> sequences.
> 
> Let's work through a little example.  We are just going to build up the string
> 'Quick' one character at a time.
> s := ''.
> s := s , 'Q'.   "creates a new string holding 'Q'"
> s := s , 'u'.   "creates a new string holding 'Qu'"
> s := s , 'i'.   "creates a new string holding 'Qui'"
> s := s , 'c'.   "creates a new string holding 'Quic'"
> s := s , 'k'.   "creates a new string holding 'Quick'"
> 
> You see that building a string of n characters will actually create n new 
> strings,
> all but the last of which will be thrown away, taking O(n**2) time.
> 
> Now let's use a stream.
> w := WriteStream on: (String new: 4).  "Yes, I know that's too small."
> w nextPutAll: 'Q'. "The stream now holds 'Q...' in its buffer."
> w nextPutAll: 'u'. "The stream now holds 'Qu..' in its buffer."
> w nextPutAll: 'i'. "The stream now holds 'Qui.' in its buffer."
> w nextPutAll: 'c'. "The stream now holds 'Quic' in its buffer."
> s nextPutAll: 'k'. "There is no room left in the buffer, so the stream 
> allocates
>                     a new buffer twice the size and copies the old one into 
> it.
>                     Now it has 'Quic....' and it has room.
>                     The stream now holds 'Quick...' in its buffer."
> s := w contents.   "We are asking for the defined elements of the buffer.
>                     This means s := buffer copyFrom: 1 to: w position."
> 
> You see that building a string of n characters this way requires a minimum of
> two strings, the buffer and the final result.  The buffer may be periodically
> resized, but growing by doubling means the average cost is still O(n).
> 
> Let's time these to get an idea.
> Time millisecondsToRun: [
>       |s|
>       s := ''.
>       1 to: 10000 do: [:i |
>               s := s , i printString].
>       s size]
> => 124
> Time millisecondsToRun: [
>       |w|
>       w := WriteStream on: (String new: 10000).
>       1 to: 10000 do: [:i |
>               w nextPutAll: i printString].
>       w contents size]
> => 7
> 
> This is exactly the reason that Java has both String and StringBuilder.
> The tragedy of Java (well, not the only one) is that they had the example
> of Smalltalk before them, showing very very clearly that the best way to
> handle object to text conversion is to use #printOn: as the primitive,
> not #printString, and they *still* went ahead and did the worse thing.
> (Ruby has even less excuse for privileging to_s.)
> 
> There are quite a few books about Smalltalk available as free PDFs from
> the Pharo web site, a wonderful resource.  The Blue Book (Smalltalk-80
> The Language and its Implementation) describes streams in Chapter 12.
> 
> 
> On Fri, 17 May 2019 at 07:21, Roelof Wobben <r.wob...@home.nl> wrote:
> Hello, 
> 
> Im testing all my solutions with critiz and can solve almost all problems,
> 
> Only this one I cannot figure out. 
> 
> I have this code
> (gifts allButLast
>                 inject: ''
>                 into: [ :str :each | str , each , ', ' ]) , 'and ' , gifts 
> last ]
> 
> and critiz says I should use a stream .
> 
> How can I make this work ? 
> 
> Roelof
> 
> 


Reply via email to