Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Re: 2D arrays simulated using lists

64 views
Skip to first unread message

sleb...@gmail.com

unread,
Oct 2, 2008, 11:20:44 AM10/2/08
to
On Oct 2, 10:48 pm, costincarai...@gmail.com wrote:
> I'm trying to maneuver around some 2D arrays, and I'm going crazy in
> the meanwhile. Sometimes a certain syntax works, sometimes it doesn't
> (for accessing elements).


I'm not entirely sure what you're tryint to do but the following:

> set ListeActuelleAgents {};

creates a REGULAR variable (scalar in Perl parlance) initialised to
the empty string (which also happens to be exactly the same as an
empty list and an empty dict).

BUT, the later in the code, the following:

> set $ListeActuelleAgents($idxLigne,$idxColonne)

attempts to access it as an array, which SHOULD generate an error.

What you probably want is:

array set ListeActuelleAgents {};
set ListeActuelleAgents($idxLigne,$idxColonne) $somevalue

Ralf Fassel

unread,
Oct 2, 2008, 11:25:11 AM10/2/08
to
* costinc...@gmail.com
| In this bit of code, I can't set the damn array element.

What is the error you get?

| Here is the code snipper:
|
--<snip-snip>--
| Comparer_Tableaux ListeAgents ListeActuelleAgents $ListeAgentsLarg AgentsSupprimer AgentsAjouter AgentsModifier;

You're calling 'Comparer_Tableaux' with the name of the arrays.
How dows 'Comparer_Tableaux' access those?

R'

Gerald W. Lester

unread,
Oct 2, 2008, 12:41:10 PM10/2/08
to
costinc...@gmail.com wrote:
>> array set ListeActuelleAgents {};
>> set ListeActuelleAgents($idxLigne,$idxColonne) $somevalue
>
> Ok, I believe I'm getting closer.
>
> 1. How do I create reliably a 2D array?
> array set array set ListeActuelleAgents {};
>
> 2. How do I initialize all the values in the array? (or any value, for
> that matter of fact :) )
> array set ListeActuelleAgents [list ($idxLigne,$idxColonne)
> [$Widgets(scw_ListAgent) cellcget $idxLigne,$idxColonne -text]];
>
> 3. How do I access those values from a function? (I have been using
> upvar and global, which don't feel right, am I doing the right thing
> (TM)? )
>
> This is still eluding me, I just need to pass the array name for
> things to work alright?
>
> * Main issue: am I doing everything ok? *
>
> Hope no one gets offended by this:
> BTW, if this is the syntax, it is IMHO, hideous. I understand that
> there is some backwards compatibility involved, but it makes working
> with arrays terrible. It is very flexible, but simple loops on 2D or
> 3D arrays get a lot of ugly code...

First off Tcl does not have two or three or N dimensional arrays -- Tcl
arrays are dimensionless. The index can be any string -- you happen to be
using a string that looks like an array index in some other languages.

To initialize an array to an empty value do:
array set ListActualAgents {}

To initialize an array to an non-empty value or set values for several
indexes at once do:
set v1 [$Widgets(scw_ListAgent) cellcget $idxLigne,$idxColonne -text]
set v2 [$Widgets(scw_ListAgent) cellcget $idxLigne2,$idxColonne2 -text]
array set ListActualAgents [list \
$idxLigne,$idxColonne $v1 \
$idxLigne2,$idxColonne2 $v2 \ ]

To set the value of a single index do:
set v1 [$Widgets(scw_ListAgent) cellcget $idxLigne,$idxColonne -text]
set ListActualAgents($idxLigne,$idxColonne) $v1

As to #3, why do they not feel right?

From your questions and comments, I take it that you are modifying someone
else's code with a short deadline and have not the time to really learn Tcl.


--
+------------------------------------------------------------------------+
| Gerald W. Lester |
|"The man who fights for his ideals is the man who is alive." - Cervantes|
+------------------------------------------------------------------------+

Alexandre Ferrieux

unread,
Oct 2, 2008, 1:22:29 PM10/2/08
to
On Oct 2, 6:48 pm, costincarai...@gmail.com wrote:
>
> I thought that I could make my life simpler by using a type of index
> which I thought was simpler to wield. But the array(x,y) stuff bogged
> down badly :(

I would be very sad to see you believe Tcl doesn't meet your
requirement. This is an illusion. Tcl has all you would need as far as
simple data structures are concerned, and more complex ones can be
readily built atop.

I think a strong reason for this superficial "impedance mismatch" is
the careless use (on either side...) of words which have different
meanings depending on your computing habits (arrays, lists, indexes).
To solve this, let's forget about notations and names of data
structures, and let's just to grasp an outer characterization of your
problem:

Is your two-dimensional "Thing" (no loaded names):

- sparse or full ?
- fixed or dynamically sized ?
- mutable (cell-by-cell) or not ?
- to be just accessed by given x,y
- or also by iteration on a row or column (x,* ; *,y)
- to be populated quickly from an outer source (file, string)
- or just built internally

Notice that all these questions don't aim at a choice between
alternative implementions; some are only here to provide you with the
proper idioms.

Alternatively, you may just describe your problem in functional terms,
abstracting away the manner you'd initially imagined to solve it.

-Alex

Larry W. Virden

unread,
Oct 2, 2008, 1:38:07 PM10/2/08
to
On Oct 2, 12:14 pm, costincarai...@gmail.com wrote:
> >   array set ListeActuelleAgents {};
> >   set ListeActuelleAgents($idxLigne,$idxColonne) $somevalue
>
> Ok, I believe I'm getting closer.
>
> 1. How do I create reliably a 2D array?
> array set array set ListeActuelleAgents {};

Actually, you want
array set ListeActuelleAgents {}


>
> 3. How do I access those values from a function? (I have been using
> upvar and global, which don't feel right, am I doing the right thing
> (TM)? )
>
> This is still eluding me, I just need to pass the array name for
> things to work alright?
>

Tcl variables occur in scope.

So, if I have the example:
proc p1 {var1} {
array set LAA {}

puts "LAA initially contains:"
parray LAA

for {set i 0; set j 0;} {[expr $i*$j] < $var1} { incr i; incr
j;} {
set LAA($i,$j) $j
}

puts "LAA later contains:"
parray LAA

puts "One member of LAA is LAA(3,3), which has the value
$LAA(3,3)"

return
}

p1 10

puts $LAA(3,3)

the results I see when I run the above code (in Tcl 8.5 anyways) is:
LAA initially contains:
LAA later contains:
LAA(0,0) = 0
LAA(1,1) = 1
LAA(2,2) = 2
LAA(3,3) = 3
One member of LAA is LAA(3,3), which has the value 3
can't read "LAA(3,3)": no such variable
while executing
"puts $LAA(3,3)"
(file "/tmp/t.tcl" line 23)


That last bit says that when the line that executed occurred, the
array was no longer available.

To solve this, you have to create the array in either the default
global namespace or in a uniquely created namespace, then make certain
the code consistently accesses the array in that namespace.

The easiest thing to do is to use the global namespace. If I were to
change the example above to:
proc p1 {var1} {
array set ::LAA {}

puts "LAA initially contains:"
parray ::LAA

for {set i 0; set j 0;} {[expr $i*$j] < $var1} { incr i; incr
j;} {
set ::LAA($i,$j) $j
}

puts "LAA later contains:"
parray ::LAA

puts "One member of LAA is LAA(3,3), which has the value
$::LAA(3,3)"

return
}

p1 10

puts $::LAA(3,3)

The output from the example now becomes:
LAA initially contains:
LAA later contains:
::LAA(0,0) = 0
::LAA(1,1) = 1
::LAA(2,2) = 2
::LAA(3,3) = 3
One member of LAA is LAA(3,3), which has the value 3
3

There are other ways to do this - making use of upvar, etc. - this is
just one approach.


> BTW, if this is the syntax, it is IMHO, hideous. I understand that
> there is some backwards compatibility involved, but it makes working
> with arrays terrible. It is very flexible, but simple loops on 2D or
> 3D arrays get a lot of ugly code...


Don't think of this as the syntax for 2D arrays. Think of this as a
programming method of treating a hash table as if it were a 2D array.


oak...@bardo.clearlight.com

unread,
Oct 2, 2008, 2:10:32 PM10/2/08
to
On Oct 2, 12:42 pm, costincarai...@gmail.com wrote:
> 2) comparing stuff
> (loops)
> set NomAgentVieux [lindex [array get ListeAgentsParam [list ($idxLigne,
> 0)]] 1];
> set NomAgentActuel [lindex [array get ListeActuelleAgentsParam [list
> ($idxLigneActuelle,0)]] 1];
> if { [string equal $NomAgentVieux $NomAgentActuel] } {

You are working waaaaaay too hard. You can get an element of an array
without resorting to array and lindex commands:

Your code is very hard to decipher, but what I think you want is:

if {[string equal $ListeAgentsParam($idxLigne,0) \
$ListeActuelleAgentsParam($idxLigne,0)]} {...

or something like that. I'm not entirely sure which elements you are
wanting to compare. The backslash isn't strictly necessary, I justed
wanted shorter lines so it doesn't get mangled by the news system.

Even though "array set" has been thrown around a lot in this thread,
you don't need them. To set a specific cell just do something like
this:

set ListActuelleAgentsParam($idxLigne,$idxColonne) "some value"

To get back that value, use a $:

puts "the value is $ListActuelleAgentsParam($idxLigne,$idxColonne)"

See? Not complicated at all. Notice the lack of a '$' before
ListActuelleAgentsParam in the first example, and the use of it in the
second.

I think part of your confusion stems from an earlier error where you
tried to do this:

set $ListeActuelleAgents($idxLigne,$idxColonne)
Can't read "ListeActuelleAgents(0,0)": no such element in array

Notice how you have a '$' before the variable name. That means "use
the value of the following variable" which generally is not what you
want in a set command. You want the name of the variable. If you had
removed the initial $ it would have worked as expected.

Alexandre Ferrieux

unread,
Oct 2, 2008, 4:55:05 PM10/2/08
to
On Oct 2, 7:42 pm, costincarai...@gmail.com wrote:
>
> > Is your two-dimensional "Thing" (no loaded names):
>
> >       - sparse or full ?
> >       - fixed or dynamically sized ?
> >       - mutable (cell-by-cell) or not ?
> >       - to be just accessed by given x,y
> >       - or also by iteration on a row or column (x,* ; *,y)
> >       - to be populated quickly from an outer source (file, string)
> >       - or just built internally
>
> > Notice that all these questions don't aim at a choice between
> > alternative implementions; some are only here to provide you with the
> > proper idioms.
>
> > Alternatively, you may just describe your problem in functional terms,
> > abstracting away the manner you'd initially imagined to solve it.
>
> Ok, I'll try to do this, you're probably right.
>
> I have a table (something 30x6 or so, not huge), in a tablelist.

Whatever a tablelist means :-)
(let's not mix GUI and algorithmics while we're still struggling to
understand the problem)

> I need to compare what it stores in the beginning with what's stored
> in the end.

By "in the beginning" and "in the end" you mean, at times t1 and t2,
right ? Not the upper and lower part of a matrix ?

> I decided that the simplest way to do it would be to use
> to matrixes (2D arrays), one for the beginning, one for the end.

Let's ignore for now what you decided was simplest, for reasons stated
above...

> The matrixes are full, far from sparse :)

Good. So you answered one of my seven questions. Great !

> I compare them, after a certain algorithm (the first column is a sort
> of index, so I'm comparing the indexes at first, then I'm comparing
> the rest). I need to store the differences per line, because those
> lines will be:
> a) new line -> inserted into a DB
> b) modified line -> modified in DB
> c) removed -> removed in DB

Maybe you could just describe concretely the problem domain ? Unless
it's amilitary secret of course.
Er, come to think of it, you're not aiming ICBMs with _that_ code,
right ? ;-)

The reason I'm asking for further details is that the above
description is everything but clear, sorry.
It vaguely reminds me of some kind of DP-matching, but described in
nontechnical terms. Is it the case ?

> I thought I could weasel my way around using Tcl arrays not declared
> using [array set] because I thought that it would make the whole
> algorithm even more cumbersome for me. But then I hit a brick wall
> moving stuff in and out of the function used for the comparison.

Forget about the Tcl solution, I'm still at square one regarding the
algorithm. Please just describe the conrete task.

-Alex

Cameron Laird

unread,
Oct 2, 2008, 8:16:11 PM10/2/08
to
In article <2c751ba6-6dbc-4c4f...@y79g2000hsa.googlegroups.com>,
<costinc...@gmail.com> wrote:
.
.
.

>3. How do I access those values from a function? (I have been using
>upvar and global, which don't feel right, am I doing the right thing
>(TM)? )
>
>This is still eluding me, I just need to pass the array name for
>things to work alright?
.
.
.
Alexandre et al. are of course taking good care of you.
I'll just add the explicit remark that your instinct was
sound: density of upvar and global is an unhealthy
symptom, in general. Many fine Tcl programs do without
upvar at all, in fact.

It's also true that there are good uses for upvar, and
certainly for global. They seem unlikely in what you're
trying to achieve, though ...

Alexandre Ferrieux

unread,
Oct 3, 2008, 4:44:24 AM10/3/08
to
On Oct 3, 9:48 am, costincarai...@gmail.com wrote:
>
> I need to detect that a some information in a GUI has changed, and I
> have 3 entries and ahttp://wiki.tcl.tk/5527. If there are changes I
> prompt for a save (to a DB). For the entries it's simple, for the
> tablelist, I'm using those matrixes (aka arrays).
> The matrix showed in the tablelist contains lines like this:
> Agent1 Agent1Property1 Agent1Property2 ...
> Agent2 Agent2Property1 Agent2Property2 ...
> The agents are unique.
> I then return a list of new agents, of removed agents and of modified
> agents (and I'll use these in some DB functions).

Great !! The above 10 lines extracted from your message happen to be
necessary and sufficient to state the problem... Try to stick to that
style next time :-)

OK here is how I would handle the job: I would set up a mapping from
Agent IDs to vector-of-properties. I'm intentionally not using Tcl
data structure names, we are still at the algorithmic level. The
reason is the unicity of Agent IDs/names, and the "lifecycle"
semantics (new, removed, modified).
Then, I would handle the two _sets_ (again, the in mathematical sense,
don't look for a primitive Set type in Tcl) of agents, Old and New, to
compute the three interesting subsets:

- Added = New minus Old
- Removed = Old minus New
- Kept = Old intersect New

(sorry ASCII is nor rich with set theory symbols ;-)
Last, I would scan Kept, comparing for each agent the old and new
vector-of-properties, to determine the Modified subset.

Now we can talk about Tcl !

# 1. Establish the mapping: populate an array,
# where Agent names (1st column of the tablelist)
# are the keys, and the remaining sublists are the values.

# Assuming [.t get returns a list of lists (the rows)]
# (I have zero experience with tablelist as you know)

# at t1
foreach row [.t get ...] {
set old([lindex $row 0]) [lrange $row 1 end]
}

# at t2
foreach row [.t get ...] {
set new([lindex $row 0]) [lrange $row 1 end]
}

# 2. Compute the interesting subsets
# they are implemented as lists

set added {}
set deleted {}
set kept {}

foreach x [array names new] {
if {[info exists old($x)]} {
lappend kept $x
} else {
lappend added $x
}
}

foreach x [array names old] {
if {![info exists new($x)]} {
lappend deleted $x
}
}

# 3. Compute Modified and do the associated DB stuff on
Added,Deleted,Modified

set modified {}
foreach x $kept {
if {$old($x)!=$new($x)} {
lappend modified $x
}
}

foreach x $deleted {
do_db_stuff_for_deletion $x
}
foreach x $added{
do_db_stuff_for_creation $x $new($x)
}
foreach x $modified{
do_db_stuff_for_modification $x $new($x)
}

As you can see, the only nontrivial comparison occurs in Kept, by
comparing "vectors-of-properties" as a whole ($old($x)!=$old($new)).

HTH,

-Alex

Cameron Laird

unread,
Oct 3, 2008, 6:25:12 AM10/3/08
to
In article <049503b1-4809-42e3...@c65g2000hsa.googlegroups.com>,
Alexandre Ferrieux <alexandre...@gmail.com> wrote:
.
.

.
>Then, I would handle the two _sets_ (again, the in mathematical sense,
>don't look for a primitive Set type in Tcl) of agents, Old and New, to
>compute the three interesting subsets:
.
.
.
While I don't want to distract the original questioner from
Alexandre's correct design, it might interest other readers
to know of <URL: http://wiki.tcl.tk/5483 > as a tangent.

Ron Fox

unread,
Oct 3, 2008, 7:21:15 AM10/3/08
to
Seems that since a tablelist is involved the -editendcommand option
can be used to keep track of both whether or not changes have been
made and which parts of the table list were changed. If you need to
know the value prior to the change, that can be gotten via an
-editstartcommand script.

RF

--
Ron Fox
NSCL
Michigan State University
East Lansing, MI 48824-1321

Alexandre Ferrieux

unread,
Oct 3, 2008, 7:39:00 AM10/3/08
to
On Oct 3, 12:12 pm, costincarai...@gmail.com wrote:
>
> This looks very nice (certainly nicer that what I'm writing). I don't
> know if I should be happy or sad about this, but I finnally got things
> going with my implementation...

I'll answer this question: sad.
You come up to the newsgroup looking for advice, we spend some time
rewinding the whole design process to clean up misconceptions, give
you a ready-to use implementation with the proper idioms and optimal
performance... And you incrementally patch up your O(N^2) can of worms
(ignoring [foreach], [break], [info exists] !)

This is completely discouraging for *me*.

As a last attempt, below is a "bridge" snippet that converts your
awkward "bidimensional array" in the array-of-lists structure needed
for the code above.
Just use it this way:

# at t1
bidim2arlist ListeAgents old
# at t2
bidim2arlist ListeAgents new
# paste my above code here

Now of course do as you see fit; but don't expect the kind of in-depth
help we've been trying to offer you if you keep ignoring it.

-Alex

proc bidim2arlist {vfrom vto} {
upvar $vfrom from
upvar $vto to
array unset to
for {set i 0} {1} {incr i} {
set n [llength [array names from $i,*]]
if {!$n} break
set key $from($i,0)
for {set j 1} {$j<$n} {incr j} {
lappend to($key) $from($i,$j)
}
}
}

Gerald W. Lester

unread,
Oct 3, 2008, 12:46:24 PM10/3/08
to
Alexandre Ferrieux wrote:
>...

> # 2. Compute the interesting subsets
> # they are implemented as lists
>
> set added {}
> set deleted {}
> set kept {}
>
> foreach x [array names new] {
> if {[info exists old($x)]} {
> lappend kept $x
> } else {
> lappend added $x
> }
> }
>
> foreach x [array names old] {
> if {![info exists new($x)]} {
> lappend deleted $x
> }
> }

This step can be simplified using TclLibs struct package to:

package require struct::set
set oldList [array names old]
set newList [array names new]
set deltaList [::struct::set intersect3 $oldList $newList]
lassign $deltaList added kept deleted

Larry W. Virden

unread,
Oct 3, 2008, 12:59:36 PM10/3/08
to
On Oct 3, 12:46 pm, "Gerald W. Lester" <Gerald.Les...@cox.net> wrote:
> Alexandre Ferrieux wrote:
> >...
> >  # 2. Compute the interesting subsets
> >  #  they are implemented as lists
>
> >  set added {}
> >  set deleted {}
> >  set kept {}
>
> >  foreach x [array names new] {
> >    if {[info exists old($x)]} {
> >      lappend kept $x
> >    } else {
> >      lappend added $x
> >    }
> >  }
>
> >  foreach x [array names old] {
> >    if {![info exists new($x)]} {
> >      lappend deleted $x
> >    }
> >  }
>
> This step can be simplified using TclLibs struct package to:
>
> package require struct::set
> set oldList [array names old]
> set newList [array names new]
> set deltaList [::struct::set intersect3 $oldList $newList]
> lassign $deltaList added kept deleted

Once you get these 3 lists, then if you want to know if the values of
the array members have changed, you would loop through comparing. For
instance, the list in $added would be those array member names which
are present in both the old and the new arrays. The list in $kept are
the array member names which are only in the old array, and the list
in $added are those member names only in the new array. This should
make the logic easier to handle.

Then you could do something like

foreach x $added {
if { $old($x) != $new($x) } {
puts "member value old($x) = $old($x) changed to $new($x)"
}
}

Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted
0 new messages