Sum of elements/numbers in N lists in Tcl? - tcl

How to add elements/numbers from n lists in tcl ?
I have tried in python and it worked using below:
[sum(x) for x in zip(*C)]
How to do this in tcl?
Is there a zip function in tcl?
Is there any other way to achieve this ?
I have 2 lists for now:
l1 {11 333 4 567 129}
l2 {23 47 56 10 13}
I can have N-number of lists
I need to return element wise sum from these lists.
for two lists I implemented below :
set result {}
foreach x $l1 y $l2 {
lappend result [expr {$x + $y}]
}
My concern is: I can have one or many lists. So in that scenario how do I implement?

To get a new list of the pairwise sums of the elements of two lists:
set result [lmap a $list1 b $list2 { expr {$a + $b} }]
Make the obvious addition for three lists, four lists, etc.

There are no function similar to zip in Tcl as far as I'm aware. You can however create one if you want to. A rough one might be like this:
proc zip {lists} {
set result {}
for {set i 0} {$i < [llength [lindex $lists 0]]} {incr i} {
set elem {}
foreach list $lists {
if {$i >= [llength $list]} {return $result}
lappend elem [lindex $list $i]
}
lappend result $elem
}
return $result
}
Then you can use it like this:
set l1 {11 333 4 567 129}
set l2 {23 47 56 10 13}
set ln [list $l1 $l2]
set sum [lmap x [zip $ln] {::tcl::mathop::+ {*}$x}]
# 34 380 60 577 142

Then you don't want a variable per list, you want something else like a list of lists.
This is my take on the above recommendation by Shawn:
set l1 {11 333 4 567 129}
set l2 {23 47 56 10 13}
lappend l $l1 $l2
proc zippedSum {p} {
set inner [llength [lindex $p 0]]
set result [list]
for {set i 0} {$i < $inner} {incr i} {
lappend result [::tcl::mathop::+ {*}[lmap sublist $p {lindex $sublist $i}]]
}
return $result
}
zippedSum $l
You would not maintain separate variables, but a list of lists (l). Then you would process the sublists per index (with the maximum index determined by the first sublist), using the lmap/lindex combo recommended elsewhere for what you call zipping.

Related

To add preceding digits in a list in Tcl

I am trying to incorporate the preceding digits in a numerical list, say 1-14, before the list 15-43.
I am using TCL for writing my code.
I am expecting the list should be 1,2,3,....43, instead the list is coming to be 15,16,17,...43.
I have tried to incorporate the missing numbers as follows:
set nres2 ""
set x 0
set y [lindex $nres $x]
while {$x < [llength $nres]} {
set i [lindex $nres $x]
while {$y < $i} {
lappend nres2 $y
incr y
}
incr x
incr y
}
This will incorporate missing numbers within a list like, if a list 15,16,17...,43 do not have numbers like 18, 22, 34, etc., it will incorporate these numbers in a separate list named as nres2.
But, I cannot include the preceding numbers of a corresponding list.
Any comments/suggestions will be greatly helpful.
Thanks in advance...
Prathit Chatterjee
I would change the y at the start:
set nres2 ""
set x 0
set y 1
while {$x < [llength $nres]} {
set i [lindex $nres $x]
while {$y < $i} {
lappend nres2 $y
incr y
}
incr x
incr y
}
With nres as 4 6 8 9, nres2 becomes 1 2 3 5 7, which is what I think you are trying to get.
Jerry posted already the immediate fix, but you may think about general improvements over your implementation using pairwise comparisons:
proc printMissings1 {lst} {
set b [lrepeat [lindex $lst end] 0]
foreach i $lst {
lset b $i 1
}
lsearch -exact -integer -all $b 0
}
printMissings1 $nres
lrepeat is used to create a Tcl list (array) of [llength $nres] elements.
lset is used to mark the elements whose indices correspond to values in the given range.
lsearch is used to return the indices of the elements left unmarked, which correspond to the missing values.

TCL Get the value of a second level variable

I à stuck with a TCL issue. I would like to have access to the content of a second level variable with keeping the format (list).
please see my code :
At first, I declare variable contents
set x1y {1 2 3 4}
set x2y {10 11 12 13}
After I perform a for loop
for { i 0} {i < 4} { incr i}
I would like to have in xy variable the content of x1y with keeping the list format
set xy [eval ["x${i}y"]]
foreach x $xy {
....
}
Do you have any idea / proposal. I tried subst but it doesn’t keep the format.
Thank you in advance
The way to read from a variable whose name is not a constant is to use the single argument form of set:
set x1y {1 2 3 4}
set x2y {10 11 12 13}
foreach i {1 2} {
foreach val [set x${i}y] {
puts "$i --> $val"
}
}
However, it is usually easier to make an alias to the variable with upvar 0, like this:
foreach i {1 2} {
upvar 0 x${i}y xy
foreach val $xy {
puts "$i --> $val"
}
}
And in almost every case where you're doing this, you should consider using arrays instead (remembering that Tcl's arrays are associative arrays; you can use compound keys as well as simple integers):
set xy(1) {1 2 3 4}
set xy(2) {10 11 12 13}
foreach i {1 2} {
foreach val $xy($i) {
puts "$i --> $val"
}
}
You probably want to try to avoid using eval or subst for this sort of thing; those commands have side effects that may hurt the stability of your code if you're not careful. Definitely not ones for cases like these. (Also, they'll be slower as they force Tcl to recompile its internal bytecode more frequently. All the solutions I present above don't have that misfeature.)
set x1y {1 2 3 4}
set x2y {10 11 12 13}
for {set i 1} {$i <= 2} {incr i} {
foreach e [set x${i}y] {
puts $e
}
}

Check if element exist in list, if it doesn't append to the list

I want to have a list of items, namely coordinates of a point (x,y,z). Now, keeping value of x same i want to increase the values of y and z and do something inside the for loops.
set x [string range $x1 1 $lgthx];
set lst_nodes [list $x 0 0];
for { set y 0} {$y < 1000} {incr y} {
for { set z 0} {$z < 1000} {incr z} {
# Here i want to check if the item is present in the list or not
set lst_nodes [lappend $lst_nodes [<Do something here> $x $y $z]];
}
}
I tried many options to make this work. But, i am getting errors like for the value of x: invalid command name "215.5623"
lappend works on variable name, not the variable itself, so [lappend lst_nodes [<Do something here> $x $y $z]];.
Also, lappend modifies the list. I'm not sure why you are using set.
Assuming that with the above changes the script works, then to do the check, I would use something like this (ni means 'not in' and returns true if an element is not present in a list):
set x [string range $x1 1 $lgthx]
set lst_nodes [list [list $x 0 0]]
for {set y 0} {$y < 1000} {incr y} {
for {set z 0} {$z < 1000} {incr z} {
# Here i want to check if the item is present in the list or not
set new_node [<Do something here> $x $y $z]
if {$new_node ni $lst_nodes} {
lappend lst_nodes $new_node
}
}
}
I wrapped [list $x 0 0] into another list because I believe you are keeping a list of lists (these being the coordinates). If you are not, then you need to append each element to the list instead of appending a list (the new node) using list expansion lappend lst_nodes {*}$new_node.

How to read number count of words?

How to read number count of words?
Lines has this format:
vertices_count
X, Y
X, Y
X, Y
(X, Y pair can be in the same line)
for example:
3
12.5, 56.8
12.5, 56.8
12.5, 56.8
I would like to read vertices_count number of words(escaping comma):
So for above example reading words should be:
12.5 56.8 12.5 56.8 12.5 56.8
set fh [open f r]
gets $fh num
read $fh data
close $fh
set number_re {-?\d+(?:\.\d*)?|-?\d*\.\d+}
set vertices {}
foreach {_ x y} [regexp -inline -all "($number_re),\\s*($number_re)" $data] {
lappend vertices $x $y
if {[llength $vertices] == $num * 2} break
}
puts $vertices
# => 12.5 56.8 12.5 56.8 12.5 56.8
while {[llength $vertices] < $num * 2} {
gets $fh line
foreach {_ x y} [regexp -inline -all "($number_re),\\s*($number_re)" $line] {
lappend vertices $x $y
if {[llength $vertices] == $num * 2} break
}
}
close $fh
I'm still not clear exactly what you are after. Here is some code to read data from a named file. Judging from your other question, you can have several sets of data in your input stream and this code returns them all as a list. Each element of the list is one set of coordinates
# Read the input from file
set fil [open filename.file]
set input [read $fil]
close $fil
set data [list]; # No output so for
set seekCount yes; # Next token is a vertex count
foreach token [string map {, " "} $input] {
# Convert commas to spaces
if {$seekCount} {
set nCoords [expr $token * 2];
# Save number of coordinates
set datum [list]; # Clean out vertex buffer
} else {
lappend datum $token; # Save coordinate
incr nCoords -1
if {$nCoords <= 0} {
# That was the last coordinate
lappend data $datum; # Append the list of coordinates
set seekCount yes; # and look for anopther count
}
}
}
This is a very quick-and-dirty solution, which makes no attempt to handle errors. One thing, however that it will cope with is variable amounds of whitespace and missing whitespace after the commas.
Good luck, I hope this helps.
This procedure first reads a count line, then reads that number of lines and puts as a list into $varName. It returns the number of elements in $varName, or -1 if EOF occured before a count was read.
proc getNLines {stream varName} {
upvar 1 $varName lines
set lines {}
if {[gets $stream n] < 0} {
return -1
}
while {$n > 0} {
if {[gets $stream line] < 0} {
error "bad data format"
}
lappend lines $line
incr n -1
}
return [llength $lines]
}
while {[getNLines stdin lines] >= 0} {
# ...
}

Is there shorthand in Tcl to get a sequential array of numbers?

For example, in Perl, to get a sequential array of numbers from 1 to 10, you could simply do:
#myArray = (1 .. 10);
The two periods serve as shorthand for this operations instead of making a for loop or writing the whole thing out manually. Other languages I've used have something similar also.
Does a similar shorthand exist in Tcl?
You can define the method:
proc fillArray {a b} {
eval return \[list $a [string repeat "\[incr a\] " [incr b -$a]]\]
}
And use it as:
set myArray [fillArray 1 10]
You even can beautify the call of procedure to make it look as in perl. For that just redefine unknown procedure:
rename unknown __unknown
proc unknown {args} {
if {[llength $args] == 3} {
lassign $args a op b
if {[string is integer $a] && $op == ".." && [string is integer $b]} {
return [fillArray $a $b]
}
}
return [uplevel __unknown {*}$args]
}
After that you can write just simple as:
set myArray [1 .. 10]
:)
Not quite this one, but
% package require struct::list
1.6.1
% struct::list iota 10
0 1 2 3 4 5 6 7 8 9
Also search this for the "iota" keyword to see how this can be done using a one-liner.
With the exception of expressions (which are their own little language) Tcl has no operators and is always a strictly prefix-driven language. This means that there isn't such a convenient shorthand for doing loops. On the other hand, there's nothing particularly special about Tcl's standard commands (apart from some minor efficiency details that don't matter here) so making your own is no problem:
proc .. {from to} {
if {$from >= $to} {
for {set i $from} {$i <= $to} {incr i} {lappend out $i}
} else {
for {set i $from} {$i >= $to} {incr i -1} {lappend out $i}
}
return $out
}
puts [.. 1 10]; # --> “1 2 3 4 5 6 7 8 9 10”
You can fake infix operators by using an unknown handler (as in GrAnd's answer) but that's really quite slow by comparison with the above.
No, a similar shorthand does not exist in tcl.
If you really want shorthand, you can create your own command that looks almost the same. For example:
proc : {start ignore end} {
set result []
for {set i $start} {$i <= $end} {incr i} {
lappend result $i
}
return $result
}
puts "from 1 to 10: [: 1 .. 10]"