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

dict replace $list does not return a dict

16 views
Skip to first unread message

Harald Oehlmann

unread,
Apr 28, 2011, 4:59:21 AM4/28/11
to
Task on TCL 8.5.9:
Transfer list "l"
% set l {A 0 A 1 B 0}
into dict "d".

Classical solution:
% set d [dict create {*}$l]
A 1 B 0
I thought, this would be with less shimmering (specially if l is
already a dict):
% set d [dict replace $l]
A 0 A 1 B 0
but the double-key "A" is not removed thus this is internally not a
dict.

Is this a bug ? Is this smart ?

---
Backround: Do the tcl 8.6 [lsearch -stride 2 -exact $l B] in an
efficient way by:
(l may be a list or a dict)
proc lsearchstride {l k} {
set d [dict replace $l]
if { ! [dict exists $d $k]} {
return -1
}
set pos [lsearch -exact [dict keys $d] $k]
for {set pos [expr {$pos * 2}]}\
{[lindex $l $pos] ne $k}\
{incr pos 2}\
{}
return $pos
}

-Harald

Andreas Leitgeb

unread,
Apr 28, 2011, 10:26:55 AM4/28/11
to
Harald Oehlmann <wort...@yahoo.de> wrote:
> Task on TCL 8.5.9:
> Transfer list "l"
> % set l {A 0 A 1 B 0}
> into dict "d".
>
> Classical solution:
> % set d [dict create {*}$l]
> A 1 B 0

Now, $d is a dict, and $l is still a list.
Btw., dict create didn't get to see the original $l object,
so it couldn't possibly shimmer $l, even if it "wanted" to ;)

> I thought, this would be with less shimmering (specially if l is
> already a dict):

$l isn't a pure dict-object at this point. (It is, however acceptable
as a dict.)

> % set d [dict replace $l]
> A 0 A 1 B 0

It seems to me that it is actually *not shimmering at all*, when
there is nothing here to replace. "dict replace" doesn't guarantee
a canonical dict.
Try also: dict replace {asdfa{fdgsdf}sdgasdgf}
It doesn't even check its argument for being anything remotely
conceivable as a dict. It's different in this regard from things
like "lrange $l 0 end" or "lappend someVar"

> Is this a bug ? Is this smart ?

It's an (imho)acceptable consequence of "do nothing, gracefully".

Harald Oehlmann

unread,
Apr 28, 2011, 11:20:42 AM4/28/11
to
On 28 Apr., 16:26, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at>
wrote:

> Harald Oehlmann <wortka...@yahoo.de> wrote:
> > Task on TCL 8.5.9:
> > Transfer list "l"
> > % set l {A 0 A 1 B 0}
> > into dict "d".
>
> > Classical solution:
> > % set d [dict create {*}$l]
> > A 1 B 0
>
> Now, $d is a dict, and $l is still a list.
> Btw., dict create didn't get to see the original $l object,
> so it couldn't possibly shimmer $l, even if it "wanted" to ;)

Dear Andreas,

thank you for the answer.

Perhaps I did not correctly describe my intention.
I wanted to speedup following "dict get" operations and inshure to
have an internal dict.
In addition, I wanted to have a cheap copy (e.g. internally just have
the reference count increased and no physical copy) of the object, if
$l is already a canonical dict.

Anyway, I stopped with those optimisations and stayed with my older
list search method:
proc parse::lsearchpair {l k} {
foreach pos [lsearch -all -exact $l $k] {
if { 0 == ($pos % 2) } {
return $pos
}
}
return -1
}

-Harald

Andreas Leitgeb

unread,
Apr 28, 2011, 1:19:36 PM4/28/11
to
Harald Oehlmann <wort...@yahoo.de> wrote:
> Perhaps I did not correctly describe my intention.
> I wanted to speedup following "dict get" operations and inshure to
"ensure", not "inshure"
> have an internal dict.

Your procedure returned the index position. If you *really* need
the index (and don't just check it for >=0 or <0 or use it to get the
(index+1)st element), then dicts wouldn't have helped you, anyway.

If, however, you really just want lookup of the associated value then
dicts would serve you well:


proc parse::lsearchpair {l k} {

if {[dict exists $l $k]} {dict get $l $k}
}
(which returns the value or "", not the index!)

> In addition, I wanted to have a cheap copy (e.g. internally just have
> the reference count increased and no physical copy) of the object, if
> $l is already a canonical dict.

The only "no-op" on dicts or dict-alikes that would really "ensure"
properness of the resulting dict would be "dict merge"ing it with
itself:
set d [dict merge $l $l]

PS: Note, that tcl 8.5.8 (which seems to be still current with at least
Ubuntu 10.4 and 10.10) has a few severe bugs in this area!! This
may be of importance to you, if you intend to distribute your script
to others who may have older versions of tcl. (in 8.5.9 these
bugs are fixed).

Harald Oehlmann

unread,
Apr 28, 2011, 2:11:29 PM4/28/11
to
On 28 Apr., 19:19, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at>
wrote:

> The only "no-op" on dicts or dict-alikes that would really "ensure"
> properness of the resulting dict would be "dict merge"ing it with
> itself:
>   set d [dict merge $l $l]

Thank you for the answer and the sample script.
Unfortunately, I need the index and the list is large and may have
multiple keys.

The dict merge with itself is clever and results in a canonical dict.
It would be interesting, which is more efficient:
a) set d [dict create (*)$l]
b) set d [dict merge $l $l]
for the cases:
- l has an internal dict representation
- l is just a list but has no duplicate keys
- l is a list and has duplicate keys

Nevertheless, enjoy your evening,
Harald

Andreas Leitgeb

unread,
Apr 29, 2011, 7:55:58 AM4/29/11
to
Harald Oehlmann <wort...@yahoo.de> wrote:
> It would be interesting, which is more efficient:
> a) set d [dict create (*)$l]
> b) set d [dict merge $l $l]

> for the cases:
> - l has an internal dict representation

"create" doesn't use the dict-rep and doesn't create one for $l, either.
"merge" uses existing dict-rep, or creates one for $l, otherwise.


The price of merging two large dicts actually outweighs the
recreation of their dict-rep, unless the original list
contained a high percentage of duplicates.

If you expect really *lots* of duplicates *and* do at least a couple
of lookups between each change to the list, *then* go with merge,
else "create" trumps.

MartinLemburg@Siemens-PLM

unread,
May 2, 2011, 4:56:48 AM5/2/11
to a...@logic.at
Hi,

in which way does "dict create" NOT use the dict representation?

% ::tcl::unsupported::representation [dict create {*}[list a 1 a 2 b 3 c 4]]
value is a dict with a refcount of 1, object pointer at
01EB9488, internal representation 01EA8FA0:01E18190,
no string representation.

I'm a bit surprised, that the noop of "dict replace" really does nothing, even if the data given to "dict replace" is not a "valid" dictionary and would have to be changed into a dictionary.

Yes ... the next use of the returned data of a noop "dict replace" in another dict operation would convert the data in a dictionary, but I ... am still surprised, that the return of a dict command/operation does NOT return a valid dictionary!

Another thing, that came to my mind was ... is it really necessary to shimmer between dictionaries and lists, if a dictionary is accessed via list commands?
The string representation of a dictionary is a valid list, so why not maintaining the dictionary, while building a kind of secondary type structure (list type) to allow the usage of this data as list.
This secondary type structure is (as secondary says) not that important, than the primary type structure (here of dict type) and may be more easy lost than the primary.

This would perhaps optimize such situations, where a data collection must be accessed as dictionary and as list, where a conversion from this to that and back may have big costs.

Ok - others may say, that the design of the data and its usage is not good, and should be changed, to use one data access API on the data.
But tcl is so flexible, that a dict is a list and a list may be a dict, that this shimmering of data between those internal data types perhaps could be prevented!

Best regards,

Martin

Andreas Leitgeb

unread,
May 2, 2011, 7:27:45 AM5/2/11
to
First of all, you seem to be using Google's "new interface".
Don't! It sucks. It loses the reference-information, so your
posting doesn't show which other post you responded to. Each
article posted by that new google-interface appears to start
a new thread. There was a discussion in some other group about
this new google groups interface, and accordign to that, at
least back then, google still offered their "old" interface,
as well, which didn't have these problems.

MartinLemburg@Siemens-PLM <martin.lembur...@gmx.net> wrote:
> in which way does "dict create" NOT use the dict representation?

It was about "use", not "create". "dict create" creates a dict with
internal dict-rep, of course, but if the thing behind the {*} already
was a dict, then the outer "dict create" wouldn't even get to see that.

dict create {*}[dict create a 1 a 2 b 3 c 4]

causes a dict to be created, then converted to a list, which is then
expanded into separate arguments of the outer "dict create". I can't
believe, that any hidden dict-metadata would be preserved over separated
arguments, to be possibly seen and re-used by outer [dict create].

> I'm a bit surprised, that the noop of "dict replace" really does nothing,
> even if the data given to "dict replace" is not a "valid" dictionary and
> would have to be changed into a dictionary.

Well, yes, that's "do nothing" in its purest form :-)
Perhaps some canonical idiom of dict-purification should be
proposed (TIP anyone?). I'd suggest [dict merge $d] rather
than [dict replace], but either (or even both) would do.

> Another thing, that came to my mind was ... is it really necessary to
> shimmer between dictionaries and lists, if a dictionary is accessed via
> list commands?

Some of this shimmering is already optimized away, internally.

Andreas Leitgeb

unread,
May 2, 2011, 7:52:33 AM5/2/11
to
Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at> wrote:
> Perhaps some canonical idiom of dict-purification should be
> proposed (TIP anyone?). I'd suggest [dict merge $d] rather
> than [dict replace], but either (or even both) would do.

Hmm, partially changing my mind only 5 minutes later ;-)

Based on [lrange $l 0 end] being the canonisation idiom for lists,
[dict replace $d] would indeed seem like the more natural pendant.

0 new messages