For Programmers: Free Programming Magazines  


Home > Archive > Tcl > June 2005 > parray improvements









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author parray improvements
Andreas Leitgeb

2005-06-03, 9:08 am

For the need to write out the contents of an array to a file,
I had a look into $tcl_library/parray.tcl and found two (minor)
things:

1.) for finding the longest key, it traverses the *lsorted*
list. Here, sorting is just a waste of cpu-cycles.
(as opposed to sorting in the actual output-loop)

2.) I wonder, if it could be easily extended with a few options
to puts the formatted text to a specified channel (rather than
to hardcoded stdout), or evenmore: to just return the text.

Donal K. Fellows

2005-06-03, 9:08 am

Andreas Leitgeb wrote:
> 1.) for finding the longest key, it traverses the *lsorted*
> list. Here, sorting is just a waste of cpu-cycles.
> (as opposed to sorting in the actual output-loop)


The efficient thing to do is to compute the list once (before the loop)
and sort it then too. I've updated the HEAD to do this (I'm not going to
backport the change on its own; it's just inefficiency in something
which is best used only for debugging anyway).

> 2.) I wonder, if it could be easily extended with a few options
> to puts the formatted text to a specified channel (rather than
> to hardcoded stdout), or evenmore: to just return the text.


Any suggestions for a backward-compatible API change to do this?

Donal.
Arjen Markus

2005-06-03, 9:08 am

"Donal K. Fellows" wrote:
>
> Andreas Leitgeb wrote:
>
> The efficient thing to do is to compute the list once (before the loop)
> and sort it then too. I've updated the HEAD to do this (I'm not going to
> backport the change on its own; it's just inefficiency in something
> which is best used only for debugging anyway).
>
>
> Any suggestions for a backward-compatible API change to do this?
>
> Donal.


What about :

parray -inline arrayName

and

parray -out $channel arrayName

?

Regards,

Arjen
Kaitzschu

2005-06-03, 4:00 pm

On Fri, 3 Jun 2005, Arjen Markus wrote:

> "Donal K. Fellows" wrote:
>
> What about :
>
> parray -inline arrayName
>
> and
>
> parray -out $channel arrayName
>
> ?


This is not backwards compatible. Good for Tcl9, I guess. Until then,

parray array ?pattern=* ?channel=stdout??

Clumsy (forces channelwriters to specify pattern) but is compatible as
parray accepted only one parameter. Using empty channel name just returns
list of formatted lines (true channel can't have empty name?). At Tcl9
backward compatibility can be broken by adding true switches to [parray].

Memorywasting when printing huge arrays is reduced by wasting clock cycles
to check channel at every iteration. Diff is against 8.5a2 parray.tcl
(I failed to find those cvs head differences).

13c13
< proc parray {a {pattern *}} {
---
> proc parray {a {pattern *} {chan {stdout}}} {

24a25
> set olst {}

27c28,32
< puts stdout [format "%-*s = %s" $maxl $nameString $array($name)]
---
> if {$chan eq {}} {
> lappend olst [format "%-*s = %s" $maxl $nameString $array($name)]
> } else {
> puts $chan [format "%-*s = %s" $maxl $nameString $array($name)]
> }

28a34
> if {$chan eq {}} {return $olst}


Just an idea.

--
-Kaitzschu
s="TCL ";while true;do echo -en "\r$s";s=${s:1:${#s}}${s:0:1};sleep .1;done
Arjen Markus

2005-06-03, 4:00 pm

Kaitzschu wrote:
>


>
> This is not backwards compatible. Good for Tcl9, I guess. Until then,
>
> parray array ?pattern=* ?channel=stdout??
>
> Clumsy (forces channelwriters to specify pattern) but is compatible as
> parray accepted only one parameter. Using empty channel name just returns
> list of formatted lines (true channel can't have empty name?). At Tcl9
> backward compatibility can be broken by adding true switches to [parray].
>
> Memorywasting when printing huge arrays is reduced by wasting clock cycles
> to check channel at every iteration. Diff is against 8.5a2 parray.tcl
> (I failed to find those cvs head differences).
>


Oh, I forgot about the pattern ... Yes, you are right, that makes
it less trivial. However, would it be disastrous to be not 100%
compatible:

- If an option is given, then it must start with a "-"
- Patterns often have * or ? in them

So my proposal would preclude:
- array names that start with a "-"
- patterns without wild-cards
- (and possibly only the combination)

Regards,

Arjen
Glenn Jackman

2005-06-03, 4:00 pm

At 2005-06-03 04:13AM, Andreas Leitgeb <avl@gamma.logic.tuwien.ac.at> wrote:
> For the need to write out the contents of an array to a file,
> I had a look into $tcl_library/parray.tcl and found two (minor)
> things:
>
> 1.) for finding the longest key, it traverses the *lsorted*
> list. Here, sorting is just a waste of cpu-cycles.
> (as opposed to sorting in the actual output-loop)
>
> 2.) I wonder, if it could be easily extended with a few options
> to puts the formatted text to a specified channel (rather than
> to hardcoded stdout), or evenmore: to just return the text.


I once rewrote parray for my own purposes, to add an optional channelID
like puts:

########################################
##################################
# parray ?channelId? arrayName ?pattern?:
#
# based on:
# RCS: @(#) $Id: parray.tcl,v 1.3 1998/09/14 18:40:03 stanton Exp $
#
# Copyright (c) 1991-1993 The Regents of the University of California.
# Copyright (c) 1994 Sun Microsystems, Inc.
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.

proc parray args {
switch -exact -- [llength $args] {
1 { set chan stdout ; set a $args ; set pattern * }
2 {
# is the first arg a channel?
if {[catch {tell [lindex $args 0]} out] == 0} {
# yep
foreach {chan a} $args break
set pattern *
} else {
# nope
foreach {a pattern} $args break
set chan stdout
}
}
3 { foreach {chan a pattern} $args break }
default { error "wrong # args: should be \"[lindex [info level 0] 0] ?channelID? arrayName ?pattern?\"" }
}

upvar 1 $a array
if {![array exists array]} {
error "\"$a\" isn't an array"
}
set maxl 0
foreach name [array names array $pattern] {
if {[string length $name] > $maxl} {
set maxl [string length $name]
}
}
set maxl [expr {$maxl + [string length $a] + 2}]
foreach name [lsort [array names array $pattern]] {
set nameString [format %s(%s) $a $name]
puts $chan [format "%-*s = %s" $maxl $nameString $array($name)]
}
}


--
Glenn Jackman
NCF Symin
glennj@ncf.ca
Gerald W. Lester

2005-06-04, 4:01 am

Andreas Leitgeb wrote:
> For the need to write out the contents of an array to a file,
> I had a look into $tcl_library/parray.tcl and found two (minor)
> things:
>
> 1.) for finding the longest key, it traverses the *lsorted*
> list. Here, sorting is just a waste of cpu-cycles.
> (as opposed to sorting in the actual output-loop)
>
> 2.) I wonder, if it could be easily extended with a few options
> to puts the formatted text to a specified channel (rather than
> to hardcoded stdout), or evenmore: to just return the text.
>


If it does not need to be pretty (i.e. you want to save it to read in again
later):

puts $outChan [list array set arrayName [array get arrayName]]

--
+--------------------------------+---------------------------------------+
| Gerald W. Lester | "The man who fights for his ideals is |
| Gerald.Lester@cox.net | the man who is alive." -- Cervantes |
+--------------------------------+---------------------------------------+
Donal K. Fellows

2005-06-05, 3:58 pm

Kaitzschu wrote:[color=darkred]
> < proc parray {a {pattern *}} {

That's about what I was thinking as well.

Donal.
Andreas Leitgeb

2005-06-06, 8:58 pm

Donal K. Fellows <donal.k.fellows@manchester.ac.uk> wrote:
> Kaitzschu wrote:
> That's about what I was thinking as well.


yes, looks quite ok.
If chan is provided and empty (or special "return"), then
it should collect and finally [return], rather than write
it out...

Andreas Leitgeb

2005-06-06, 8:58 pm

Gerald W. Lester <Gerald.Lester@cox.net> wrote:
> If it does not need to be pretty (i.e. you want to save it to read in again
> later):
> puts $outChan [list array set arrayName [array get arrayName]]

sure, for reading it in later, this is *the* way to do it.

In my case it was for a human reader to check if the array contained
the right things. The only requirement was, that the code should fit
in one or two lines and only at those places where something was
watched, which excluded writing up my own procedure.
Instead, I wrote:
set xx ""; foreach {k v} [array get myarr] {append xx "x($k)=$v\n"}
exec echo $xx > /tmp/avl-logfile
Even shorter would have been:
exec echo "myarr:\n[join [array get myarr] "\n"]\n" > /tmp/avl-logfile
But I could afford two lines, and I found it worth the luxury :-)
(why "shelling"(I know, no /bin/sh involved!) out to echo?
Because that takes fewer chars than open/puts/close would. :-)


Flattened lists (like what is returned by array get, or listified dicts)
are a bit tough to handle in Tcl. The only command that really handles
them sensibly is foreach with multiple variables.

I propose some extension for [join] (and, unless the echo here
is mostly negative or nonexistent, I'll likely tip it up later):

example first:
set list {1a 1b 1c 2a 2b 2c 3a 3b 3c}
puts >[join $list . , \n]<
>1a.1b,1c

2a.2b,2c
3a.3b,3c<

join _now_ accepts the list and _one_ (optional) string.
join would *then* accept the list and an *arbitrary number* of
strings which would be cycled through.

Neil Madden

2005-06-06, 8:58 pm

Andreas Leitgeb wrote:
> Gerald W. Lester <Gerald.Lester@cox.net> wrote:

....
>
> I propose some extension for [join] (and, unless the echo here
> is mostly negative or nonexistent, I'll likely tip it up later):
>
> example first:
> set list {1a 1b 1c 2a 2b 2c 3a 3b 3c}
> puts >[join $list . , \n]<
>
>
> 2a.2b,2c
> 3a.3b,3c<
>
> join _now_ accepts the list and _one_ (optional) string.
> join would *then* accept the list and an *arbitrary number* of
> strings which would be cycled through.
>


A little while ago, I created quick versions of split and join for
nested lists (http://wiki.tcl.tk/10916). This is slightly different to
your version, as it operates on nested lists rather than flat lists. You
can achieve your version from mine (for dicts/arrays) using a simple
pairing function:

% proc pair {list} {
set ret [list]
foreach {a b} $list { lappend ret [list $a $b] }
return $ret
}
% set res [multi::join [pair [array get tcl_platform]] \n =]
byteOrder=bigEndian
osVersion=8.1.0
platform=unix
machine=Power Macintosh
threaded=1
os=Darwin
wordSize=4
user=neilmadden
% multi::split $res \n =
{byteOrder bigEndian} {osVersion 8.1.0} {platform unix} {machine {Power
Macintosh}} {threaded 1} {os Darwin} {wordSize 4} {user neilmadden}

A simple [join] on this will flatten it back into dict format.

Your more general function above though is quite different. However, it
is easily achieved in pure Tcl via:

% proc cycle-join {list args} {
set len [llength $args] ;# Possible divide by zero error...
for {set i 0} {$i < ([llength $list]-1)} {incr i} {
append ret [lindex $list $i] [lindex $args [expr {$i % $len}]]
}
append ret [lindex $list end]
}
% cycle-join {1a 1b 1c 2a 2b 2c 3a 3b 3c 4a 4b 4c} . , \n
1a.1b,1c
2a.2b,2c
3a.3b,3c
4a.4b,4c

Probably these are worth adding to tcllib somewhere (textutils), if such
functions do not already exist there.

-- Neil
Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2008 codecomments.com