To add preceding digits in a list in Tcl - 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.

Related

Sum of elements/numbers in N lists in 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.

Finding Median and average of list in tcl

I am having trouble finding a way to calculate the median and average of a list of numbers and the resources online seem to be really limited with Tcl. So far I managed to only print the numbers of the list.
Your help would be greatly appreciated.
proc ladd {l} {
set total 0
set counter 0
foreach nxt $l {
incr total $nxt
incr counter 1
}
puts "$total"
puts "$counter"
set average ($total/$counter)
puts "$average"
}
set a [list 4 3 2 1 15 6 29]
ladd $a
To get the average (i.e., the arithmetic mean) of a list, you can just do:
proc average {list} {
expr {[tcl::mathop::+ {*}$list 0.0] / max(1, [llength $list])}
}
That sums the values in the list (the trailiing 0.0 forces the result to be a floating point value, even if all the added numbers are integers) and divides by the number of elements (or 1 if the list is empty so an empty list gets a mean of 0.0 instead of an error).
To get the median of a list, you have to sort it and pick the middle element.
proc median {list {mode -real}} {
set list [lsort $mode $list]
set len [llength $list]
if {$len & 1} {
# Odd number of elements, unique middle element
return [lindex $list [expr {$len >> 1}]]
} else {
# Even number of elements, average the middle two
return [average [lrange $list [expr {($len >> 1) - 1] [expr {$len >> 1}]]]
}
}
To complete the set, here's how to get the mode of the list if there is a unique one (relevant for some applications where values are selected from a fairly small set):
proc mode {list} {
# Compute a histogram
foreach val $list {dict incr h $val}
# Sort the histogram in descending order of frequency; type-puns the dict as a list
set h [lsort -stride 2 -index 1 -descending -integer $h]
# The mode is now the first element
return [lindex $h 0]
}
I'll leave handling the empty and non-unique cases as an exercise.

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.

expected integer but got "floating point number" error

I try to write a very simple program in TCL using list.
Below is the list
list { 1 2 3 4 5 6 1.5 7 }
Below is my code
set sum 0
for {set i 0} {$i < [llength $list]} {incr i} {
incr sum [lindex $list $i]
}
puts $sum
On executing the above program I am getting the below error due to floating point value of 1.5 in the list
expected integer but got "1.5"
(reading increment)
invoked from within
"incr sum [lindex $list $i]"
I searched on internet and could not find anything relevant.
Please advise how do I handle the floating point value?
While using incr command, variable must have value that can be interpreted as a an integer. See tcl wiki.
If variable is a non-integral real number, [incr] could not be used, but [set] could:
set sum 0
for {set i 0} {$i < [llength $list]} {incr i} {
set sum [expr {$sum + [lindex $list $i]}]
}
puts $sum
Omsai's answer should solve your problem, but a cleaner solution is to use foreach:
set sum 0
foreach n $list {
set sum [expr {$sum + $n}]
}
puts $sum
Summing up a list of numeric values can also be done with the ::tcl::mathop::+ command:
::tcl::mathop::+ {*}$list
This looks more complicated that it is. The + command isn't available in the regular namespace, so you need to specify where it comes from (the ::tcl::mathop namespace). The command expects to get each operand as a separate argument, so if they are in a list you need to expand that list using the {*} prefix.
foreach and the various mathop commands are documented here: foreach, mathop.
(Note: the 'Hoodiecrow' mentioned in the comments is me, I used that nick earlier.)
Tcl gives an error if you will try
incr a 1.5
you have to change the logic.
clearly you want to add all the numbers in the list. and answers are easy and many. But i will give you the shortest way:
set l { 1 2 3 4 5 6 1.5 7 }
set sum [expr [join $l +]]
NO LOOPING REQUIRED.

Combinations of all charcaters and all lengths with using less number of loops?

Brain Teaser: I self originated this question, but stuck completely.
I want to create all possible combination of all characters, but of all possible lengths. Suppose, [a-z] combination of 1 length, then [a-z] combination of 2 length, and so on till the maximum length achieved.
this could be very easily done by iterative looping.
Example for 3 length:
proc triples list {
foreach i $list {
foreach j $list {
foreach k $list {
puts [list $i $j $k]
}
}
}
}
But, it should solve using less loops (looping needs to be dynamic)
set chars "abcdefghijklmnopqrstuvwxyz"
set chars [split $chars ""]
set complete_length [llength $chars]
set start 0
set maximum_length 15
while {1} {
if {$start > $maximum_length} {
break
}
for {set i [expr $maximum_length-$start]} {$i >= 0} {incr i -1} {
# dump combinations
}
incr start
}
In this chunk, what algorithm or method i should apply? Any kind of suggestions/help/code will be appreciated.
Sry, this is not an answer, but hopefully some interesting discussion anyway:
The word "combinations" is often used way too generally, so it can be interpreted in many different ways. Let's say that you have a source list of 26 different elements, the english letters, and you want to pick 3 of them and combine in a 3 element destination list:
Can you always pick any letter from the source list, or do the elements disappear from it as you pick them? Either define "pick" (are the elements copied or moved during a pick), or define the set of source values (is there 1 of each of A-Z or an infinite amount of A-Z).
Does the order in the destination list matter? Is AHM considered to be the same combination as HAM? Define "combine".
If you have a list where not all elements are different, e.g. {2 10 10 64 100}, you have even more possibilities. Define your set of values.
Your first example prints permutations, not combinations. If that's what you want, the easiset way is a recursive procedure. Combinations are more complicated to generate.
EDIT:
I wrote this procedure for a Project Euler program. It picks all the elements, but maybe you can modify it to pick n. It takes a command prefix as argument, so you don't have to store all permutations.
package require Tcl 8.5.0
proc forEachPerm {list cmdPrefix} {
_forEachPerm {} $list $cmdPrefix
}
proc _forEachPerm {head list cmdPrefix} {
if {![llength $list]} {
{*}$cmdPrefix $head
} else {
for {set i 0} {$i < [llength $list]} {incr i} {
_forEachPerm [concat $head [lrange $list $i $i]] [lreplace $list $i $i] $cmdPrefix
}
}
}
# example use:
forEachPerm {a b c} {apply {{list} {puts [join $list]}}}