What is a simple and concise way to break from a whileTrue
loop in GNU Smalltalk that doesn't require returning?
Here is my code. I want to break from the loop at Line 31 at the end if char_stack
is empty.
https://gist.github.com/SYZYGY-DEV333/ea3f5eeb3473927c8faa294bb72a8858
Any help would be much appreciated.
One of the articles of the Byte magazine (1982) titled Building Control Structures in the Smalltalk-80 System by Peter Deutsch , shows how easy is to implement while-loop breaks for infrequent events that might happen inside the loop.
To implement this we only need a new class and an extension to BlockClosure
, making a total of 9 lines of code(!).
The class: BlockWithExit
, subclass of Object
with two ivars exit
and block
and the following methods
on: aBlock
block := aBlock
value
exit := [^nil].
^block value
exit
exit value
Extension
BlockClosure>>withExit
^BlockWithExit new on: self
and that's it!
Example
Find the max of a collection until its exhaustion or until nil
is found (the infrequent event)
maxBeforeNil: aCollection
| max supplier loop |
max := 0.
supplier := aCollection readStream.
loop := [
[supplier atEnd]
whileFalse: [
value := supplier next.
value isNil ifTrue: [loop exit].
max := max max: value]] withExit.
loop value.
^max
Why does this work the way it does? Because a block with a non-local return exits from the method that defines the block.
In this case this method is BlockWithExit>>value
, therefore when [^nil]
is evaluated from loop exit
, the flow exits value
and goes to its sender, right after loop value
.
The outstanding corollary of Deutsch's discovery is that the whole mechanism of Exceptions
can be built using this very same trick of defining an exit block in an ivar like: exit := [^nil]
.