Control Structures: (:for) Commentary

: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.