- scalar @ scalar -> scalar (like expr does)
- vector @ scalar -> vector
- scalar @ vector -> vector
- vector @ vector -> vector (all of same dimensions, element-wise)
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.
}