Loop Example: Rectangles

Home, Up: Loop and Script Examples

 

In this example:

Inline Loops, Reading Data from File, 2-dimensional Array, Accumulation Mode, Buffer.

 

As always, the question mark at the beginning of a line is Hypatia's input prompt, you do not type it.

 

On the page HY and Accumulation Mode in chapter "Results" we had a "floor plan" example where we added up the areas of rooms, that is, we computed the sum of the products of their lenghts and widths.

Here we want to read the data from a file. Let's not be concerned with the individual products here, we just want the sum.

In more general terms, we have a two-dimensional array of data -- two colums, and an unknown number of rows. (It would be easy to generalize this to more than two columns, or to different calculations.)

Let's use the data from the "accumulation mode" example, and let's assume we have them in a file with the name rectangles (you can create it with the command EDIT rectangles):

2 1.5

4 3

5 3

6 4

This file could have any number of lines, in this example we stay with these four.

 

When Hypatia reads this file the line breaks are ignored, and we have a one-dimensional list of data:

2 1.5 4 3 5 3 6 4

To treat this as a two-dimensional array -- that is, to match lenghts and widths of the rectangles as we process the data -- we have to use our own line and row indexes. We can easily compute them from the loop index I:

The line index is I 2 * 1 - (the first item in the list is the length of the first rectangle, the third item is the length of the second rectange, and so on.)

The row index is I 2 * (the first item in the list is the length of the first rectangle, the third item is the length of the second rectange, and so on.)

Before we calculate the sum of the products of lengths and widths we should read the data file into a user defined element, to make the loop faster (with 4 lines this would make no noticeable difference, but with tens of thousands it definitely would).

Now we have to set $ to 0, and then we can use an inline loop for our calculation.

For the second ITEM operator we need the vertical bar n-argument delimiter | to let it know where its list of items begins. (The first ITEM operator has replaced its list of arguments and itself with its result, that is, with the addressed element.)

Because the calculation contains an UDE, at the first pass of the loop it is shown with the UDE's content (as would be the case if we used an insert file) -- this is limited to the length of one line on the screen.

 

? @data = (rectangles)

  @data = 2 1.5 4 3 5 3 6 4

? 0

= 0

? DO * :: @data I 2 * 1 - ITEM | @data I 2 * ITEM * $ +

  2 1.5 4 3 5 3 6 4 i 2 * 1 - item | 2 1.5 4 3 5 3 6 4 i 2 * item * $ +

Loop exit condition met in pass 4

= 54

 

The loop exit condition is the fact that the last item in the list has been addressed.

Alternatively, we could use the loop not to calculate the sum of the products, but to write the individual products (rectangle areas) into a result file.

Both for speed, but also to better be able to work with the results, we use buffer mode, and save the products to a file areas (Hypatia's messages are omitted here).

 

? @data = (rectangles)

? BUFFER START

? DO * :: @data I 2 * 1 - ITEM | @data I 2 * ITEM * &

Loop exit condition met in pass 4

= 24

? BUFFER SAVE areas

? BUFFER DISCARD

? (areas) SUM

= 54

 

The result 24 is the loop's last calculation result, we can ignore it. The areas of the individual rectangles are now in the file areas -- we calculated the sum, but we can also calculate the mean, see how many rectangles (data rows) we had, what size the smallest one had, etc.:

? (areas) MEAN

= 13.5

? (areas) N

= 4

? (areas) MIN

= 3

 

If we want to do this calculation more often with different data, we can use a script instead of an inline loop.

To show how things can be done in different ways, instead of saving the content of the buffer (the products of our data pairs) to a file we will copy its content to a new user defined element (of course, instead of discarding the buffer you could still save it).

In the first loop pass we not only read the data file into a user defined element, we also check whether it has an even number of data.

You have to adapt the first line in the script if the data file has a different name than rectangles.

This is how the script, let's call it areasum, could look:

 

I1: @data = (rectangles)

I1: IF @data N 2 MULTIPLE IS0 THEN ABORT Number of data is not even!

I1: BUFFER START

$length = @data I 2 * 1 - ITEM

$width = @data I 2 * ITEM

$length $width * &

I*: @areas = (buffer)

I*: BUFFER DISCARD

I*: $n = @areas N

I*: $total = @areas SUM

I*: $mean = @areas MEAN

I*: # number of rectangles, total sum of areas, mean size:

I*: SHOW $n $total $mean

 

To calculate the total area of the rectangles and their mean size call the script in an infinite loop:

? _*areasum

Buffer deleted, buffer mode is now OFF

  # number of rectangles, total sum of areas, mean size:

$n = 4

$total = 54

$mean = 13.5

Loop exit condition met in pass 4

= 24

 

(Again, at the end of the loop the last calculation result $ will be displayed, whether we need it or not.)

This example that reads two-dimensional data with 2 columns can easily be generalized to more than 2 columns and to different or more complex calculations.

With the help of line breaks in accumulation mode Hypatia can also generate 2-dimensional output.

What Hypatia cannot do, though, is update individual array elements -- to achieve this, the whole array has to be re-written to hy or (preferably, because much faster) to the buffer.

 

Home, Up: Loop and Script Examples, Prev: Loop Example: Body Mass Index, Next: Loop Example: Centimeters-Feet-Inches Table