Expression Pattern Language (eXPL)

Calculator Loops

Procedural execution in eXPL is constrained by it's unification engine to progress in a linear fashion. A loop has just one point of entry and departure so can be inserted in a program anywhere without causing a discontinuity. Nesting of loops inside loops is also not a problem.

There are 2 types of loops:

  • Conditional Block - A short circuit expression controls entry into a block from which the program exits when the end of the block is reached. The block is a one-transit loop. There are 2 short circuit operators, ? and :. The ? operator has already been encountered and has the sense "short circuit on false". The : operator has the opposite sense - "short circuit on true". To short circuit the conditional block means to not enter it.
  • Unconditional Loop - There is no expression controlling entry into the loop and the only way out of the loop is by a short circuit operation. Beware!. The loop can become an infinite loop if a bug in the code prevents any of the short circuit expressions inside the loop from evaluating an exit condition.

A loop is only allowed in a calculator and is defined in the code as a term sequence inside a set of braces {}. The following example has a single unconditional loop with a ? expression exit.

Unconditional Loop Example

The Factorial application of tutorial8 generates the factorial of 4 (2*3*4 = 24) and 5 (2*3*4*5 = 120).

calc factorial (
  integer n,
integer i = 1,
  integer factorial = 1,
  {
factorial *= i,
? i++ < n
  }
);
query factorial4(factorial)(n = 4);
query factorial5(factorial)(n = 5);

Conditional Block Example

Application CalculateSquareMiles2 in tutorial8 contains a conditional sequence to adjust nation surface area from km2 to square miles if the country is USA.

calc filter_area (
  country { "United States" , "Australia" },
...
  // Use imperial measurements if country is USA
  ? country == "United States"
  {
    surface_area *= 0.3861,
    units = "mi2"
  }
);

The result shows surface areas for Australia and USA in Km2 and mi2 respectively:

country=Australia, surface_area=7,741,220, units=km2
country=United States, surface_area=3,795,946.011, units=mi2

Insert Sort

Calculator allows normal data processing operations such as sorting to take place. An example of an insert sort is in application HighCitiesSorted in tutorial8. This produces a list of cities sorted by altitude. A loop is used to shuffle cities, as needed, to keep the list of cities in sort order as it grows. Here is the loop:

{
? altitude < high_cities[j].altitude,
high_cities[j + 1] = high_cities[j],
? --j >= 0
},

There are 2 short circuit expressions in the loop, the first is to exit the loop when the correct sort order is achieved and the second is an exit when the end of the list is reached. The loop variable "j" provides both a list index offset and list position tracker. A cursor can also be used to access the list inside the loop, which makes it clearer that the loop is shuffling items one place towards the end of the list. This is what a cursor shuffle looks like, the cursor being named "sorter":

sorter[1] = sorter--,

Nested Loops

Loops can be nested inside loops. An example is the NestedLoops application of tutorial8. This is another insert sort, but performed on the terms of an axiom, so there is an additional outer loop to traverse the terms of the axiom.

axiom unsorted() {12, 3, 1, 5, 8};

calc insert_sort
+ list<term> sorted(unsorted);
(
i = 1,
{
  j = i - 1,
  temp = sorted[i],
  {
    ? temp < sorted[j],
    sorted[j + 1] = sorted[j],
    ? --j >= 0
  },
  sorted[j + 1] = temp,
  ? ++i < sorted.length
}
)
query sort_axiom (insert_sort);

The most important thing to remember about nested loops is that all variables in a calculator belong to the same namespace and are visible at all nest levels. This means all variables within the calculator must have unique names.