if 0 {[Richard Suchenwirth] 2005-04-20 - [APL] and J (see [Tacit programming]) have the feature that arithmetics can be done with vectors and arrays as well as scalar numbers, in the varieties (for any operator @): * scalar @ scalar -> scalar (like [expr] does) * vector @ scalar -> vector * scalar @ vector -> vector * vector @ vector -> vector (all of same dimensions, element-wise) Here's experiments how to do this in Tcl. First [lmap] is a collecting [foreach] - it maps the specified body over a list:} ====== proc lmap {_var list body} { upvar 1 $_var var set res {} foreach var $list {lappend res [uplevel 1 $body]} set res } #-- We need basic scalar operators from [expr] factored out: foreach op {+ - * / % ==} {proc $op {a b} "expr {\$a $op \$b}"} ====== if 0 {This generic wrapper takes one binary operator (could be any suitable function) and two arguments, which may be scalars, vectors, or even matrices (lists of lists), as it recurses as often as needed. Note that as my [lmap] above only takes one list, the two-list case had to be made explicit with [foreach]. [Jim] users can use a multi-list [lmap] there for simplicity.} ====== proc vec {op a b} { if {[llength $a] == 1 && [llength $b] == 1} { $op $a $b } elseif {[llength $a]==1} { lmap i $b {vec $op $a $i} } elseif {[llength $b]==1} { lmap i $a {vec $op $i $b} } elseif {[llength $a] == [llength $b]} { set res {} foreach i $a j $b {lappend res [vec $op $i $j]} set res } else {error "length mismatch [llength $a] != [llength $b]"} } #-- Tests: proc e.g. {cmd -> expected} { catch $cmd res if {$res ne $expected} {puts "$cmd -> $res, not $expected"} } #-- Scalar + Scalar e.g. {vec + 1 2} -> 3 #-- Scalar + Vector e.g. {vec + 1 {1 2 3 4}} -> {2 3 4 5} #-- Vector / Scalar e.g. {vec / {1 2 3 4} 2.} -> {0.5 1.0 1.5 2.0} #-- Vector + Vector e.g. {vec + {1 2 3} {4 5 6}} -> {5 7 9} #-- Matrix * Scalar e.g. {vec * {{1 2 3} {4 5 6}} 2} -> {{2 4 6} {8 10 12}} #-- Multiplying a 3x3 matrix with another: e.g. {vec * {{1 2 3} {4 5 6} {7 8 9}} {{1 0 0} {0 1 0} {0 0 1}}} -> \ {{1 0 0} {0 5 0} {0 0 9}} ====== if 0 { ---- [DKF]: Note that the dot product of two vectors is a scalar... ;^) [RS]: Sure, but that's easily had too, given a ''sum'' function: ====== proc sum list {expr [join $list +]+0} sum [vec * {1 2} {3 4}] ====== should result in 11 (= (1*3)+(2*4)). ---- Here's a little application for this: a vector factorizer, that produces the list of divisors for a given integer. For this we again need a 1-based [integer range generator]:} ====== proc iota1 x { set res {} for {set i 1} {$i<=$x} {incr i} {lappend res $i} set res } e.g. {iota1 7} -> {1 2 3 4 5 6 7} #-- We can compute the modulo of a number by its index vector: e.g. {vec % 7 [iota1 7]} -> {0 1 1 3 2 1 0} #-- and turn all elements where the remainder is 0 to 1, else 0: e.g. {vec == 0 [vec % 7 [iota1 7]]} -> {1 0 0 0 0 0 1} ====== if 0 {At this point, a number is prime if the sum of the latest vector is 2. But we can also multiply out the 1s with the divisors from the i ndex vector:} ====== e.g. {vec * [iota1 7] [vec == 0 [vec % 7 [iota1 7]]]} -> {1 0 0 0 0 0 7} #-- Hence, 7 is only divisible by 1 and itself, hence it is a prime. e.g. {vec * [iota1 6] [vec == 0 [vec % 6 [iota1 6]]]} -> {1 2 3 0 0 6} ====== if 0 {So 6 is divisible by 2 and 3; non-zero elements in (lrange $divisors 1 end-1) gives the "proper" divisors. And three nested calls to ''vec'' are sufficient to produce the divisors list :) Just for comparison, here's how it looks in J: iota1=.>:@i. iota1 7 1 2 3 4 5 6 7 f3=.iota1*(0&=@|~iota1) f3 7 1 0 0 0 0 0 7 f3 6 1 2 3 0 0 6 ---- See also [lexpr] for a precursor of this page. <> Concept | Arts and crafts of Tcl-Tk programming | Mathematics }