[comp.lang.smalltalk] Nested Control Structures Within Iterators

Will@cup.portal.com (Will E Estes) (04/08/91)

Can someone explain to me whether it is possible to place another 
control structure on the same line as an iterator?  Say, for instance,
that I'm iterating across a file stream and I want to continue to
do so only while some condition is true.

	input do:
	[
		:char |
		output nextPut: char.
		"more code here"
	].

Now what I would like to do is qualify the iteration of do: such that
when some condition becomes true we stop iterating?  One thought that
comes to mind is an equivalent to the C control structure BREAK, but
I was not able to find this.  Is there someone of achieving the effect
of BREAK?  And if not is there some way of further qualifying the
iterator statement?

Thanks,
Will Estes        Internet: Will@cup.portal.com                  
                  UUCP: apple!cup.portal.com!Will

kentb@argosy.UUCP (Kent Beck) (04/08/91)

Assuming you want to have some setup and teardown code in the same method,
put the loop in a submethod and use ^.

Kent

mario@cs.man.ac.uk (Mario Wolczko) (04/09/91)

In article <41040@cup.portal.com>, Will@cup.portal.com (Will E Estes) writes:
> Can someone explain to me whether it is possible to place another 
> control structure on the same line as an iterator?  Say, for instance,
> that I'm iterating across a file stream and I want to continue to
> do so only while some condition is true.
> 
> 	input do:
> 	[
> 		:char |
> 		output nextPut: char.
> 		"more code here"
> 	].
> 
> Now what I would like to do is qualify the iteration of do: such that
> when some condition becomes true we stop iterating?  One thought that
> comes to mind is an equivalent to the C control structure BREAK, but
> I was not able to find this.  Is there someone of achieving the effect
> of BREAK?  And if not is there some way of further qualifying the
> iterator statement?
> 
> Thanks,
> Will Estes        Internet: Will@cup.portal.com                  
>                   UUCP: apple!cup.portal.com!Will

One of the nice things about Smalltalk is the ability to build new
control structures.  Appended is a goodie which, when filed in, allows
you to build blocks that can be exited prematurely by sending a
special "break object" the value message.  So, your example would look
like this:
	[ :break |
	input do:
	  [ :char |
	  output nextPut: char.
	  "some test" ifTrue: [break value]
	  ]
	] valueWithExit

valueWithExit evaluates a block, passing it an object that, when sent
a value message, causes immediate escape from the block.

Try these examples:

1 to: 10 do: [ :i | Transcript show: i printString ; cr].

[ :break | 
	1 to: 10 do: [ :i |
		Transcript show: i printString ; cr.
		i > 5 ifTrue: [break value]]] valueWithExit0


Mario Wolczko

   ______      Dept. of Computer Science   Internet:      mario@cs.man.ac.uk
 /~      ~\    The University              uucp:      mcsun!ukc!man.cs!mario
(    __    )   Manchester M13 9PL          JANET:         mario@uk.ac.man.cs
 `-':  :`-'    U.K.                        Tel: +44-61-275 6146  (FAX: 6280)
____;  ;_____________the mushroom project___________________________________

If you're using a pre-2.5 version of Smalltalk-80, you'll have to
change the := to leftarrow (_).  The example code (in comments) only
works in Rel 4.0.

I'll install this in the Manchester Archive as BlockWithExit.st.

'From Objectworks(r)\Smalltalk, Release 4 of 25 October 1990 on 8 April 1991 at 8:40:51 pm'!



!BlockClosure methodsFor: 'evaluating'!

valueWithExit
	"Self should be a block of one argument.  It is evaluated, and is passed a block, which,
if sent a value: message, will exit self, returning the parameter of the value: message.
	Used for premature returns to the caller."

	"| val |
	val := [ :exit || goSoon |
			goSoon := DialogView confirm: 'Exit prematurely?' .
			goSoon ifTrue: [exit value: 'Bye!!'].
			Transcript show: 'Thank you for not exiting,'.
			' and have a nice day!!'] valueWithExit.
	Transcript show: val"

	^self value: [ :exit | ^exit]!

valueWithExit0
	"Self should be a block of one argument.  It is evaluated, and is passed a block, which,
if sent a value message, will exit self.
	Used for premature returns to the caller."

	"[ :exit || goSoon |
		goSoon := DialogView confirm: 'Exit prematurely?' .
		goSoon ifTrue: [Transcript show: 'Bye!!'.  exit value].
		Transcript show: 'Thank you for not exiting!!'] valueWithExit0"

	^self value: [^self]!

withExit
	"Self should be a block of one argument.  Returns a block which when evaluated will evaluate self, passing it an exit continuation.
	Used for premature returns to the caller."

	"| exitBlock |
	exitBlock := [ :exit || goSoon |
			goSoon := DialogView confirm: 'Exit prematurely?' .
			goSoon ifTrue: [exit value: 'Bye!!'].
			Transcript show: 'Thank you for not exiting,'.
			' and have a nice day!!'] withExit.
	Transcript show: exitBlock value"

	^[self valueWithExit]!

withExit0
	"Self should be a block of one argument.  Returns a block which when evaluated will evaluate self, passing it an exit continuation.
	Used for premature returns to the caller."

	"| exitBlock |
	exitBlock := [ :exit || goSoon |
			goSoon := DialogView confirm: 'Exit prematurely?' .
			goSoon ifTrue: [Transcript show: 'Bye!!'.  exit value].
			Transcript show: 'Thank you for not exiting!!'] withExit0.
	exitBlock value"

	^[self valueWithExit0]! !

fmarh@minyos.xx.rmit.oz.au (Robert Hinterding) (04/09/91)

In article <41040@cup.portal.com> Will@cup.portal.com (Will E Estes) writes:
>Can someone explain to me whether it is possible to place another 
>control structure on the same line as an iterator?  Say, for instance,
>that I'm iterating across a file stream and I want to continue to
>do so only while some condition is true.
>
>	input do:
>	[
>		:char |
>		output nextPut: char.
>		"more code here"
>	].
>
>Now what I would like to do is qualify the iteration of do: such that
>when some condition becomes true we stop iterating?  One thought that
>comes to mind is an equivalent to the C control structure BREAK, but
>I was not able to find this.  Is there someone of achieving the effect
>of BREAK?  And if not is there some way of further qualifying the
>iterator statement?
>
>Thanks,
>Will Estes        Internet: Will@cup.portal.com                  
>                  UUCP: apple!cup.portal.com!Will

The problem is that do: is for iterating over the whole collection.  To
do what you want, you need to use a different method.  As you stated 
that input was a file stream a way of doing it is shown below.

	[ condition and:[ input atEnd not] ]
	whileTrue:[ 
		input next "more code here"
        ]

whileTrue is a method for class Context, atEnt and next are methods
for class FileStream in the example.  
I hope this helps you.

-- 
Robert Hinterding                             fmarh@minyos.xx.rmit.oz.au
Victoria University of Technology             Phone +61 3 6884686
PO Box 64, Footscray 3011                     Fax: +61 3 687 7632
Australia

rick@cua.cary.ibm.com (Rick DeNatale) (04/12/91)

>In article <41040@cup.portal.com> Will@cup.portal.com (Will E Estes) writes:
>Can someone explain to me whether it is possible to place another 
>control structure on the same line as an iterator?  Say, for instance,
>that I'm iterating across a file stream and I want to continue to
>do so only while some condition is true.
>
>	input do:
>	[
>		:char |
>		output nextPut: char.
>		"more code here"
>	].
>
>Now what I would like to do is qualify the iteration of do: such that
>when some condition becomes true we stop iterating?  One thought that
>comes to mind is an equivalent to the C control structure BREAK, but
>I was not able to find this.  Is there someone of achieving the effect
>of BREAK?  And if not is there some way of further qualifying the
>iterator statement?
>

   One way to do this is:

      input detect:
      [
         :char |
          output nextPut: char.
          conditional test
      ]
      ifNone: [ ]

conditional test would be an expression which would evaluate to true if
you want to terminate the loop.  The ifNone: [ ], avoids an error if 
the end of input is reached before the test succeeds.  The empty block
could be replaced with something useful if you wanted to do something special
if the whole input was processed.

Rick DeNatale
Of course my opinion is my own, who else would want it?