what are the elements to feed join command? - tcl

I am confused at "join" command, here is my code inside tclsh:
% lappend aaa 1
1
% lappend aaa 2
1 2
% lappend aaa {3 4}
1 2 {3 4}
% join $aaa
1 2 3 4
so the list aaa should have 3 elements: 1, 2 and {3 4}. Because "join" command should just concat all the elements together to form a string, then the return should be "1 2 {3 4}" because the list has just 3 elements. Why does join command break the 3rd element?

It doesn't. The 3rd element is a string 3 4 -- the braces you see are not actually part of the string. You're joining with a space, so the space in the string is not visually different. An example:
% lappend aaa 1
1
% lappend aaa 2
1 2
% lappend aaa {3 4}
1 2 {3 4}
% lappend aaa 5
1 2 {3 4} 5
% join $aaa :
1:2:3 4:5

Related

Pass few but not all optional arguments to a Tcl procedure

In TCL the way to make a parameter optional is to give it a default value. I don't know if there are any other ways too. e.g
proc my_func {a b c {d 10} {e 11} {f 12}} {
...
}
Now in the above example the parameters a, b and c are compulsory. The parameters d, e and f are optional. Is there another way to create optional parameters?
I am in a situation where I need to create a parameter that can be called from a TCL terminal (in Xilinx Vivado) which has some optional parameters. The user decide to pass a few or all of the optional parameters or none at all. The problem is that, when using positional argument passing, it is impossible to tell TCL which optional parameter we are passing to it. What is the solution to this? e.g
my_func 1 2 3 4 5 6
shall call the my_func with values a=1, b=2, c=3, d=4, e=5 and f=6. Also,
my_func 1 2 3 4
shall call my_func with values a=1, b=2, c=3 and d=4 and the e, f left at their default values. However, I might need to do something like this
my_func 1 2 3 100
where I am passing 100 to f and leave c and d at default value. But the above stament will set d to 100 instead and leave e and f at their default values.
What is the solution since I can clearly not use the positional argument technique here.
A readable way to design the function is to do it Tk style: use -d 100 options:
proc my_func {a b c args} {
set opts [dict merge {-d 10 -e 11 -f 12} $args]
puts "a = $a"
puts "b = $b"
puts "c = $c"
puts "d = [dict get $opts -d]"
puts "e = [dict get $opts -e]"
puts "f = [dict get $opts -f]"
}
Then when you use them, you can specify them in any order:
% my_func
wrong # args: should be "my_func a b c ?arg ...?"
% my_func 1 2 3
a = 1
b = 2
c = 3
d = 10
e = 11
f = 12
% my_func 1 2 3 -e 100 -d 200
a = 1
b = 2
c = 3
d = 200
e = 100
f = 12
If the final argument in your proc definition is literally args, then the remaining arguments (if any) are collected in a list.
This proc demonstrates how d,e,f can be optional. The optional arguments are included as a {name value} pair.
proc my_func {a b c args} {
set defaults {d 10 e 11 f 12}
foreach {var_name var_value} $defaults {
set $var_name $var_value
}
foreach arg $args {
set [lindex $arg 0] [lindex $arg 1]
}
puts "a:$a b:$b c:$c d:$d e:$e f:$f"
}
tcl8.6.8> my_func 1 2 3
a:1 b:2 c:3 d:10 e:11 f:12
tcl8.6.8> my_func 1 2 3 {d 5} {e 8} {f 99}
a:1 b:2 c:3 d:5 e:8 f:99
tcl8.6.8> my_func 1 2 3 {f 99}
a:1 b:2 c:3 d:10 e:11 f:99
The below is a minor variation to the solutions already suggested. By using dict with, on can unpack the dictionary content into the proc-local scope as variables:
proc my_func {a b c args} {
set () [dict merge {(d) 10 (e) 11 (f) 12} $args]
dict with () {}
puts "a = $a"
puts "b = $b"
puts "c = $c"
puts "d = $(d)"
puts "e = $(e)"
puts "f = $(f)"
}
Some remarks:
To avoid collisions with other (existing?) proc-local variables, the optional parameters are denoted as elements of an array named using the empty string: ().
dict with will unpack the so-named keys into that array: (e), (f), ...
The processed optionals can be accessed via $ syntax: $(e), $(f), ...
Watch:
my_func 1 2 3
my_func 1 2 3 (e) 100 (d) 200
Yields:
a = 1
b = 2
c = 3
d = 10
e = 11
f = 12
a = 1
b = 2
c = 3
d = 200
e = 100
f = 12

Identify the empty line in text file and loop over with that list in tcl

I have a file which has the following kind of data
A 1 2 3
B 2 2 2
c 2 4 5
d 4 5 6
From the above file I want to execute a loop like ,
three iteration where first iteration will have A,B elements 2nd iteration with c elements and 3rd with d. so that my html table will look like
Week1 | week2 | week3
----------------------------
A 1 2 3 | c 2 4 5 | d 4 5 6
B 2 2 2
I found this in SO catch multiple empty lines in file in tcl but I'm not getting what I exactly want.
I would suggest using arrays:
# Counter
set week 1
# Create file channel
set file [open filename.txt r]
# Read file contents line by line and store the line in the varialbe called $line
while {[gets $file line] != -1} {
if {$line != ""} {
# if line not empty, add line to current array with counter $week
lappend Week($week) $line
} else {
# else, increment week number
incr week
}
}
# close file channel
close $file
# print Week array
parray Week
# Week(1) = {A 1 2 3} {B 2 2 2}
# Week(2) = {c 2 4 5}
# Week(3) = {d 4 5 6}
ideone demo

Print multiple tcl lists in a uniform manner

I have a group of lists some with strings, some with numbers and some with both. All these lists have variable lengths. I would like to know what would be the best way to print it to a file so that they all have equal spacing between them.
For example, I use,
set numbers {0 1 2 3 4}
set type {dog reallybigbaddog thisisaevenlargersentence cat bird}
set paths {aaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ccc ddddddddddddddddd efgh}
puts $fid "NUMBERS\t\tTYPE\tPATHS"
foreach numbersval $numbers typeval $type pathsval $paths {
puts $fid "$numbersval\t\t$typeval\t$pathsval"
}
The result was,
NUMBERS TYPE PATHS
0 dog AAA
1 reallybigbaddog bbbbbbbbbbbbbbbbbbbbbbbb
2 thisisaevenlargersentence ccc
3 cat ddddddddddddddddd
4 bird efgh
I Tried using "format" based on one of the suggestions on this site but that resulted in a similar output, I guess we need a way to determining what the longest string is and cant arbitrarily use "\t"? Would appreciate any better suggestions.
For reference, this is how you could do it with struct::matrix and report:
package require struct::matrix
package require report
set nrows 5
set ncols 3
set npads [expr {$ncols + 1}]
struct::matrix m
m add rows $nrows
m add column {0 1 2 3 4}
m add column {dog reallybigbaddog thisisaevenlargersentence cat bird}
m add column {aaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ccc ddddddddddddddddd efgh}
m insert row 0 {NUMBERS TYPE PATHS}
report::report r $ncols
r data set [lrepeat $npads \t]
m format 2string r
(This uses only a fraction of the formatting power of report.) This method can handle values with spaces in them.
Result (there is a tab character to the left of the first column on each row, but it's lost in the formatting here.):
NUMBERS TYPE PATHS
0 dog aaa
1 reallybigbaddog bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
2 thisisaevenlargersentence ccc
3 cat ddddddddddddddddd
4 bird efgh
Documentation: expr, lrepeat, package, report package, set, struct::matrix package
In this case, I'd call out to column -t to do the work for me:
set all "NUMBERS TYPE PATHS\n"
foreach n $numbers t $type p $paths {
append all "$n $t $p\n"
}
set formatted [exec column -t << $all]
puts $formatted
NUMBERS TYPE PATHS
0 dog aaa
1 reallybigbaddog bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
2 thisisaevenlargersentence ccc
3 cat ddddddddddddddddd
4 bird efgh
A pure Tcl way to do this:
array set maxl {numbers 0 type 0 paths 0}
foreach l {numbers type paths} {
foreach e [concat $l [set $l]] {
if {[set len [string length $e]] > $maxl($l)} {
set maxl($l) $len
}
}
}
puts [format "%-*s %-*s %-*s" $maxl(numbers) NUMBERS $maxl(type) TYPE $maxl(paths) "PATH LISTS"]
foreach n $numbers t $type p $paths {
puts [format "%-*s %-*s %-*s" $maxl(numbers) $n $maxl(type) $t $maxl(paths) $p]
}

Returning Some Variable from a "proc" in Tcl

Suppose that a procedure in Tcl is as follows:
proc Section {ID x y} {
.
.
"Some calculations do here"
.
.
}
Section 1 20 30
Section 2 25 35
Section 3 30 40
Section 4 35 45
Now, I define this:
set IDsection {1 3}
Then, I would like to read all values (arbitrary number, 2 or more) into a set (IDsection) which would show the ID in above procedure and generate the corresponding y:
set Load {30 40}
How I do produce the values in {} in front of "Load"?
You could do it like this:
proc Section {ID x y} {
set ::section_data($ID) [list $x $y]
}
proc getLoads {ids} {
global section_data
foreach id $ids {
lappend loads [lindex $section_data($id) end]
}
return $loads
}
Section 1 20 30
Section 2 25 35
Section 3 30 40
Section 4 35 45
set IDsection {1 4 1 3}
set Load [getLoads $IDsection] ;# => 30 45 30 40

First value from second column with the smallest value from column 1

I have this file
2 1
12 2
34 1
56 1
45 3
33 2
77 1
83 2
62 3
75 3
I want to take the first value from second column with the smallest value from column 1
like this
2 1
12 2
45 3
This can be done with a linear scan and a little bit of use of an associative array, something like this:
set f [open $filename]
foreach line [split [read $f] "\n"] {
# ASSUME: valid Tcl list of numbers
lassign $line col1 col2
if {![info exists minima($col2)] || $minima($col2) > $col1} {
set minima($col2) $col1
}
}
close $f
foreach col2 [array names minima] {
puts "$minima($col2) $col1"
}
Imposing whatever sorts of parsing, sorting and formatting you require are left to you.