Number 29; April 1995

In this issue ...

New Area Code

Most of Arizona, including Tucson, now has a new area code, 520. Until October 21, 1995, our old area code, 602, also will work. After that, you'll have to use 520 to reach us.

A word of warning: Some automated switchboards only can dial area codes whose second digit is a 0 or 1. If you try to call us at 520 and are unable to reach us, that may be the problem.

Procedure and Operator Values

String invocation, described in the last issue of the Analyst, allows you to invoke procedures and operators using their string names. It's also possible to get procedure and operator values from their string names.

In past articles we've mentioned that procedures are Icon values, although it's seldom necessary to use such values explicitly. For example, the global variable trim has a procedure (function) value at the beginning of execution, which can be seen by executing
   write(type(trim))
which writes procedure. It's this value that's looked up if you execute
   "trim"(line)
If you're using trim() explicitly, there's no reason to use string invocation, but suppose you use
   p := read() | stop("procedure name expected")
to provide a string name for a procedure that happens to be "trim". If you subsequently use p to invoke trim(), there's a lookup each time you use it. That overhead can be avoided by getting the actual procedure value corresponding the name. The function proc(s) does this. If s is the name of a procedure, proc(s) produces the corresponding procedure value, but if s is not the name of a procedure, proc() fails. For example,
   p := proc("trim")
assigns the procedure value for trim() to p. In the example above, proc() might be used as
   p := proc(read()) | stop("procedure name expected")
While the notion that procedures are values is generally well known to experienced Icon programmers, many aren't aware that there also are values for operators. Unlike procedures, there are no global variables corresponding to operators. An expression like
   i + j
is a syntactic form; you can't use
   +(i, j)
although you can use
   "+"(i, j)
Nevertheless, operators are values and you can use proc() to get them. In addition to the string name for the operator, a second argument to proc() is needed to specify how many operands the operator has. Thus,
   proc("*", 1)
is the unary size operator, while
   proc("*", 2)
is the binary multiplication operator. The default for the second argument to proc() is 1, so
   proc("*")
produces the unary size operator. But what is the value produced by proc() for an operator? It's a procedure value. For example,
   type(proc("*"))
produces "procedure". This may seem a bit strange, but there's nothing particularly subtle about it. In the implementation of Icon, procedures and operators are much the same. The difference is that procedures are the initial values of global variables, while operators are distinguished syntactically.

The values for operators are not just curiosities; they allow operators to be invoked using the same syntax as is used for procedures. For example, you can do this:
   size := proc("*")
and then
   write(size(x))
There are a few things about proc() for operators that you need to know. One is that proc(s, n) fails if s not the name of an nary operator. A bit of care is needed in specifying the second argument. For example,
   proc("...", 2)
fails, since to-by is a ternary operator given in distributed syntactic form, and proc() makes no allowance for the fact that the by clause can be omitted. If you want to know if s is the name of any operator, you can use
   proc(s, 1 to 3)
If s is the name of a unary operator, you get that operator value. If it isn't, proc() fails and 1 to 3 is resumed to produce 2. If s is name of a binary operator, you then get that value, and so on. In some situations, you might want to reorder the alternatives, as in
   proc(s, 2 | 1 | 3)
Incidentally, if s is the name of a procedure, proc() produces the corresponding procedure value and the second argument is ignored; the forms above can be used for either procedure names or operator names.

There is one subtle aspect of procedure names and values. Suppose the value of a variable that corresponds to a built-in procedure (function) has been changed. This happens if there is a declared procedure by the same name, as in
   procedure tab(i)
        ...
   end
This also happens if a value other than a procedure has been assigned to the global variable, as in
   tab := 8
In the first case, proc("tab") gives the variable for the declared procedure, which certainly is reasonable. In the second case, proc("tab") fails, which also is reasonable, since attempting to use tab to invoke the function would not work.

Suppose, however, you want to use the built-in function even if the corresponding global variable no longer has this function as value. Prior to Version 8.10 of Icon, there was no way to do this; in fact, if the value of a global variable corresponding to a built-in function was changed without being saved in another variable, there was no way to get to the built-in function.

Starting with Version 8.10, proc(s, 0) produces the built-in function for s, if there is one. This feature allows a built-in function to be retrieved if its global variable has been changed, and it also allows a procedure to "overload" a built-in function, while still allowing the built-in function to be used.

The latter case is illustrated by a procedure to trim both the beginning and end of a string:
   procedure trim(s, c)
      static trim_end

      initial trim_end := proc("trim", 0)

      /c := ' '

      trim_end(s, c) ? {
         tab(many(c))
         return tab(0)
         }

   end
Of course, you could use a name other than trim for this procedure, but if you want to change the functionality of trim() in a program, this is an elegant way to do it. This technique also can be used in library procedures to overload built-in functions without having to change the programs that link them.

There is another function that sometimes is useful in conjunction with proc() -- args(p), which returns the number of arguments expected by the procedure or operator p.

For built-in functions like write() that accept an arbitrary number of arguments, args(p) produces 0. (There is no built-in function that expects no arguments.) For a declared procedure, args(p) produces the negative of the number of arguments given in the procedure's declaration. There is no way to tell if a procedure has been declared with an arbitrary number of arguments.

Finally, a word of caution; args(p) requires a procedure-valued argument; it does not automatically convert string names.

Applications of String Invocation

String invocation may seem a bit ethereal or even fraught with dangers. You may not even see why it's needed.

String invocation is one of those features that isn't needed often, but when it's needed, it can be very useful indeed. In some situations, string invocation may not be necessary, but it may simplify program design and provide generality that can't be provided by other means. Using string invocation may, in fact, suggest useful approaches to programming. We'll illustrate these points with a few examples.

Filtering Files

Many programming tasks involve "filtering" the lines of a file, applying the same operation to each line to produce another file. The operation may transform the line, eliminate it, or produce several lines from one.

Many "one-shot" filters are written in Icon simply by writing a short program that applies the operation, as in
   procedure main()

      while write(trim(read()))

   end
which trims trailing blanks from the lines of input. Suppose this program is named trim.icn. Then in UNIX, for example,
   trim <input >output
writes the trimmed lines of input to output. A more general approach is to write a program, ifilter.icn, that takes the name of the filtering operation on the command line, as in
   ifilter trim <input >output
Since the name of the operation is given as a string on the command line, string invocation can be used to get the corresponding procedure, as in
   procedure main(args)
      local p

      p := args[1] | stop("*** no operation")
      while line := read() do
         every write(p(line)

   end
The every loop is used in case the operation is a generator. This formulation has two problems. If the command-line argument is not the name of an operation, a run-time error occurs when it is applied and the message may be mystifying to a user. In addition, the procedure is looked up by its string name for every line that is read in. Both of these problems can be solved by converting the name to a procedure, as described in the preceding article:
procedure main(args)

   local p

   p := proc(args[1] ) |
      stop("*** invalid operation")
      while line := read() do
         every write(p(line))

   end
Although this program doesn't use string invocation explicitly, what it does amounts to the same thing; whether string invocation is used directly or proc() is used, the same underlying feature is at work -- getting a procedure value from its string name.

In either of the formulations, operators can be used for filtering, as in
   ifilter '*' <input >output
which writes the lengths of the lines in input to output. Procedures in libraries also can be used with these formulations, but they must be linked with ifilter.icn when it is translated, as in
   icont ifilter wordform.u
which adds the procedures in wordform to ifilter. It's also necessary to add
   invocable all
to ifilter.icn, as described in the article on string invocation in the last issue of the Analyst. To make ifilter really useful, we need to be able to handle arguments as well as the operation to apply. For example, to use a function like right(), it's necessary to supply the field width and the padding string. With this addition, for example,
   ifilter right 10 0 <input >output
could be used to produce lines of width 10 filled with zeros at the left. Here's a version of ifilter that handles arguments:
   invocable all

   procedure main(args)
      local p

      p := proc(args[1], 1 to 3) | stop("*** invalid operation")
      while args[1] := read() do
         every write(p ! args)

   end
This program requires some explanation. The second argument to proc() is used as described in the preceding article to cover unary, binary, and ternary operators (in that order -- without an additional feature, the program uses the one it finds first). The lines
   while args[1] := read() do
      every write(p ! args)
may be less clear. The value of args for
   ifilter right 10 0 <input >output
is
   ["right", "10", "0"]
while what is needed for a line of input is
   p(read(), "10", "0")
where p is the result of proc(args[1], 1 to 3). By overwriting the first element of args, once it has been used to get p, args is equivalent to
  [read(), "10", "0"]
This allows list invocation to be used in
   p ! args 
Note that the version of ifilter above takes advantage of the fact that many Icon functions have a string of interest as their first argument, while trailing arguments provide parameters. This also allows trailing arguments to be omitted when defaults apply, as in
   ifilter right 10
which produces lines filled with blanks on the left and
   ifilter map
which converts uppercase characters to lowercase ones.

Interactive Expression Evaluation

If you are learning or testing Icon, it's handy to be able to keyboard an Icon expression and immediately get the results of evaluating it without having to write and run a separate program. A program that reads in an Icon expression and evaluates it is what's needed.

Such a program needs to parse the expression, read in as a string, and use string evaluation to evaluate it. Here's such a program:
   invocable all

   link ivalue

   procedure main()
      local line

      while line := read() do
         every write(eval(line))

   end

   procedure eval(expr)
      local p, args, tok

      &error := -1

      expr ? {
         p := trim(tab(upto('(')), '\t ') | {
            write(&errout, "** syntax error")
            fail
            }
         p := proc(p, 2 | 1 | 3) | {
            write(&errout, "** invalid operation")
            fail
            }
         move(1)
         args := []
         repeat {
            tab(many(' \t'))
            tok := trim(tab(upto(',)'))) | break
            put(args, ivalue(tok)) | fail
            move(1)
            }
         suspend p ! args
         }

   end
This program assumes functional form for input and it can handle "expressions" like +(2, 3), but it can't handle infix expressions like (2 + 3) or expressions involving control structures.

Error conversion is used so that syntax errors in the input do not cause run-time errors. The name for the operators is found and converted to a procedure. The procedure ivalue() from the Icon program library is used to convert arguments, which are placed on a list for subsequent invocation. It can handle literals and constants. ivalue() is a story in itself; see the Icon program library if you're interested.

As you'll note, this program does not handle nested expressions; we'll leave that as an "exercise". It's not easy to handle nested generators. For this, "think recursive generators" [1].

A Suffix Calculator

Perhaps the most common use of string invocation is to carry out "commands" entered by a user of an application. We described a suffix calculator for Icon in an earlier Analyst article [2]. Since we explained this program in some detail in that article, we'll just list the program here. Look at it in terms of the material described in this article.
   invocable all

   link ivalue
   link usage

   global stack

   procedure main()
      local line

      stack := []
      while line := read() do
         (operation | value | command)(line) |
            Error("erroneous input ",  image(line))

   end

   procedure command(line)

      case line of {
      "clear":  stack := []
      "dump":   every write(image(!stack))
      "quit":   exit()
      default:     fail
      }

      return

   end

   procedure operation(line)
      local p, n, arglist

      if p := proc(line, 2 | 1 | 3) then {
         n := abs(args(p))
         arglist := stack[-n : *stack + 1] | {
            Error("too few arguments")
            fail
            }
         stack := stack[1 : -n]
         &error := 1                             # anticipate possible error
         put(stack, p ! arglist) | {
            if &error = 0 then
               Error("error ", &errornumber, " evaluating ", image(line))
            else
               Error("failure evaluating ", image(line))
            stack |||:= arglist
            }
         &error := 0
         return
         }
      else fail
   end

   procedure value(line)

      put(stack,ivalue(line)) | fail

      return

   end

Conclusions

You may not use string invocation often in your Icon programs, but at least keep it in mind. It not only makes writing some programs much easier than they would be using other techniques, but it may suggest ways of designing programs that provide generality and flexibility that aren't feasible to provide in other ways.

References

1. "Recursive Generators", Icon Analyst 13, pp. 10-12.

2. "Anatomy of a Program - A Suffix Calculator", Icon Analyst 12, pp. 2-4.

Curiosity or Problem?

Bob Alexander noticed something apparently unusual about Icon's random number generator. Consider this program, in which the seed of the random number generator is set to successive even integers:
   procedure main()

      i := 20

      every &random := 0 to 30 by 2 do
         write(
            right(?i, 3), right(?i, 5), right(?i, 5),
            right(?i, 5), right(?i, 5), right(?i, 5),
            right(?i, 5), right(?i, 5), right(?i, 5),
            )

   end
The output is:
     5     9     7    11     9     7     2    15     2
     5    10     7     5     3    13    11    20     7
     6    10     7    19    16    19    19     5    11
     6    11     7    14    10     5     8    10    16
     7    12     7     8     3    11    16    15     1
     7    13     8     3    17    18     5    20     6
     8    14     8    17    11     4    13     5    11
     9    15     8    11     4    10     2    10    16
     9    15     8     6    18    16    10    15     1
    10    16     8    20    12     2    19    20     6
    10    17     8    15     5     9     7     5    11
    11    18     9     9    19    15    16    10    16
    11    19     9     3    13     1     4    15     1
    12    19     9    18     6     7    13    20     6
    12    20     9    12    20    14     1     5    11
    13     1     9     6    13    20    10    10    16
The regularities in the columns are less apparent for larger values of i, but they're definitely there.

Some persons are amazed at the results and think something is terribly wrong with Icon's random number generator. Other persons think the output is interesting but not surprising or worrisome, and common to all linear congruential random number generators.

Perhaps, if you're mathematically inclined, you can shed some light on this that we can pass along to readers of the Analyst.

[library setting]

From the Library

When we choose material from the Icon program library for these articles, we usually look for things that are the most useful. The Icon programming library also has many entertainments. These programs take several forms -- the library has games and puzzles, and also a few visual amusements. We've picked one of these for this article.

The program we've chosen produces symmetrical drawings. Symmetry is fascinating and has been the subject of much study and writing. See References 1-18 for some of the more accessible literature.

There's something about symmetrical designs that human beings find attractive. The reasons for this are not at all clear and lead to deep water (such as, does attraction to symmetric features have a survival value that might have had an evolutionary effect?).

To appreciate the power symmetry has on our perception, consider this relatively meaningless scribble:

[scribble]

This scribble looks, perhaps, like a sketch of a piece of coastline, but there's nothing particularly attractive about it. But now consider the design below, which was constructed by mirroring the scribble above in a symmetric fashion:

[symmetrical drawing]

This isn't art, but it's certainly more interesting than the scribble from which it was created. The symmetry used in this figure, one of the 17 plane symmetries, is called the sunflower symmetry in quilting [19] and carries the technical name p4m (or sometimes p4mm) in crystallography. The sunflower symmetry is produced by reflecting a drawing using two sets of mirrors:

[sunflower symmetry]

The library program symdraw, whose visual interface is shown below, lets a user create drawings with the sunflower symmetry:

[symdraw interface]

The drawing area at the right shows the axes of reflection as lines and one shaded octant. The shaded region is called the generating region, since anything drawn in it is reflected in the other octants. When a user presses and drags with the left mouse button with the mouse cursor in the generating region, a line is drawn following the mouse cursor, and this line is reflected in the other octants. Drawing stops if the mouse cursor moves outside the generating region.

Lines can be erased by using the right mouse button in a similar fashion. The middle mouse button is used to draw straight lines. A line begins where the middle mouse button is pressed and ends where it is released.

The File menu provides for saving a snapshot of the drawing (without the mirror lines and shading) and for quitting the application. The buttons at the left provide for clearing the drawing area, turning the lines and shading on and off, and restricting the drawing to the generating region. If drawing is not restricted to the generating region, drawing anywhere in the drawing area is reflected in all octants. Unrestricted drawing is easier and more fun, but it tends to produce less attractive results than restricted drawing.

The images produced by symdraw are black -and-white line drawings, but they easily can be colored in any painting application that provides a "paint bucket" or similar tool for filling areas. The best results for color usually are obtained if symmetrically placed areas are filled with the same color. Here's a grayscale image to give you an idea of the possibilities:

[grayscale pattern]

Images produced by symdraw tile seamlessly to produce larger repeat patterns such as the one shown here in reduced form.

[grayscale repeat]

symdraw was included in a recent update of the Icon program library that was sent to update subscribers. It will be included in the next general release of the library, which is scheduled for this summer.

At present, symdraw only supports the sunflower symmetry. This symmetry is relatively easy to implement, since drawing symmetric points involves only sign changes and exchanges of the x,y coordinates. There are other symmetries that would be even easier to implement, such as the "prickly pear" symmetry:

[prickley pear symmetry]

Other symmetries, such as the "turnstile" symmetry, require more complicated computations:

[turnstile symmetry]

References

1. D'Arcy Wentworth Thompson, On Growth and Form, Cambridge University Press, 1942.

2. Hermann Weyl, Symmetry, Princeton University Press, 1952.

3. L. Fejes Tóth, Regular Figures, Pergamon Press, 1964.

4. Gyorgy Kepes, ed., Module, Proportion, Symmetry, Rhythm, George Braziller, 1966.

5. F. J. Budden, The Fascination of Groups, Cambridge University Press, 1972.

6. A. V. Shubnikov and V. A. Koptsik, Symmetry in Science and Art, Plenum Press, 1974.

7. Peter S. Stevens, Patterns in Nature, Little, Brown, and Company, 1974.

8. Joe Rosen, Symmetry Discovered, Cambridge University Press, 1975.

9. E. H. Lockwood and R. H. Macmillan, Geometric Symmetry, Cambridge University Press, 1978.

10. Krome Barratt, Logic and Design in Art, Science, and Mathematics, Design Books, 1980.

11. Branko Grünbaum and G. C. Shephard, Tilings and Patterns, W. H. Freeman and Company, 1987.

12. Dorothy K. Washburn and Donald W. Crowe, Symmetries of Culture; Theory and Practice of Plane Pattern Analysis, University of Washington Press, 1988.

13.Doris Schattschneider, M. C. Escher; Visions of Symmetry, W. H. Freemen and Company, 1990.

14. Jay Kappraff, Connections; The Geometric Bridge Between Art and Science, McGraw-Hill, Inc., 1991.

15. Peters S. Stevens, Handbook of Regular Patterns, The MIT Press, 1991.

16. Ian Stewart and Martin Golubitsky, Fearful Symmetry; Is God a Geometer?, Penguin Books, 1992.

17. Michael Field and Martin Golubitsky, Symmetry in Chaos, Oxford University Press, 1992.

18. Michelle Emmer, ed., The Visual Mind; Art and Mathematics, The MIT Press, 1993.

19. Xaos Tools, Terrazzo; User's Guide, Macintosh Version 1.0, 1994.

Dynamic Analysis of Icon Programs (Continued)

In the last issue of the Analyst, we started a series of articles on the dynamic analysis of Icon programs -- what goes on during program execution. In this and subsequent articles, we'll explore various aspects of program execution in Icon. Before going on, we need to explain how we've chosen programs for analysis.

Analysis Test Bed

Since static analysis only depends on the text of programs, it can be applied to any program and it's not particularly difficult to select programs to analyze. In earlier static analyses, we used the entire Icon program library.

Choosing programs for dynamic analysis is more difficult. Dynamic analysis, unlike static analysis, is very time consuming. It's impractical to apply the same dynamic analysis to a large number of programs. We decided to pick about a dozen programs. Selecting even this few was a problem.

In order to perform dynamic analysis on a program, the program needs to perform enough computation to produce meaningful results. In order for analysis to be practical, a program also must run in batch mode. Appropriate test data also is needed. In order to make comparisons between programs, they also need to run for approximately the same amount of time.

We chose our programs for dynamic analysis from the Icon program library so that persons interested in dynamic analysis would have easy access to the programs. There are 285 programs in the Version 9 program library. You'd think it would be easy to find a dozen that are suitable, but it wasn't. Most of the graphics programs and many others are unsuitable because they can't be run in batch mode. We also lack test data for most of the remaining programs. In some cases we were able to create test data, but for others it wasn't possible to do this without investing more time and effort than we could afford. Among the remaining candidates, many were unsuitable because they weren't designed to perform the extensive computation needed to make the results of dynamic analysis useful.

We finally managed to find 11 programs that met our needs. These programs are by no means representative of Icon programming -- if such a concept even is meaningful. Here are the ones we've chosen:
      program         functionality

      csgen.icn       sentences from context-free grammars
      deal.icn        randomly dealt bridge hands
      fileprnt.icn    character display of files
      genqueen.icn    solutions to the n-queens problem
      iiencode.icn    text encoding for files
      ipxref.icn      cross references for Icon programs
      kwic.icn        keyword-in-context listings
      press.icn       file compression
      queens.icn      solutions to the n-queens problem
      rsg.icn         sentences from context-free grammars
      turing.icn      Turing machine simulation
The choice of two programs for the n-queens problem was deliberate; the methods used in the two programs are different, and we thought it would be interesting to compare them.

In retrospect, after performing extensive dynamic analyses on these programs, we're not particularly satisfied with our choices. Some of the programs use specific features of Icon that aren't used in many programs, and it's easy to draw unwarranted conclusions if these programs are taken to be representative. For now, we'll present the result we have with a caution about drawing such conclusions.

The Evaluation of Functions and Operators

In the last issue of the Analyst, we showed a summary report of function events for one program (iiencode.icn), which we repeat here for reference:
           event                   count

      function call               197393
      function failure               441
      function return             190679
      function suspension           6272
      function resumption              0
      function suspension removal   6272
This summary only provides information for functions, and for them, only the aggregate activity.

With the instrumentation provided by MT Icon and support procedures that have been developed for dynamic analysis, it's relatively easy to get information not only for individual functions but for operations as well. The following listing shows the results for iiencode.icn, ordered by decreasing number of calls:
name          calls    returns   suspends   failures    resumps   removals

iand()        56867      56867          0          0          0          0
e1[e2]        37634      37634          0          0          0          0
ord()         37632      37632          0          0          0          0
ishift()      31360      31360          0          0          0          0
writes()      25929      25929          0          0          0          0
e1 := e2      25514      25514          0          0          0          0
e1 + e2       25509      25509          0          0          0          0
char()        25507      25507          0          0          0          0
e1 ~== e2     25507      24971          0        536          0          0
-e            18816      18816          0          0          0          0
+e            12544      12544          0          0          0          0
ior()         12544      12544          0          0          0          0
move()         6691          0       6272        419          0       6272
*e              421        421          0          0          0          0
reads()         420        419          0          1          0          0
pos()           419        419          0          0          0          0
find()           21          0          0         21          0          0
/e                5          4          0          1          0          0
\e                3          0          0          3          0          0
e1 === e2         2          0          0          2          0          0
close()           2          2          0          0          0          0
e1 <= e2          2          1          0          1          0          0
e1 to e2 by e3    1          0          21         1         21          0
exit()            1          0          0          0          0          0
e1 :=: e2         1          1          0          0          0          0

total        343352     336094       6293        985          21      6272
From this listing alone, we can determine many things about the program. The extensive use of iand(), ior(), ishift(), ord(), and char() suggests the kind of computation the program is doing, which is atypical; few Icon programs use these functions. The large number of uses of e1[e2] is interesting, and we'll have more to say about this later. The use of move(), pos(), and find() shows the program uses string scanning. In the column for suspensions, there are only two nonzero entries. move() always suspends so that it can restore the cursor position if it is resumed. Note, however, that move() never is resumed, indicating there is no backtracking in string scanning in this program. The only generator used in this program is e1 to e2 by e3, which almost certainly occurs in every-do, not goal-directed evaluation. In fact, Icon's expression-evaluation mechanism probably plays no significant role in this program. (To be sure of that, we'd have to have information on procedure activity. This is easy to get but is confusing when given in combination with the activity of built-in expressions.)

A plausible conclusion is that this program could have been written in a lower-level language like C without much of a change in structure.

Now look at the corresponding listing for csgen.icn:
name          calls    returns   suspends   failures    resumps   removals

e1[e2]        81343      81343          0          0          0          0
find()        70771          0       5303      65468          0       5303
?e            47869      47869          0          0          0          0
tab()         10608          0      10608          1          1      10607
e1 || e2      10568      10568          0          0          0          0
move()         5307          0       5306          1          0       5306
*e             5284       5284          0          0          0          0
e1 := e2       2864       2864          0          0          0          0
upto()         2812          0       2643        170          1       2642
\e             2643          0          0       2643          0          0
!e             2642          0      22882          0      20240       2642
write()         150        150          0          0          0          0
e1 == e2         24          2          0         22          0          0
read()           22         22          0          0          0          0
[...]            21         21          0          0          0          0
e1 to e2 by e3   20          0        169         20        169          0
put()            19         19          0          0          0          0
/e                3          1          0          2          0          0
integer()         2          1          0          1          0          0
table()           2          2          0          0          0          0
get()             1          0          0          1          0          0
any()             1          0          0          1          0          0
e1 < e2           1          1          0          0          0          0
pull()            1          0          0          1          0          0

total        242978     148147       46911     68331      20411      26500
This program shows extensive use of generators. Most of the resumptions, however, are for !e, which probably is used in every-do, not in goal-directed evaluation. The use of string scanning clearly is much more extensive in this program than in iiencode.icn. In other words, csgen.icn is more "Icon-ish" than iiencode.icn.

Next Time

We've run out of space. In the next article in this series, we'll show a composite for expression activity in all 11 test programs and then go on to other aspects of program execution.

Subscription Renewal

For many of you, the next issue is the last in your present subscription to the Analyst and you'll find a subscription renewal form in the center of this issue. Renew now so that you won't miss an issue.

Your prompt renewal also helps us in planning and by reducing the number of follow-up notices we have to send.

[masthead]

What's Coming Up

We have several articles in the works -- several on dynamic analysis, material in the Icon program library, one on integers and the design problems related to large-integer arithmetic, and the first of a series on building applications with visual interfaces. We also are planning a series of articles that go into some depth on the way things are implemented in Icon, especially how lists, sets, and tables are implemented.

We not yet sure what will appear in the next issue of the Analyst, but more on dynamic analysis is a good bet.


© Copyright 1995 by Madge T. Griswold and Ralph E. Griswold. All rights reserved.
Icon home page

Icon Analyst