Richard Suchenwirth 2005-04-02 - Say you want to make a multiplication table for an elementary school kid near you. Easily done in a few lines of Tcl code:
proc multable {rows cols} { set res "" for {set i 1} {$i <= $rows} {incr i} { for {set j 1} {$j <= $cols} {incr j} { append res [format %4d [expr {$i*$j}]] } append res \n } set res }
The code does not directly puts its results, but returns them as a string - you might want to do other things with it, e.g. save it to a file for printing. Testing:
% multable 3 10 1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30
Or print the result directly from wish:
catch {console show} puts "[multable 3 10]"
Here's a different way to do it à la functional programming:
proc multable2 {rows cols} { formatMatrix %4d [outProd * [iota 1 $rows] [iota 1 $cols]] }
The body is nice and short, but consists of all unfamiliar commands. They are however better reusable than the multable proc above. The first formats a matrix (a list of lists to Tcl) with newlines and aligned columns for better display:
proc formatMatrix {fm matrix} { join [lmap row $matrix {join [lmap i $row {format $fm $i}] ""}] \n }
hort again, and slightly cryptic, as is the "outer product" routine, which takes a function f and two vectors, and produces a matrix where f was applied to every pair of a x b - in APL they had special compound operators for this job, in this case "°.x":
proc outProd {f a b} { lmap i $a {lmap j $b {$f $i $j}} }
Again, lmap (the collecting foreach) figures prominently, so here it is in all its simplicity:
proc lmap {_var list body} { upvar 1 $_var var set res {} foreach var $list {lappend res [uplevel 1 $body]} set res } #-- We need multiplication from [expr] exposed as a function: proc * {a b} {expr {$a * $b}} #-- And finally, ''iota'' is an [integer range generator]: proc iota {from to} { set res {} while {$from <= $to} {lappend res $from; incr from} set res }
With these parts in place, we can see that multable2 works as we want:
% multable2 3 10 1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30
So why write six procedures, where one did the job already? A matter of style and taste, in a way - multable is 10 LOC and depends on nothing but Tcl, which is good; multable2 describes quite concisely what it does, and builds on a few other procs that are highly reusable. Should you need a unit matrix (where the main diagonal is 1, and the rest is 0), just call outProd with a different function (equality, ==):
% outProd == [iota 1 5] [iota 1 5] {1 0 0 0 0} {0 1 0 0 0} {0 0 1 0 0} {0 0 0 1 0} {0 0 0 0 1}
which just requires expr's equality to be exposed too:
proc == {a b} {expr {$a == $b}}
One of the fascinations of functional programming is that one can do the job in a simple and clear way (typically a one-liner), while using a collection of reusable building-blocks like lmap and iota. And formatMatrix and outProd are so general that one might include them in some library, while the task of producing a multiplication table may not come up any more for a long time...
SS agrees 100% with the spirit of this page, that's why Jim has lmap and range (that is similar to the iota of this page).