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

Nested "dict incr"?

745 views
Skip to first unread message

Ted Nolan <tednolan>

unread,
Aug 15, 2013, 2:55:51 PM8/15/13
to
I'm only fairly recently working with 8.5 on all my platforms, and I
am trying to start using dictionaries in new code. Still trying to
get my head around some of the implications..

In particular, consider this code:

#!/usr/bin/tclsh8.5
dict set new_dict my_inventory keychains 11
dict set new_dict my_inventory watchfobs 31
dict set new_dict my_inventory hatpins 42

Now suppose I buy another watchfob? I figured that

dict incr new_dict my_inventory watchfobs

would do the trick, but instead I get:

expected integer but got "keychains 11 watchfobs 31 hatpins 42"
while executing
"dict incr new_dict my_inventory watchfobs"
(file "/tmp/doit" line 8)

Of course this works:

set wf [dict get $new_dict my_inventory watchfobs]
incr wf
dict set new_dict my_inventory watchfobs $wf

but it seems cumbersome. If I can "set" a value, shouldn't I be
able to "incr" it? Is there a better way to do the increment?

--
------
columbiaclosings.com
What's not in Columbia anymore..

mango

unread,
Aug 15, 2013, 4:43:55 PM8/15/13
to
Clearly you have discovered that "dict incr" supports only a single level of keys. If you find the code sequence to fetch the value of a nested dictionary key, increment that value and set it back too "cumbersome", then the only resolution I can think of is to "write a proc". It's actually an interesting little tidbit to support an arbitrary depth of key nesting and the same semantics of "dict incr" for missing keys. I'm sure you are up to the challenge.

Andrew Mangogna

nagu

unread,
Aug 16, 2013, 4:20:51 AM8/16/13
to
Its also true with [dict lappend] that it doesn't accept nested key. As Andrew said, writing a proc is the solution. I just attempted this:

proc dict_incr {dictvarname args} {
upvar $dictvarname dictname
puts "$dictvarname $dictname"
if {[dict exists $dictname {*}$args]} {
set oldval [dict get $dictname {*}$args]
if {![string is integer -strict $oldval]} {
return -code error "nested key $args does not map to an integer in dict $dictvarname"
}
dict set dictname {*}$args [incr oldval]
} else {
dict set dictname {*}$args 1
}
}

set dict1 [dict create k1 {k2 {v1 0}}]
dict_incr dict1 k1 k2 v1
puts [dict get $dict1 k1 k2 v1]

set dict2 [dict create k1 {k2 {v1 abcd}}]
dict_incr dict2 k1 k2 v1


Best Regards,
nagu.

Ted Nolan <tednolan>

unread,
Aug 16, 2013, 8:10:27 AM8/16/13
to
In article <0d18d9a1-e3b3-477e...@googlegroups.com>,
nagu <nagarajan...@gmail.com> wrote:
>Its also true with [dict lappend] that it doesn't accept nested key. As
>Andrew said, writing a proc is the solution. I just attempted this:
>

OK, thanks!

I had figured that might be necessary, but was hoping for something builtin
that I had missed.

Schelte Bron

unread,
Aug 16, 2013, 9:31:08 AM8/16/13
to
Ted Nolan <tednolan> wrote:
> I had figured that might be necessary, but was hoping for
> something builtin that I had missed.

dict with new_dict {dict incr my_inventory watchfobs}

or

dict with new_dict my_inventory {incr watchfobs}


You will have to watch out for var names getting clobbered by dict
key names, but inside a helper proc you should be OK.


Schelte.

George Petasis

unread,
Aug 16, 2013, 2:46:34 PM8/16/13
to
Στις 16/8/2013 3:10 μμ, ο/η Ted Nolan <tednolan> έγραψε:
> In article <0d18d9a1-e3b3-477e...@googlegroups.com>,
> nagu <nagarajan...@gmail.com> wrote:
>> Its also true with [dict lappend] that it doesn't accept nested key. As
>> Andrew said, writing a proc is the solution. I just attempted this:
>>
>
> OK, thanks!
>
> I had figured that might be necessary, but was hoping for something builtin
> that I had missed.
>
Also note that dict is an ensemble, you can extend it.

Here is an example of an "klappend" dict method, which is an lappend
with a list of keys:

proc ::tcl::dict::klappend {dict args} {
upvar 1 $dict d
::set keys [::lrange $args 0 end-1]
try {
::set v [get $d {*}$keys]
::lappend v [::lindex $args end]
} on error {} {
::lappend v [::lindex $args end]
}
set d {*}$keys $v
};# ::tcl::dict::klappend

namespace ensemble configure dict -map \
[dict merge [namespace ensemble configure dict -map] \
{klappend ::tcl::dict::klappend}]

George

Ted Nolan <tednolan>

unread,
Aug 16, 2013, 11:40:50 PM8/16/13
to
In article <kul9ip$t8a$1...@speranza.aioe.org>,
Cool.

Donal K. Fellows

unread,
Aug 17, 2013, 3:54:35 PM8/17/13
to
On 15/08/2013 19:55, Ted Nolan <tednolan> wrote:
> I'm only fairly recently working with 8.5 on all my platforms, and I
> am trying to start using dictionaries in new code. Still trying to
> get my head around some of the implications..
[...]
> If I can "set" a value, shouldn't I be
> able to "incr" it? Is there a better way to do the increment?

Not with [dict incr]. The issue formally is detecting what the meaning
of various arguments is; there's an ambiguous case with:

dict incr fooDict $a $b

What is the meaning of $b in this if we allow multiple keys? I had to
pick something, so I made it the increment (which means that we don't
support multiple keys). A possible compromise might have been to let $a
be a list of keys, but that would be awkward for keys which aren't
simple words; a key principle of dicts is that keys can be *any* string.

It's a compromise. :-(

Donal.
--
Donal Fellows — Tcl user, Tcl maintainer, TIP editor.

nagu

unread,
Aug 18, 2013, 2:40:50 PM8/18/13
to
mmm.... I missed this basic point in my code. I assumed increment to be always 1. Then, the proc will have to be:

proc dict_incr {dictvarname increment args}

where args are just a sequence of keys. But, this changes the original syntax used in incr where increment comes at the end.

Best Regards,
nagu.
0 new messages