Previous Table of Contents Next


Scanning Environments

The subject and the position in it constitute a scanning environment. Before beginning a scanning expression, the current scanning environment is saved, and it is restored when the new scanning expression terminates. This allows scanning expressions to be nested.

An example is

    while line := read() do {
       result := “”
       line ? {
          while field := tab(upto(‘,’)) do {
             field ?  {
                 if =”X” then next
                }
             result ||:= left(field, Width)
             move(1)
             }
          }
       write(result ||:= left(tab(0), Width))
       }

which omits fields that begin with “X”.

The function pos(i) succeeds if the current position in the subject is i but fails otherwise. The argument of pos(i) can be a nonpositive specification. For example, pos(–1) succeeds if the position is before the last character in the subject.

The keywords &subject and &pos contain the values of the subject and position in the current scanning environment. They change when the scanning environment changes.

The position in the subject can be changed explicitly by assignment to &pos. For example,

    &pos := 1

sets the position to the beginning of the subject. Assignment to &pos fails if the position would be outside the range of the subject.

In most situations, it is not necessary to refer to &subject and &pos explicitly. There are situations, however, in which this is useful. See section 6.3.2.

Procedure calls do not change the scanning environment. This allows procedures to be used for scanning within a scanning expression. An example is

    procedure blankx()

       if =”X” then return “ “ else return tab(0)

    end

which could be used to replace fields that begin with an ”X” by blank fields, leaving other fields unchanged, as in

    while line := read() do {
       result := “”
       line ? {
          while field := tab(upto(‘,’)) do {
             field := {
                field ? blankx()
                }
             result ||:= left(field, width)
             move(1)
             }
          }
       write(result ||:= left(tab(0), width))
       }

6.2.4. Structures

Icon provides four kinds of structures for organizing and accessing collections of values in different ways:

  records, which are fixed in size and accessed by named fields
  lists, which are one-dimensional arrays that can be accessed by position or as stacks and queues
  sets, which are collections of distinct values
  tables, which provide associative lookup

Structures are created during program execution. The values in structures can be of any type, and a structure can contain values of different types. All structures except records can change in size during program execution.

6.2.4.1. Records

Records are declared, as in

    record rational(numer, denom)

which declares a record named rational with field names numer and denom.

Instances of records are created during program execution by using a function whose name corresponds to the record name and whose arguments correspond to the fields, as in

    portion := rational(2, 3)

A record declaration adds a type corresponding to the record name to Icon’s built-in type repertoire. For example, type(portion) produces “rational”.

Record fields are accessed by name using the binary dot (.) operator, as in

    portion.numer := 1

which changes the numer field of portion to 1.

Record fields also can be accessed by position, as in

    portion[2] := 5

which changes the denom field of portion to 5.

6.2.4.2. Lists

A list consists of a sequence of zero or more values. A list is created by an expression in which the values are enclosed in square brackets, as in

    primaries := [“red”, “blue”, “green”]

A list also can be created by the function list(i, x), which produces a list of i values, all of which are x. For example,

    grades := list(35, 0)

assigns to grades a list of 35 values, all of which are 0.

An empty list, which contains no values, can be produced by [] or list(0).

The values in a list are called elements. The elements in a list can be accessed by using subscripts, as in

    write(primaries[2])

which writes blue. Elements also can be changed by assigning to a subscript, as in

    grades[21] +:= 10

which adds 10 to the twenty-first element of grades.

Nonpositive specifications can be used for subscripting lists. For example,

    write(primaries[–1])

writes green.

Two lists can be concatenated to produce a new list using the operation L1 ||| L2, as in

    newgrades := grades ||| list(5, 0)

which assign to newgrades a list of 40 values.

The section operation, L[i:j], produces a new list consisting of elements i through j of L.

6.2.4.3. Using Lists as Queues and Stacks

There are five functions for adding or removing values from the ends of lists, allowing lists to be used as stacks and queues. In this usage, the first element of a list is at its right end and the last at its left. The functions are

put(L, x)—Puts x on the right end of L
push(L, x1)—Pushes x onto the left end of L
get(L)—Removes the leftmost element of L and produces its value; get() fails if L is empty
pop(L)—Is a synonym for get()
pull(L)—Removes the rightmost element of L and produces its value; pull() fails if L is empty

The way these functions work is illustrated by this diagram:

An example of using stack and queue access is this code segment that writes the input file in reverse order:

    lines := []                  # start with empty list

    while push(lines, read())    # add lines at beginning

    while write(get(lines))      # write lines from beginning to end


Previous Table of Contents Next