TCL -- How to store and print table of values?
Asked Answered
tcl
O

4

6

I know that you can "hack" nesting associative arrays in tcl, and I also know that with dictionaries (which I have no experience with) you can nest them pretty easily. I'm trying to find a way to store the values of a function that has two variables, and then at the end I just want to print out a table of the two variables (column and row headers) with the function values in the cells. I can make this work, but it is neither succinct nor efficient.

Here's what should be printed. The rows are values of b and columns are values of a (1,2,3,4,5 for simplicity):

                          b
          1       2       3       4       5
    1  y(1,1)  y(1,2)  y(1,3)  y(1,4)  y(1,5)
    2  y(2,1)  y(2,2)  y(2,3)  y(2,4)  y(2,5)
  a 3  y(3,1)  y(3,2)  y(3,3)  y(3,4)  y(3,5)
    4  y(4,1)  y(4,2)  y(4,3)  y(4,4)  y(4,5)
    5  y(5,1)  y(5,2)  y(5,3)  y(5,4)  y(5,5)

To store this, I imagine I would simply do two nested for loops over a and b and somehow store the results in nested dictionaries. Like have one dictionary with 5 entries, 1 for each value of b, and each entry in this is another dictionary for each value of b.

To print it, the only way I can think of is to just explicitly print out each table line and call each dictionary entry. I'm not too versed in output formatting with tcl, but I can probably manage there.

Can anyone think of a more elegant way to do this?

Otherwhere answered 16/12, 2013 at 22:10 Comment(0)
N
5

Here are a couple of examples on how you might use the struct::matrix package.

Example 1 - Simple Create/Display

package require struct::matrix
package require Tclx

# Create a 3x4 matrix
set m [::struct::matrix]
$m add rows 3
$m add columns 4

# Populate data
$m set rect 0 0 {
    {1 2 3 4}
    {5 6 7 8}
    {9 10 11 12}
}

# Display it
puts "Print matrix, cell by cell:"
loop y 0 [$m rows] {
    loop x 0 [$m columns] {
        puts -nonewline [format "%4d" [$m get cell $x $y]]
    }
    puts ""
}

Output

Print matrix, cell by cell:
   1   2   3   4
   5   6   7   8
   9  10  11  12

Discussion

  • In the first part of the script, I created a matrix, add 3 rows and 4 columns--a straight forward process.
  • Next, I called set rect to populate the matrix with data. Depend on your need, you might want to look into set cell, set column, or set row. For more information, please consult the reference for struct::matrix.
  • When it comes to displaying the matrix, instead of using the Tcl's for command, I prefer the loop command from the Tclx package, which is simpler to read and use.

Example 2 - Read from a CSV file

package require csv
package require struct::matrix
package require Tclx

# Read a matrix from a CSV file
set m [::struct::matrix]
set fileHandle [open data.csv]
::csv::read2matrix $fileHandle $m "," auto
close $fileHandle

# Displays the matrix
loop y 0 [$m rows] {
    loop x 0 [$m columns] {
        puts -nonewline [format "%4d" [$m get cell $x $y]]
    }
    puts ""
}

The data file, data.csv:

1,2,3,4
5,6,7,8
9,10,11,12

Output

   1   2   3   4
   5   6   7   8
   9  10  11  12

Discussion

  • The csv package provides a simple way to read from a CSV file to a matrix.
  • The heart of the operation is in the ::csv::read2matrix command, but before that, I have to create an empty matrix and open the file for reading.
  • The code to display the matrix is the same as previous example.

Conclusion

While the struct::matrix package seems complicated at first; I only need to learn a couple of commands to get started.

Neal answered 17/12, 2013 at 21:16 Comment(0)
R
2

Elegance is in the eye of the beholder :)

With basic core Tcl, I think you understand your options reasonably well. Either arrays or nested dictionaries have clunky edges when it comes to tabular oriented data.

If you are willing to explore extensions (and Tcl is all about the extension) then you might consider the matrix package from the standard Tcl library. It deals with rows and columns as key concepts. If you need to do transformations on tabular data then I would suggest TclRAL, a relational algebra library that defines a Relation data type and will handle all types of tabular data and provide a large number of operations on it. Alternatively, you could try something like SQLite which will also handle tabular data, provide for manipulating it and has robust persistent storage. The Tcl wiki will direct you to details of all of these extensions.

However, if these seem too heavyweight for your taste or if you don't want to suffer the learning curve, rolling up your sleeves and banging out an array or nested dictionary solution, while certainly being rather ad hoc, is probably not that difficult. Elegant? Well, that's for you to judge.

Rotenone answered 17/12, 2013 at 1:15 Comment(0)
P
2

Nested lists work reasonably well for tabular data from 8.4 onwards (with multi-index lindex and lset) provided you've got compact numeric indices. 8.5's lrepeat is good for constructing an initial matrix too.

set mat [lrepeat 5 [lrepeat 5 0.0]]

lset mat 2 3 1.3

proc printMatrix {mat} {
    set height [llength $mat]
    set width [llength [lindex $mat 0]]
    for {set j 0} {$j < $width} {incr j} {
        puts -nonewline \t$j
    }
    puts ""
    for {set i 0} {$i < $height} {incr i} {
        puts -nonewline $i
        for {set j 0} {$j < $width} {incr j} {
            puts -nonewline \t[lindex $mat $i $j]
        }
        puts ""
    }
}
printMatrix $mat

You should definitely consider using the struct::matrix and report packages from tcllib.

Perryperryman answered 17/12, 2013 at 9:19 Comment(1)
As you might have guessed, I'm a very casual tcl programmer and I have not looked at any extensions. I didn't think to even look there. Thanks!Otherwhere
C
0
package require csv
package require struct::matrix

array set OPTS [subst {
    csv_input_filename            {input.csv}
}]

::struct::matrix indata
set chan [open $OPTS(csv_input_filename)]
csv::read2matrix $chan indata , auto
close $chan

# prints matrix as list format
puts [join [indata get rect 0 0 end end] \n]; 

# prints matrix as csv format
csv::joinmatrix indata

# cleanup
indata destroy

This is a one-liner way to print out a matrix as list or csv format respectively.

Complete answered 26/12, 2020 at 18:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.