Back: Iteration
Up: Tutorial
Forward: Integer loops
 
Top: GNU Smalltalk User's Guide
Contents: Table of Contents
Index: Class index
About: About this document

5.7 Code blocks, part two

In the last chapter, we looked at how code blocks could be used to build conditional expressions, and how you could iterate across all entries in a collection.(27) We built our own code blocks, and handed them off for use by system objects. But there is nothing magic about invoking code blocks; your own code will often need to do so. This chapter will shows some examples of loop construction in Smalltalk, and then demonstrate how you invoke code blocks for yourself.

5.7.1 Integer loops  Well, Smalltalk too has them
5.7.2 Intervals  And of course here's a peculiar way to use them
5.7.3 Invoking code blocks  You can do it, too


5.7.1 Integer loops

Integer loops are constructed by telling a number to drive the loop. Try this example to count from 1 to 20:
 
   1 to: 20 do: [:x | x printNl ] !

There's also a way to count up by more than one:
 
   1 to: 20 by: 2 do: [:x | x printNl ] !

Finally, counting down is done with a negative step:
 
   20 to: 1 by: -1 do: [:x | x printNl ] !


5.7.2 Intervals

It is also possible to represent a range of numbers as a standalone object. This allows you to represent a range of numbers as a single object, which can be passed around the system.
 
   Smalltalk at: #i put: (Interval from: 5 to: 10) !
   i printNl !
   i do: [:x | x printNl] !

As with the integer loops, the Interval class can also represent steps greater than 1. It is done much like it was for our numeric loop above:
 
   i := (Interval from: 5 to: 10 by: 2)
   i printNl !
   i do: [:x| x printNl] !


5.7.3 Invoking code blocks

Let us revisit the checking example and add a method for scanning only checks over a certain amount. This would allow our user to find "big" checks, by passing in a value below which we will not invoke their function. We will invoke their code block with the check number as an argument ment; they can use our existing check: message to get the amount.

 
   !Checking methodsFor: 'scanning'!
   checksOver: amount do: aBlock
       history associationsDo: [:assoc|
           ((assoc value) > amount)
                  ifTrue: [aBlock value: (assoc key)]
       ]
   ! !

The structure of this loop is much like our printChecks message sage from chapter 6. However, in this case we consider each entry, and only invoke the supplied block if the check's value is greater than the specified amount. The line:

 
   ifTrue: [aBlock value: (assoc key)]

invokes the user-supplied block, passing as an argument the association's key, which is the check number. The value: message, when received by a code block, causes the code block to execute. Code blocks take value, value:, value:value:, and value:value:value: messages, so you can pass from 0 to 3 arguments to a code block.(28)

You might find it puzzling that an association takes a value message, and so does a code block. Remember, each object can do its own thing with a message. A code block gets run when it receives a value message. An association merely returns the value part of its key/value pair. The fact that both take the same message is, in this case, coincidence.

Let's quickly set up a new checking account with $250 (wouldn't this be nice in real life?) and write a couple checks. Then we'll see if our new method does the job correctly:
 
   Smalltalk at: #mycheck put: (Checking new) !
   mycheck deposit: 250 !
   mycheck newChecks: 100 count: 40 !
   mycheck writeCheck: 10 !
   mycheck writeCheck: 52 !
   mycheck writeCheck: 15 !
   mycheck checksOver: 1 do: [:x | x printNl] !
   mycheck checksOver: 17 do: [:x | x printNl] !
   mycheck checksOver: 200 do: [:x | x printNl] !

We will finish this chapter with an alternative way of writing our checksOver: code. In this example, we will use the message select: to pick the checks which exceed our value, instead of doing the comparison ourselves. We can then invoke the new resulting collection against the user's code block.

 
   !Checking methodsFor: 'scanning'!
   checksOver: amount do: aBlock
       | chosen |
       chosen := history select: [:amt| amt > amount].
       chosen associationsDo: aBlock
   ! !

Unlike our previous definition of checksOver:do:, this one passes the user's code block the association, not just a check number. How could this code be rewritten to remedy this, while still using select:?

Yet, this new behavior can be useful. You can use the same set of tests that we ran above. Notice that our code block:
 
   [:x| x printNl]
now prints out an Association. This has a very nice effect: with our old method, we were told which check numbers were above a given amount; with this new method, we get the check number and amount in the form of an Association. When we print an association, since the key is the check number and the value is the check amount, we get a list of checks over the amount in the format:
 
   CheckNum -> CheckVal




This document was generated on May, 12 2002 using texi2html