can I do this with a stream or a format

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
13 messages Options
Reply | Threaded
Open this post in threaded view
|

can I do this with a stream or a format

Roelof Wobben
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 | Threaded
Open this post in threaded view
|

Re: can I do this with a stream or a format

Esteban A. Maringolo
Maybe this is a better way to build what you want.
String streamContents: [:stream |
  gifts 
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ]
]

Esteban A. Maringolo


On Thu, May 16, 2019 at 4:21 PM Roelof Wobben <[hidden email]> 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 | Threaded
Open this post in threaded view
|

Re: can I do this with a stream or a format

Roelof Wobben
oke, and then do something like :

stream :=
String streamContents: [:stream |
  gifts 
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ]
]

stream nextputAll: 'all' 
stream nextPut: ','
stream nextPut: gifts last


Roelof


Op 16-5-2019 om 21:30 schreef Esteban Maringolo:
Maybe this is a better way to build what you want.
String streamContents: [:stream |
  gifts 
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ]
]

Esteban A. Maringolo


On Thu, May 16, 2019 at 4:21 PM Roelof Wobben <[hidden email]> 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 | Threaded
Open this post in threaded view
|

Re: can I do this with a stream or a format

Roelof Wobben
I think this is better

 ^  String streamContents: [:stream |
 gifts 
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ]
  stream nextputAll: 'all'; nextPut: ','; nextPut: gifts last] 


Op 16-5-2019 om 21:47 schreef Roelof Wobben:
oke, and then do something like :

stream :=
String streamContents: [:stream |
  gifts 
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ]
     
stream nextputAll: 'all'; nextPut: ','; nextPut: gifts last
]

stream nextputAll: 'all' 
stream nextPut: ','
stream nextPut: gifts last


Roelof


Op 16-5-2019 om 21:30 schreef Esteban Maringolo:
Maybe this is a better way to build what you want.
String streamContents: [:stream |
  gifts 
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ]
]

Esteban A. Maringolo


On Thu, May 16, 2019 at 4:21 PM Roelof Wobben <[hidden email]> 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 | Threaded
Open this post in threaded view
|

Re: can I do this with a stream or a format

Roelof Wobben
nope, it takes also the last one and that schould not be used.


Op 16-5-2019 om 21:51 schreef Roelof Wobben:
I think this is better

 ^  String streamContents: [:stream |
 gifts 
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ]
  stream nextputAll: 'all'; nextPut: ','; nextPut: gifts last] 


Op 16-5-2019 om 21:47 schreef Roelof Wobben:
oke, and then do something like :

stream :=
String streamContents: [:stream |
  gifts 
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ]
     
stream nextputAll: 'all'; nextPut: ','; nextPut: gifts last
]

stream nextputAll: 'all' 
stream nextPut: ','
stream nextPut: gifts last


Roelof


Op 16-5-2019 om 21:30 schreef Esteban Maringolo:
Maybe this is a better way to build what you want.
String streamContents: [:stream |
  gifts 
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ]
]

Esteban A. Maringolo


On Thu, May 16, 2019 at 4:21 PM Roelof Wobben <[hidden email]> 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 | Threaded
Open this post in threaded view
|

Re: can I do this with a stream or a format

Esteban A. Maringolo
In reply to this post by Roelof Wobben
Sorry, I misses the "all" part at the end (I don't see how that could add something, but if it's in the requirements...)

Then it should be like this:
String streamContents: [:stream |
  gifts allButLast
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ].
  stream 
    nextPutAll: ' all, ';
    nextPutAll: gifts last].


Esteban A. Maringolo


On Thu, May 16, 2019 at 4:52 PM Roelof Wobben <[hidden email]> wrote:
I think this is better

 ^  String streamContents: [:stream |
 gifts 
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ]
  stream nextputAll: 'all'; nextPut: ','; nextPut: gifts last] 


Op 16-5-2019 om 21:47 schreef Roelof Wobben:
oke, and then do something like :

stream :=
String streamContents: [:stream |
  gifts 
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ]
     
stream nextputAll: 'all'; nextPut: ','; nextPut: gifts last
]

stream nextputAll: 'all' 
stream nextPut: ','
stream nextPut: gifts last


Roelof


Op 16-5-2019 om 21:30 schreef Esteban Maringolo:
Maybe this is a better way to build what you want.
String streamContents: [:stream |
  gifts 
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ]
]

Esteban A. Maringolo


On Thu, May 16, 2019 at 4:21 PM Roelof Wobben <[hidden email]> 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 | Threaded
Open this post in threaded view
|

Re: can I do this with a stream or a format

Roelof Wobben
In reply to this post by Roelof Wobben
and this is working

String
                streamContents: [ :stream |
                    gifts allButLast
                        do: [ :each | stream nextPutAll: each ]
                        separatedBy: [ stream
                                nextPut: $,;
                                space ].
                    stream
                        nextPut: $,;
                        nextPutAll: ' and ';
                        nextPutAll: gifts last ] ]

Thanks Estaban for the hint.

Roelof



Op 16-5-2019 om 21:51 schreef Roelof Wobben:
I think this is better

 ^  String streamContents: [:stream |
 gifts 
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ]
  stream nextputAll: 'all'; nextPut: ','; nextPut: gifts last] 


Op 16-5-2019 om 21:47 schreef Roelof Wobben:
oke, and then do something like :

stream :=
String streamContents: [:stream |
  gifts 
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ]
     
stream nextputAll: 'all'; nextPut: ','; nextPut: gifts last
]

stream nextputAll: 'all' 
stream nextPut: ','
stream nextPut: gifts last


Roelof


Op 16-5-2019 om 21:30 schreef Esteban Maringolo:
Maybe this is a better way to build what you want.
String streamContents: [:stream |
  gifts 
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ]
]

Esteban A. Maringolo


On Thu, May 16, 2019 at 4:21 PM Roelof Wobben <[hidden email]> 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 | Threaded
Open this post in threaded view
|

Re: can I do this with a stream or a format

Esteban A. Maringolo
Oh... the expected output was 'e1, e2, e3 and e4', I read "all" instead of "and".

Now it makes sense.

Esteban A. Maringolo


On Thu, May 16, 2019 at 5:09 PM Roelof Wobben <[hidden email]> wrote:
and this is working

String
                streamContents: [ :stream |
                    gifts allButLast
                        do: [ :each | stream nextPutAll: each ]
                        separatedBy: [ stream
                                nextPut: $,;
                                space ].
                    stream
                        nextPut: $,;
                        nextPutAll: ' and ';
                        nextPutAll: gifts last ] ]

Thanks Estaban for the hint.

Roelof



Op 16-5-2019 om 21:51 schreef Roelof Wobben:
I think this is better

 ^  String streamContents: [:stream |
 gifts 
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ]
  stream nextputAll: 'all'; nextPut: ','; nextPut: gifts last] 


Op 16-5-2019 om 21:47 schreef Roelof Wobben:
oke, and then do something like :

stream :=
String streamContents: [:stream |
  gifts 
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ]
     
stream nextputAll: 'all'; nextPut: ','; nextPut: gifts last
]

stream nextputAll: 'all' 
stream nextPut: ','
stream nextPut: gifts last


Roelof


Op 16-5-2019 om 21:30 schreef Esteban Maringolo:
Maybe this is a better way to build what you want.
String streamContents: [:stream |
  gifts 
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ]
]

Esteban A. Maringolo


On Thu, May 16, 2019 at 4:21 PM Roelof Wobben <[hidden email]> 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 | Threaded
Open this post in threaded view
|

Re: can I do this with a stream or a format

Roelof Wobben
I made the error somehow.
Sorry for the confusion.

Roelof


Op 16-5-2019 om 22:29 schreef Esteban Maringolo:
Oh... the expected output was 'e1, e2, e3 and e4', I read "all" instead of "and".

Now it makes sense.

Esteban A. Maringolo


On Thu, May 16, 2019 at 5:09 PM Roelof Wobben <[hidden email]> wrote:
and this is working

String
                streamContents: [ :stream |
                    gifts allButLast
                        do: [ :each | stream nextPutAll: each ]
                        separatedBy: [ stream
                                nextPut: $,;
                                space ].
                    stream
                        nextPut: $,;
                        nextPutAll: ' and ';
                        nextPutAll: gifts last ] ]

Thanks Estaban for the hint.

Roelof



Op 16-5-2019 om 21:51 schreef Roelof Wobben:
I think this is better

 ^  String streamContents: [:stream |
 gifts 
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ]
  stream nextputAll: 'all'; nextPut: ','; nextPut: gifts last] 


Op 16-5-2019 om 21:47 schreef Roelof Wobben:
oke, and then do something like :

stream :=
String streamContents: [:stream |
  gifts 
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ]
     
stream nextputAll: 'all'; nextPut: ','; nextPut: gifts last
]

stream nextputAll: 'all' 
stream nextPut: ','
stream nextPut: gifts last


Roelof


Op 16-5-2019 om 21:30 schreef Esteban Maringolo:
Maybe this is a better way to build what you want.
String streamContents: [:stream |
  gifts 
    do: [:each | stream nextPutAll: each ]
    separatedBy: [ stream nextPut: $,; space ]
]

Esteban A. Maringolo


On Thu, May 16, 2019 at 4:21 PM Roelof Wobben <[hidden email]> 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 | Threaded
Open this post in threaded view
|

Re: can I do this with a stream or a format

Richard O'Keefe
In reply to this post by Roelof Wobben
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 <[hidden email]> 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 | Threaded
Open this post in threaded view
|

Re: can I do this with a stream or a format

Sven Van Caekenberghe-2
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 <[hidden email]> 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 <[hidden email]> 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 | Threaded
Open this post in threaded view
|

Re: can I do this with a stream or a format

Esteban A. Maringolo
The good thing about these exercises is that you know about new selectors, I didn't know there was an #allButLast and #allButLastDo:.

Regards,

Esteban A. Maringolo


On Fri, May 17, 2019 at 5:44 AM Sven Van Caekenberghe <[hidden email]> wrote:
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 <[hidden email]> 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 <[hidden email]> 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 | Threaded
Open this post in threaded view
|

Re: can I do this with a stream or a format

Tim Mackinnon
 Actually that is the hidden gem on all of this - thinking about the common things people do through new eyes, has forced me to learn new things (and that feels very compatible with Pharo).

I also thank everyone here for entertaining these questions and hopefully help build another way for people to learn Pharo.

Tim

Sent from my iPhone

On 17 May 2019, at 14:00, Esteban Maringolo <[hidden email]> wrote:

The good thing about these exercises is that you know about new selectors, I didn't know there was an #allButLast and #allButLastDo:.

Regards,

Esteban A. Maringolo


On Fri, May 17, 2019 at 5:44 AM Sven Van Caekenberghe <[hidden email]> wrote:
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 <[hidden email]> 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 <[hidden email]> 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
>
>