General:.
:for (iter [.fl]) {(args[.fl]){code}}
Above you see the general form of the :for structure. The
italicized portions are optional. :for directs code to be
executed multiple times for a sequence of inputs.These inputs are called
iterators and there can be as many of them as you like. The control token is
:for. This must be followed by a parenthesis token
"(...)" and a block token
"{...}". These two additional tokens may be simple or
complex as the examples will illustrate. The :for structure may or may
not retain results from each iteration. If it does, it will return a sequence
containing those results as elements. To get this effect, do not terminate the
code with a semicolon. If you terminate the code with a semicolon
you are probably displaying results within the loop rather than capturing. In
this case, an empty sequence is returned. Within the block token your code may
contain ":break" and or ":continue"
tokens. The ":break" exits the structure. The
":continue" begins the next iteration. Iterators are
vectors. Glee will find the longest iterator and do that number
of loops. It does not use right-take to make all other iterators conform
to this length. It cycles through the elements of the iterators for each
iteration until it has run the block for all of them. Iterators exhausted early
are taken as nil. The :for control structure then exits with
the result.
Simple iteration: Using the
anonymous iteration vector (1..3) this :for loop just runs
the block three times. It pays no attention to the iterator value. It has no
clue an iteration value even exists. Since there is no semicolon terminator,
the last instruction is saved as an element in the sequence result. Using the
(,,) expose with space separator operator, we see the results.
Using the iterator: The
iterator can be named and passed into the block as shown in this example. The
block receives a single element object copy with each pass through the loop.
Variable as iterator:
This example uses a sequence of words as the iterator. Each iteration is
displayed using the $. The block is terminated with a semicolon so no
results are collected. Thus, an empty sequence is returned.
Multiple Iterators: This
example has several stories to tell. I begin by clearing the namespace;
defining 3 accounts; and setting the random seed so you see the same results as
this example. (#ns=>@; 3`=>accts; 10=>?; ). I run a
:for loop to create some sample sales data for each account (:for
(accts) {10*->4?} => sales;). This creates a 3-element sequence of
4-element numeric objects. I iterate through both of these variables with
":for (accts sales[.a.s]){". Indexing into a sequence (which
the :for is doing implicitly) returns a sequence. However, the
:for mechanism automatically discloses the indexed element so you
don't have to. Thus, the iterator for sales is a single element
numeric vector when delivered into the block's namespace. The instruction
"s \+ => t;" gives me the total for the account. Next,
('acct: 'a'; sales: 's'; total:' (s \+)$;" should be familiar. I
just assemble a sequence of results and display them with ($). Notice
I follow this with "t}". Since I don't terminate with a
semicolon, this lets me collect the totals for each account as an element of a
resulting sequence. Finally I total all the acount totals with "}<
\+". Notice again, I must expose the sequence result to obtain the
numeric object which I can then sum.
Affecting outside objects:
Usually within a loop you want to affect objects outside the loop. This example
shows you how. I set up an interator and a sequence for collecting results
"3` => count; 0> ->(count#)=>res;". Setting up
the iteration is a familiar ":for (count[.i])". I now set up
the block. The instruction "{res@[.r]{ " takes a reference
to the result and delivers it within the block as "r". The
instruction "i`=>[i]r;" generates an index vector and
indexes it into my result through the reference "r". The
instruction "res %**$; displays the "res"
object to contain my expected result.
Using the iterator: Pay close
attention to this example. It uses the expression ((1..3>)[.i]).
This produces a namespace with the object "i" containing a
one element sequence. That element is a three element numeric vector. This
namespace is the argument to the block {i}. The :for
structure delivers "i" elements one at a time. The block
returns them as a result. The ":for" collects these results
delivering the expected three element sequence of numerics. Notice what happens
when I fail to deliver a sequence. The example "$V; :for ((5..2)[.i])
{i} $;" has "(5..2)" rather than
"(5..2>)". The argument ".i" only gets
the "5" assuming subsequent arguments will get the
"4,3,and 2". Therefore, we have a one iteration
loop where the iterator "i" has the value 5. This is not
what we wanted. In the last example "$V; :for ((1..3>)[.i]) {i;}
$;", the block ends with a semicolon so produces no result. Thus, the
:for structure returns a numeric count of the number of iterations it
performed. Of course this is of no value whatsoever unless we can interact with
the "outside world" and return useful work. The next examples will
show you how.
Multiple Iterators; Update
outside object: In this example, several things are going on. First, I am
using the external object "t" as an accumulator beginning
with an initial value of "10". I have two iterators
"1..3=>x;" and "11..15=>y;". The
"y" iterator is longer by 2 elements. I pass these iterators
into the block as "i" and "j" with
":for(x y[.i.j])". Notice, since I used strand notation, I
didn't have to explicitly sequence-ize them with enclosure as I did in the
previous examples. I now have a namespace which I pass to the block. Stepping
inside the block I create another namespace for its use with
"t@[.t]{". This delivers a reference to the external
"t" to be used internally also as "t".
Since it is a reference, I can change its value by the expression
"i*j+t=>t;" and have that change be effected externally
as well. "'i='i'; j='j'; t='(t*)'; '$;" displays the values
and result as I loop. The last instruction, "t*"
dereferences "t". Since it is not followed by a semicolon
and is the last instruction before restarting the loop, the value of
"t" is collected in the result sequence with each pass
through the loop. "$V; r $; $v;" displays the resulting
sequence in verbose mode so you can see it is a sequence of single element
numerics. With "$V; r< $; $v;", since the sequence is
homogeneous, the disclosure "r<" delivers a numeric
vector which I may use for subsequent vector processing. Notice how when I
exhaust the "x/i" iterator, it is taken as
nil.
Double :for loop: On
the surface, there is nothing particularly radical in this example.The
:for iterator "i" is passed to the block as an
argument iterating through its elements. That block has a :for
iterator "j" which it passes to that block in the same way.
The second block is now within the first block. Neither of the blocks can
change the iterators to any effect. They are copies. The new line (return)
character is created and saved in a variable with (13#asc=>nl;). By
including this in the sequence and then using the convert to string
operator ( %* ) on the sequence, we have a string result for each pass
through the second loop. This is where things are a little radical. The inner
loop result is a sequence of two elements, each containing one line of output.
The outer loop result is a sequence of three elements each containing the
result from a pass through the inner loop (which is a sequence of two
elements). Glee displays it cleanly. But if you viewed the result
in verbose mode you would see its sequence underpinnings. Of course we could
use the %* operator on the overall result sequence and have our result
as a character string with embedded linefeeds.
Mulitple iterators: A
:for loop can have multiple iterators. In this example, we have a
sequence of customer names and a sequence of account numbers. We loop through
both at the same time to produce a little report. The result of this is a
sequence with three elements containing strings for each line of the report.
The " ,,\" does the expose with lf separators.
:continue and
:break: This example is a little pressure guage monitor with a
safety shutoff. It demonstrates the :continue and :break
control tokens. These are typically used within looping structures to control
processing. Here we have pressure building from 1 to 8. 1 and 2 are too low and
the :if block that fires to report this. 3 is just right and no
:if blocks fire. 4 is too high and a warning is issued. 5 requires
shut down and we exit when encountering the :break control token. The
:if blocks that annunciate status and encounters the
:continue control token causing the loop to immediately recycle from
the beginning. Note that the final return value is 5. That is the iteration we
were on when we saw the :break and exited the :for control
structure. Our results were followed by semicolons so no sequence of results
was collected. To have results collected, all blocks must return results (i.e.
must not end in a semicolon). For fun, you might comment-out the shut down
block (e.g. $$ :if (gauge=5){gauge " !
shut down"$; :break;};) to show it is protecting the system.