GNU Smalltalk - Break from whileTrue loop without return

1.9k Views Asked by At

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.

2

There are 2 best solutions below

4
On BEST ANSWER

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].

1
On

In general, Smalltalk does not have a way of breaking from a loop, except for returning from the enclosing method.

Try to extract your loop into another method from which you can return to break from the loop.

In a way, Smalltalk the language does not even have loops... but some methods happen to evaluate blocks more than once. Hence it does not have a special way to terminate "loops". Return is the way.

If you have not already done so, familiarize yourself with the different iteration methods of Collection: do:, select:, collect:, detect:ifNone:, ... The latter is another way to run an "incomplete" loop over a collection, but it does not solve all cases in which you might wish for a "break".