Multiplication tables

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