do: operation separatedBy: separator
For each element of the receiver, operation is evaluated with the element as the parameter.
Before evaluating operation the second and subsequent times, evaluate separator.
Separator is not evaluated if there are no elements or after the last element.
We could implement this in Collection like so:
do: doBlock separatedBy: separatorBlock
�� |started|
�� started := false.
�� self do: [:each |
�� �� started ifTrue: [separator value] ifFalse: [started := true].
�� �� doBlock value: each].
You can look at the actual source code by typing
do:separatedBy:
in a Playground (Workspace) and then typing Ctrl-M (show iMplementors).
The idea is simple enough: aCollection do: aBlock separatedBy: separatorBlock
invokes (aBlock value: x1) ... (aBlock value: xn) where x1 ... xn are the
elements of aCollection, but *between* each pair of calls to aBlock there is
an invocation of (separatorBlock value).
A good thing to do after you have looked at the implementation(s) is to look
at how it's used.�� Type
do:separatedBy:
into a Playground and type Ctrl-N (show seNders).�� It's almost always printing
or concatenating with a separator.�� And that is EXACTLY what is happening in
the ProfStef tutorial you show.
Let's take a simple example.
Suppose you have a string 'abcd'.
If you want to write /a/b/c/d
�� string do: [:each | aStream nextPut: $/; nextPut: each].
If you want to write a/b/c/d/
�� string do: [:each | aStream nextPut: each; nextPut: $/].
So far so good, there are exactly as many / writes as there are element writes,
so they can go in the same block.�� But if you want to write a/b/c/d what do you do?
Now you have one FEWER separator than elements.
�� string do: [:each | aStream nextPut: each] separatedBy: [aStream nextPut: $/]
does the trick.�� You could of course bodge something together yourself,
BUT #do:separatedBy: is a well known idiom so it is much easier for people to understand.
Another approach would have been
Collection >>
doFirst: firstBlock thenDo: otherBLock
�� |started|
�� started := false.
�� self do: [:each |
�� �� started ifTrue: [otherBlock value: each] ifFalse: [firstBlock value: each. started := true]].
and then we could write
�� aString doFirst: [:first | aStream nextPut: first] thenDo: [:each | aStream nextPut: $/; nextPut: each]
but that would involve code duplication, so we use #do:separatedBy: instead.