Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TCL -- How to store and print table of values?

Tags:

tcl

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?

like image 481
jktstance Avatar asked Dec 16 '13 22:12

jktstance


3 Answers

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.

like image 114
Hai Vu Avatar answered Sep 28 '22 20:09

Hai Vu


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.

like image 31
andy mango Avatar answered Sep 28 '22 19:09

andy mango


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.

like image 22
Donal Fellows Avatar answered Sep 28 '22 20:09

Donal Fellows