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
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'
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|
+------------------------------------------------------------------------+
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
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.
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.
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
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 ...
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
RF
--
Ron Fox
NSCL
Michigan State University
East Lansing, MI 48824-1321
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)
}
}
}
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)"
}
}