Stream instead of string concatenation

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

Stream instead of string concatenation

Uko2
Hi everyone!

There is a Lint rule that suggests to use stream instead of strings concatenation. What is the most common way, to create a string this way, because the only thing I can come up with is:

String streamContents: [ :stream |
        stream
                nextPutAll: 'Hello';
                nextPut: $ ;
                nextPutAll: 'World’]

But I’m not sure if it is the preferred solution.

Cheers
Uko
Reply | Threaded
Open this post in threaded view
|

Re: Stream instead of string concatenation

Luc Fabresse
Hi Uko,

2014-11-26 17:18 GMT+01:00 Yuriy Tymchuk <[hidden email]>:
Hi everyone!

There is a Lint rule that suggests to use stream instead of strings concatenation. What is the most common way, to create a string this way, because the only thing I can come up with is:

String streamContents: [ :stream |
        stream
                nextPutAll: 'Hello';
                nextPut: $ ;
                nextPutAll: 'World’]

But I’m not sure if it is the preferred solution.

Yes, I use this too.
and I use << instead of #nextPutAll:

Cheers,

Luc
 

Cheers
Uko

Reply | Threaded
Open this post in threaded view
|

Re: Stream instead of string concatenation

Max Leske
In reply to this post by Uko2

> On 26.11.2014, at 17:18, Yuriy Tymchuk <[hidden email]> wrote:
>
> Hi everyone!
>
> There is a Lint rule that suggests to use stream instead of strings concatenation. What is the most common way, to create a string this way, because the only thing I can come up with is:
>
> String streamContents: [ :stream |
> stream
> nextPutAll: 'Hello';
> nextPut: $ ;
> nextPutAll: 'World’]
>
> But I’m not sure if it is the preferred solution.

Yes, that’s what I do. With one addition:

The default size for such a stream is 100, so if I know that my collection is going to be significantly larger / smaller I initialize the collection directly:

String new: 400 streamContents [: ….

And for everyone who hasn’t yet discovered it: #streamContents is implemented on SequenceableCollection! That means you can do the same thing with arrays, orderedCollections etc. Streaming is awesome :)

>
> Cheers
> Uko


Reply | Threaded
Open this post in threaded view
|

Re: Stream instead of string concatenation

Ben Coman
In reply to this post by Uko2
Yuriy Tymchuk wrote:

> Hi everyone!
>
> There is a Lint rule that suggests to use stream instead of strings concatenation. What is the most common way, to create a string this way, because the only thing I can come up with is:
>
> String streamContents: [ :stream |
> stream
> nextPutAll: 'Hello';
> nextPut: $ ;
> nextPutAll: 'World’]
>
> But I’m not sure if it is the preferred solution.
>
> Cheers
> Uko
>

This is typical advise, but previous discussion found it to be not
necessarily true.

https://www.mail-archive.com/pharo-dev@.../msg08162.html

Perhaps the rule should be ammended/deleted.
cheers -ben


Reply | Threaded
Open this post in threaded view
|

Re: Stream instead of string concatenation

Max Leske

> On 26.11.2014, at 17:32, Ben Coman <[hidden email]> wrote:
>
> Yuriy Tymchuk wrote:
>> Hi everyone!
>> There is a Lint rule that suggests to use stream instead of strings concatenation. What is the most common way, to create a string this way, because the only thing I can come up with is:
>> String streamContents: [ :stream |
>> stream
>> nextPutAll: 'Hello';
>> nextPut: $ ;
>> nextPutAll: 'World’]
>> But I’m not sure if it is the preferred solution.
>> Cheers
>> Uko
>
> This is typical advise, but previous discussion found it to be not necessarily true.
>
> https://www.mail-archive.com/pharo-dev@.../msg08162.html
>

Very good advice, thanks Ben!

> Perhaps the rule should be ammended/deleted.

Maybe a metric can be derived from Sven’s observations, e.g. “don’t apply if the strings are constants” etc. I don’t think the rule should be deleted because in the general case it’s important to understand that streams can be more efficient.

> cheers -ben
>
>


Reply | Threaded
Open this post in threaded view
|

Re: Stream instead of string concatenation

stepharo
Sven started to write a chapter GoodPractice on PharoForTheEntreprise.
If you want to imporve it just go.

https://github.com/SquareBracketAssociates/PharoForTheEnterprise-english/tree/master/GoodPractices

Stef


!Some Good Coding Practices

Best Smalltalk Practices by Kent Beck, Smalltalk by Example and
Smalltalk with Style are three excellent books about coding practices.  
Every Smalltalkers should read them. Still there are some coding
practices that programmers should be aware of.

In this chapter we present some simple and good practices that make your
code often more efficient and avoid generating unnecessary garbage.

!!Avoid unnecessary string concatenations

In Smalltalk, the message ==,== \mthindex{String}{,} concatenates two
strings. It is handy but this message is really costly since it copies
the receiver. Therefore avoid it as much as possible and especially in
loop since it multiplies the effect.

Prefer to use \ct{streamContents:} \mthindex{String}{streamContents:},
==nextPut:== and ==nextPutAll:== since they avoid the duplication. The
message ==streamContents:== expects a block would argument is a stream
on the receiver.

Let us have a look at the following code snippet.
[[[
String streamContents: [:s |
  #('Pharo''s' 'goal' 'is' 'to' 'deliver' 'a' 'clean,' 'innovative,'
'free' 'open-source' 'Smalltalk' 'environment.' 'By' 'providing' 'a'
'stable' 'and' 'small' 'core' 'system,' 'excellent' 'dev' 'tools,' 'and'
'maintained' 'releases,' 'Pharo' 'is' 'an' 'attractive' 'platform' 'to'
'build' 'and' 'deploy' 'mission' 'critical' 'Smalltalk' 'applications.')
      do: [:each | s nextPutAll: each; nextPut: Character space].
     s contents]
]]]

Note that we took an example with many strings to stress the effect, but
the bench results shows already a 4 factor.

[[[
[String streamContents: [:s |
  #('Pharo''s' 'goal' 'is' 'to' 'deliver' 'a' 'clean,' 'innovative,'
'free' 'open-source' 'Smalltalk' 'environment.' 'By' 'providing' 'a'
'stable' 'and' 'small' 'core' 'system,' 'excellent' 'dev' 'tools,' 'and'
'maintained' 'releases,' 'Pharo' 'is' 'an' 'attractive' 'platform' 'to'
'build' 'and' 'deploy' 'mission' 'critical' 'Smalltalk' 'applications.')
      do: [:each | s nextPutAll: each; nextPut: Character space].
     s contents]] bench
     --> '110,000 per second.'

[| s|
s := ''.
  #('Pharo''s' 'goal' 'is' 'to' 'deliver' 'a' 'clean,' 'innovative,'
'free' 'open-source' 'Smalltalk' 'environment.' 'By' 'providing' 'a'
'stable' 'and' 'small' 'core' 'system,' 'excellent' 'dev' 'tools,' 'and'
'maintained' 'releases,' 'Pharo' 'is' 'an' 'attractive' 'platform' 'to'
'build' 'and' 'deploy' 'mission' 'critical' 'Smalltalk' 'applications.')
      do: [:each | s := s , each , Character space asString].
     s ] bench
     --> '31,500 per second.'
]]]



Another example that may be a bit draft and fuzzy but gives an idea of
the cost that you may gain.

[[[
String streamContents: [:s |
     Smalltalk globals allClasses do: [:each |
                                     s nextPutAll: each name].
     s contents]] bench
     --> '246 per second.'

| s |
s := ''.
[Smalltalk globals allClasses
     do: [:each | s := s, each name]] bench
     --> '2.01 per second.'
]]]


!!!Preallocate when possible.
Preallocate when possible the string used with\ct{new:streamContents:}.
For example, the following method does not take advantage that the
result is always a 8 characters string.

[[[
Time>>print24
      "Return as 8-digit string 'hh:mm:ss', with leading zeros if needed"
      ^String streamContents:
          [ :aStream | self print24: true on: aStream ]
]]]

This version does the same but more efficiently, since the system does
not have to reallocate the underlying buffer.
[[[
Time>>print24
      "Return as 8-digit string 'hh:mm:ss', with leading zeros if needed"
      ^ String new: 8 streamContents: [ :aStream |
         self print24: true on: aStream ]
]]]

!!! Use ==nextPut:== when possible.
When you can, use ==nextPut:== use it instead of ==nextPutAll:==. Indeed
==nextPutAll:== requires that the argument is a container of elements.
When you have already the element, no need to create an extra container.

For example better use the second form.
[[[
stream nextPutAll: '0'
]]]

[[[
stream nextPut: $0
]]]


!!Avoid creating temporary objects

The computation of minute in Time could be written as \ct{asDuration
minutes}. However, this solution
creates a duration object that is only used to get the minutes.
In addition to be slow such approach generates extra garbage which
stresses the garbage collector.

[[[
Time>>minute
     ^ self asDuration minutes

Time>>asDuration
     "Answer the duration since midnight"
     ^ Duration seconds: seconds nanoSeconds: nanos
]]]

A much better solution is to use the encapsulation of the class Time and
performs the computation locally as

[[[
Time>>minute
     "Answer a number that represents the number of complete minutes in
the receiver,
     after the number of complete hours has been removed."
     ^ (seconds rem: SecondsInHour) quo: SecondsInMinute
]]]

!! Avoid several iterations on the same collection
It may seem obvious but it is better to iterate once than two on the
same collection, when we can do what we want in a single pass.

For example, the following code creates a first string then creates
another one where the character ==$:== is removed.

[[[
Time>>hhmm24
      "Return a string of the form 1123 (for 11:23 am), 2154 (for 9:54
pm), of exactly 4 digits"
      ^(String streamContents:
          [ :aStream | self print24: true showSeconds: false on: aStream ])
              copyWithout: $:
]]]

Better implement it as


[[[
Time>>hhmm24
      "Return a string of the form 1123 (for 11:23 am), 2154 (for 9:54
pm), of exactly 4 digits"
      ^ String new: 4 streamContents: [ :aStream |
         self hour printOn: aStream base: 10 length: 2 padded: true.
         self minute printOn: aStream base: 10 length: 2 padded: true ]
]]]


have a look at:
http://www.slideshare.net/esug/design-principlesbehindpatagonia


Le 27/11/14 09:22, Max Leske a écrit :

>> On 26.11.2014, at 17:32, Ben Coman <[hidden email]> wrote:
>>
>> Yuriy Tymchuk wrote:
>>> Hi everyone!
>>> There is a Lint rule that suggests to use stream instead of strings concatenation. What is the most common way, to create a string this way, because the only thing I can come up with is:
>>> String streamContents: [ :stream |
>>> stream
>>> nextPutAll: 'Hello';
>>> nextPut: $ ;
>>> nextPutAll: 'World’]
>>> But I’m not sure if it is the preferred solution.
>>> Cheers
>>> Uko
>> This is typical advise, but previous discussion found it to be not necessarily true.
>>
>> https://www.mail-archive.com/pharo-dev@.../msg08162.html
>>
> Very good advice, thanks Ben!
>
>> Perhaps the rule should be ammended/deleted.
> Maybe a metric can be derived from Sven’s observations, e.g. “don’t apply if the strings are constants” etc. I don’t think the rule should be deleted because in the general case it’s important to understand that streams can be more efficient.
>
>> cheers -ben
>>
>>
>
>


Reply | Threaded
Open this post in threaded view
|

Re: Stream instead of string concatenation

philippeback
I find myself using things like:

'Some {1} is going to {2} after {3}' seasideTranslated format: { anObject. 'crip', #crap }

String>>format is

format: collection 
"Format the receiver by interpolating elements from collection, as in the following examples:  
'Today is {1}.' format: {Date today}.
'Today is {date}.' format: (Dictionary with: #date->Date today). 
'In {1} you can escape \{ by prefixing it with \\' format: {'strings'}.  "
^ self class new: self size streamContents: [ :result | | stream |
stream := self readStream.
[ stream atEnd ] whileFalse: [ | currentChar | 
(currentChar := stream next) == ${
ifTrue: [ | expression index | 
expression := stream upTo: $}.
index := Integer readFrom: expression ifFail: [ expression ].
result nextPutAll: (collection at: index) asString ]
ifFalse: [
currentChar == $\
ifTrue: [ stream atEnd ifFalse: [ result nextPut: stream next ] ]
ifFalse: [ result nextPut: currentChar ] ] ] ]

The code calls asString (which is nice) and Integer readFrom: which parses.
It also uses nextPutAll:

So a kind of mixed performance bag.
Not to mention that there is the seasideTranslated lookup.

Is there any best practice here? It is quite a common idiom I think.

Phil

On Thu, Nov 27, 2014 at 9:59 AM, stepharo <[hidden email]> wrote:
Sven started to write a chapter GoodPractice on PharoForTheEntreprise. If you want to imporve it just go.

https://github.com/SquareBracketAssociates/PharoForTheEnterprise-english/tree/master/GoodPractices

Stef


!Some Good Coding Practices

Best Smalltalk Practices by Kent Beck, Smalltalk by Example and Smalltalk with Style are three excellent books about coding practices.  Every Smalltalkers should read them. Still there are some coding practices that programmers should be aware of.

In this chapter we present some simple and good practices that make your code often more efficient and avoid generating unnecessary garbage.

!!Avoid unnecessary string concatenations

In Smalltalk, the message ==,== \mthindex{String}{,} concatenates two strings. It is handy but this message is really costly since it copies the receiver. Therefore avoid it as much as possible and especially in loop since it multiplies the effect.

Prefer to use \ct{streamContents:} \mthindex{String}{streamContents:}, ==nextPut:== and ==nextPutAll:== since they avoid the duplication. The message ==streamContents:== expects a block would argument is a stream on the receiver.

Let us have a look at the following code snippet.
[[[
String streamContents: [:s |
 #('Pharo''s' 'goal' 'is' 'to' 'deliver' 'a' 'clean,' 'innovative,' 'free' 'open-source' 'Smalltalk' 'environment.' 'By' 'providing' 'a' 'stable' 'and' 'small' 'core' 'system,' 'excellent' 'dev' 'tools,' 'and' 'maintained' 'releases,' 'Pharo' 'is' 'an' 'attractive' 'platform' 'to' 'build' 'and' 'deploy' 'mission' 'critical' 'Smalltalk' 'applications.')
     do: [:each | s nextPutAll: each; nextPut: Character space].
    s contents]
]]]

Note that we took an example with many strings to stress the effect, but the bench results shows already a 4 factor.

[[[
[String streamContents: [:s |
 #('Pharo''s' 'goal' 'is' 'to' 'deliver' 'a' 'clean,' 'innovative,' 'free' 'open-source' 'Smalltalk' 'environment.' 'By' 'providing' 'a' 'stable' 'and' 'small' 'core' 'system,' 'excellent' 'dev' 'tools,' 'and' 'maintained' 'releases,' 'Pharo' 'is' 'an' 'attractive' 'platform' 'to' 'build' 'and' 'deploy' 'mission' 'critical' 'Smalltalk' 'applications.')
     do: [:each | s nextPutAll: each; nextPut: Character space].
    s contents]] bench
    --> '110,000 per second.'

[| s|
s := ''.
 #('Pharo''s' 'goal' 'is' 'to' 'deliver' 'a' 'clean,' 'innovative,' 'free' 'open-source' 'Smalltalk' 'environment.' 'By' 'providing' 'a' 'stable' 'and' 'small' 'core' 'system,' 'excellent' 'dev' 'tools,' 'and' 'maintained' 'releases,' 'Pharo' 'is' 'an' 'attractive' 'platform' 'to' 'build' 'and' 'deploy' 'mission' 'critical' 'Smalltalk' 'applications.')
     do: [:each | s := s , each , Character space asString].
    s ] bench
    --> '31,500 per second.'
]]]



Another example that may be a bit draft and fuzzy but gives an idea of the cost that you may gain.

[[[
String streamContents: [:s |
    Smalltalk globals allClasses do: [:each |
                                    s nextPutAll: each name].
    s contents]] bench
    --> '246 per second.'

| s |
s := ''.
[Smalltalk globals allClasses
    do: [:each | s := s, each name]] bench
    --> '2.01 per second.'
]]]


!!!Preallocate when possible.
Preallocate when possible the string used with\ct{new:streamContents:}. For example, the following method does not take advantage that the result is always a 8 characters string.

[[[
Time>>print24
     "Return as 8-digit string 'hh:mm:ss', with leading zeros if needed"
     ^String streamContents:
         [ :aStream | self print24: true on: aStream ]
]]]

This version does the same but more efficiently, since the system does not have to reallocate the underlying buffer.
[[[
Time>>print24
     "Return as 8-digit string 'hh:mm:ss', with leading zeros if needed"
     ^ String new: 8 streamContents: [ :aStream |
        self print24: true on: aStream ]
]]]

!!! Use ==nextPut:== when possible.
When you can, use ==nextPut:== use it instead of ==nextPutAll:==. Indeed ==nextPutAll:== requires that the argument is a container of elements. When you have already the element, no need to create an extra container.

For example better use the second form.
[[[
stream nextPutAll: '0'
]]]

[[[
stream nextPut: $0
]]]


!!Avoid creating temporary objects

The computation of minute in Time could be written as \ct{asDuration minutes}. However, this solution
creates a duration object that is only used to get the minutes.
In addition to be slow such approach generates extra garbage which stresses the garbage collector.

[[[
Time>>minute
    ^ self asDuration minutes

Time>>asDuration
    "Answer the duration since midnight"
    ^ Duration seconds: seconds nanoSeconds: nanos
]]]

A much better solution is to use the encapsulation of the class Time and performs the computation locally as

[[[
Time>>minute
    "Answer a number that represents the number of complete minutes in the receiver,
    after the number of complete hours has been removed."
    ^ (seconds rem: SecondsInHour) quo: SecondsInMinute
]]]

!! Avoid several iterations on the same collection
It may seem obvious but it is better to iterate once than two on the same collection, when we can do what we want in a single pass.

For example, the following code creates a first string then creates another one where the character ==$:== is removed.

[[[
Time>>hhmm24
     "Return a string of the form 1123 (for 11:23 am), 2154 (for 9:54 pm), of exactly 4 digits"
     ^(String streamContents:
         [ :aStream | self print24: true showSeconds: false on: aStream ])
             copyWithout: $:
]]]

Better implement it as


[[[
Time>>hhmm24
     "Return a string of the form 1123 (for 11:23 am), 2154 (for 9:54 pm), of exactly 4 digits"
     ^ String new: 4 streamContents: [ :aStream |
        self hour printOn: aStream base: 10 length: 2 padded: true.
        self minute printOn: aStream base: 10 length: 2 padded: true ]
]]]


have a look at:
http://www.slideshare.net/esug/design-principlesbehindpatagonia


Le 27/11/14 09:22, Max Leske a écrit :
On 26.11.2014, at 17:32, Ben Coman <[hidden email]> wrote:

Yuriy Tymchuk wrote:
Hi everyone!
There is a Lint rule that suggests to use stream instead of strings concatenation. What is the most common way, to create a string this way, because the only thing I can come up with is:
String streamContents: [ :stream |
        stream
                nextPutAll: 'Hello';
                nextPut: $ ;
                nextPutAll: 'World’]
But I’m not sure if it is the preferred solution.
Cheers
Uko
This is typical advise, but previous discussion found it to be not necessarily true.

https://www.mail-archive.com/pharo-dev@.../msg08162.html

Very good advice, thanks Ben!

Perhaps the rule should be ammended/deleted.
Maybe a metric can be derived from Sven’s observations, e.g. “don’t apply if the strings are constants” etc. I don’t think the rule should be deleted because in the general case it’s important to understand that streams can be more efficient.

cheers -ben







Reply | Threaded
Open this post in threaded view
|

Re: Stream instead of string concatenation

Sven Van Caekenberghe-2

> On 27 Nov 2014, at 10:10, [hidden email] wrote:
>
> I find myself using things like:
>
> 'Some {1} is going to {2} after {3}' seasideTranslated format: { anObject. 'crip', #crap }

I also use that often.

Yes, it is not very efficient, but it is high level and easy to use. In most cases that is more important.

It is only when you know that something is on a critical path that you have to think about performance.

Of course, for base classes in the system, the tradeoff can be different, efficiency in execution speed and memory usage (garbage creation) become more important.


> String>>format is
>
> format: collection
> "Format the receiver by interpolating elements from collection, as in the following examples:  
> 'Today is {1}.' format: {Date today}.
> 'Today is {date}.' format: (Dictionary with: #date->Date today).
> 'In {1} you can escape \{ by prefixing it with \\' format: {'strings'}.  "
>
> ^ self class new: self size streamContents: [ :result | | stream |
> stream := self readStream.
> [ stream atEnd ] whileFalse: [ | currentChar |
> (currentChar := stream next) == ${
> ifTrue: [ | expression index |
> expression := stream upTo: $}.
> index := Integer readFrom: expression ifFail: [ expression ].
> result nextPutAll: (collection at: index) asString ]
> ifFalse: [
> currentChar == $\
> ifTrue: [ stream atEnd ifFalse: [ result nextPut: stream next ] ]
> ifFalse: [ result nextPut: currentChar ] ] ] ]
>
> The code calls asString (which is nice) and Integer readFrom: which parses.
> It also uses nextPutAll:
>
> So a kind of mixed performance bag.
> Not to mention that there is the seasideTranslated lookup.
>
> Is there any best practice here? It is quite a common idiom I think.
>
> Phil
>
> On Thu, Nov 27, 2014 at 9:59 AM, stepharo <[hidden email]> wrote:
> Sven started to write a chapter GoodPractice on PharoForTheEntreprise. If you want to imporve it just go.
>
> https://github.com/SquareBracketAssociates/PharoForTheEnterprise-english/tree/master/GoodPractices
>
> Stef
>
>
> !Some Good Coding Practices
>
> Best Smalltalk Practices by Kent Beck, Smalltalk by Example and Smalltalk with Style are three excellent books about coding practices.  Every Smalltalkers should read them. Still there are some coding practices that programmers should be aware of.
>
> In this chapter we present some simple and good practices that make your code often more efficient and avoid generating unnecessary garbage.
>
> !!Avoid unnecessary string concatenations
>
> In Smalltalk, the message ==,== \mthindex{String}{,} concatenates two strings. It is handy but this message is really costly since it copies the receiver. Therefore avoid it as much as possible and especially in loop since it multiplies the effect.
>
> Prefer to use \ct{streamContents:} \mthindex{String}{streamContents:}, ==nextPut:== and ==nextPutAll:== since they avoid the duplication. The message ==streamContents:== expects a block would argument is a stream on the receiver.
>
> Let us have a look at the following code snippet.
> [[[
> String streamContents: [:s |
>  #('Pharo''s' 'goal' 'is' 'to' 'deliver' 'a' 'clean,' 'innovative,' 'free' 'open-source' 'Smalltalk' 'environment.' 'By' 'providing' 'a' 'stable' 'and' 'small' 'core' 'system,' 'excellent' 'dev' 'tools,' 'and' 'maintained' 'releases,' 'Pharo' 'is' 'an' 'attractive' 'platform' 'to' 'build' 'and' 'deploy' 'mission' 'critical' 'Smalltalk' 'applications.')
>      do: [:each | s nextPutAll: each; nextPut: Character space].
>     s contents]
> ]]]
>
> Note that we took an example with many strings to stress the effect, but the bench results shows already a 4 factor.
>
> [[[
> [String streamContents: [:s |
>  #('Pharo''s' 'goal' 'is' 'to' 'deliver' 'a' 'clean,' 'innovative,' 'free' 'open-source' 'Smalltalk' 'environment.' 'By' 'providing' 'a' 'stable' 'and' 'small' 'core' 'system,' 'excellent' 'dev' 'tools,' 'and' 'maintained' 'releases,' 'Pharo' 'is' 'an' 'attractive' 'platform' 'to' 'build' 'and' 'deploy' 'mission' 'critical' 'Smalltalk' 'applications.')
>      do: [:each | s nextPutAll: each; nextPut: Character space].
>     s contents]] bench
>     --> '110,000 per second.'
>
> [| s|
> s := ''.
>  #('Pharo''s' 'goal' 'is' 'to' 'deliver' 'a' 'clean,' 'innovative,' 'free' 'open-source' 'Smalltalk' 'environment.' 'By' 'providing' 'a' 'stable' 'and' 'small' 'core' 'system,' 'excellent' 'dev' 'tools,' 'and' 'maintained' 'releases,' 'Pharo' 'is' 'an' 'attractive' 'platform' 'to' 'build' 'and' 'deploy' 'mission' 'critical' 'Smalltalk' 'applications.')
>      do: [:each | s := s , each , Character space asString].
>     s ] bench
>     --> '31,500 per second.'
> ]]]
>
>
>
> Another example that may be a bit draft and fuzzy but gives an idea of the cost that you may gain.
>
> [[[
> String streamContents: [:s |
>     Smalltalk globals allClasses do: [:each |
>                                     s nextPutAll: each name].
>     s contents]] bench
>     --> '246 per second.'
>
> | s |
> s := ''.
> [Smalltalk globals allClasses
>     do: [:each | s := s, each name]] bench
>     --> '2.01 per second.'
> ]]]
>
>
> !!!Preallocate when possible.
> Preallocate when possible the string used with\ct{new:streamContents:}. For example, the following method does not take advantage that the result is always a 8 characters string.
>
> [[[
> Time>>print24
>      "Return as 8-digit string 'hh:mm:ss', with leading zeros if needed"
>      ^String streamContents:
>          [ :aStream | self print24: true on: aStream ]
> ]]]
>
> This version does the same but more efficiently, since the system does not have to reallocate the underlying buffer.
> [[[
> Time>>print24
>      "Return as 8-digit string 'hh:mm:ss', with leading zeros if needed"
>      ^ String new: 8 streamContents: [ :aStream |
>         self print24: true on: aStream ]
> ]]]
>
> !!! Use ==nextPut:== when possible.
> When you can, use ==nextPut:== use it instead of ==nextPutAll:==. Indeed ==nextPutAll:== requires that the argument is a container of elements. When you have already the element, no need to create an extra container.
>
> For example better use the second form.
> [[[
> stream nextPutAll: '0'
> ]]]
>
> [[[
> stream nextPut: $0
> ]]]
>
>
> !!Avoid creating temporary objects
>
> The computation of minute in Time could be written as \ct{asDuration minutes}. However, this solution
> creates a duration object that is only used to get the minutes.
> In addition to be slow such approach generates extra garbage which stresses the garbage collector.
>
> [[[
> Time>>minute
>     ^ self asDuration minutes
>
> Time>>asDuration
>     "Answer the duration since midnight"
>     ^ Duration seconds: seconds nanoSeconds: nanos
> ]]]
>
> A much better solution is to use the encapsulation of the class Time and performs the computation locally as
>
> [[[
> Time>>minute
>     "Answer a number that represents the number of complete minutes in the receiver,
>     after the number of complete hours has been removed."
>     ^ (seconds rem: SecondsInHour) quo: SecondsInMinute
> ]]]
>
> !! Avoid several iterations on the same collection
> It may seem obvious but it is better to iterate once than two on the same collection, when we can do what we want in a single pass.
>
> For example, the following code creates a first string then creates another one where the character ==$:== is removed.
>
> [[[
> Time>>hhmm24
>      "Return a string of the form 1123 (for 11:23 am), 2154 (for 9:54 pm), of exactly 4 digits"
>      ^(String streamContents:
>          [ :aStream | self print24: true showSeconds: false on: aStream ])
>              copyWithout: $:
> ]]]
>
> Better implement it as
>
>
> [[[
> Time>>hhmm24
>      "Return a string of the form 1123 (for 11:23 am), 2154 (for 9:54 pm), of exactly 4 digits"
>      ^ String new: 4 streamContents: [ :aStream |
>         self hour printOn: aStream base: 10 length: 2 padded: true.
>         self minute printOn: aStream base: 10 length: 2 padded: true ]
> ]]]
>
>
> have a look at:
> http://www.slideshare.net/esug/design-principlesbehindpatagonia
>
>
> Le 27/11/14 09:22, Max Leske a écrit :
> On 26.11.2014, at 17:32, Ben Coman <[hidden email]> wrote:
>
> Yuriy Tymchuk wrote:
> Hi everyone!
> There is a Lint rule that suggests to use stream instead of strings concatenation. What is the most common way, to create a string this way, because the only thing I can come up with is:
> String streamContents: [ :stream |
>         stream
>                 nextPutAll: 'Hello';
>                 nextPut: $ ;
>                 nextPutAll: 'World’]
> But I’m not sure if it is the preferred solution.
> Cheers
> Uko
> This is typical advise, but previous discussion found it to be not necessarily true.
>
> https://www.mail-archive.com/pharo-dev@.../msg08162.html
>
> Very good advice, thanks Ben!
>
> Perhaps the rule should be ammended/deleted.
> Maybe a metric can be derived from Sven’s observations, e.g. “don’t apply if the strings are constants” etc. I don’t think the rule should be deleted because in the general case it’s important to understand that streams can be more efficient.
>
> cheers -ben
>
>
>
>
>
>
>