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 <[email protected]> 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 <[email protected]> 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/[email protected]/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 via email to