Discussion:
Array get element with default (no error if not exist)
Add Reply
RodionGork
2024-08-16 07:10:30 UTC
Reply
Permalink
Hi Friends!

Still making my first feeble steps in TCL so please excuse me if this is
naive or was asked multiple times.

Attempt to fetch by non-existing key in "associative array" results in
error, e.g.

set a(1) 5
puts $a(2) ;# yields error

the workaround seems to be [info exists ::a(2)] which feels a bit remote
from other "array" commands.

Is there some motivation why some command for get-with-default is not
implemented, e.g.

puts [array peek $a 2 "default value"]

Popular use-case for this would be creating map where elements are
updated (like counter of words etc) - though I found this is cleverly
covered by "incr" and "append" commands properly behaving
when element to be incremented or appended does not exist yet.

But I suspect there are other situations when such a command may be
handy.

Also why [array exists ...] command does not exist (while [dict exists
..] does)? Perhaps there is something about no good syntax for it due
to how arrays are implemented?
--
to email me substitute github with gmail please
Ralf Fassel
2024-08-16 09:11:29 UTC
Reply
Permalink
* RodionGork <***@github.com>
| Is there some motivation why some command for get-with-default is not
| implemented, e.g.
| puts [array peek $a 2 "default value"]

Most probably because up to now nobody required it so badly that s/he
implemented it, since the [info exists] approach is not too bad IMHO.

proc array_peek {arr key default} {
upvar $arr a
if {![array exists a] || ![info exists a($key)]} {
return $default
}
return $a($key)
}
array set a {1 one}
array_peek a 1 "default value"
=> one
array_peek a 2 "default value"
=> default value

Or you could set up a read-trace on the array to provide non-existing values:

array set a {}
trace add variable a read set_array_default
proc set_array_default {name1 name2 op} {
upvar a $name1
# puts stderr "set_array {$name1} {$name2} {$op}"
if {![info exists a($name2)]} {
set a($name2) "default"
}
}
set a(2)
=> default

Though that would alter the array and make the key exists which might
not be what you want.

HTH
R'
RodionGork
2024-08-17 06:13:34 UTC
Reply
Permalink
Friends, thanks for all the replies!

And for suggestions providing insight on techniques of which I wasn't
aware of, especially that "read trace".

Yep, I definitely meant something like [array element_exists ...] rather
than [array exists ...] which seems a bit inconsistent with [dict exists
..] but of course it is not a big issue.

My curiosity was mostly satisfied, thanks! The only small thing I still
haven't grasped well is why it happened that checking for element in
array found its way into [info exists...] rather than some [array ...]
subcommands. I suspected there was something hindering implementing it
there, but probably it just, well, so happened historically :)
--
to email me substitute github with gmail please
Rich
2024-08-17 16:17:23 UTC
Reply
Permalink
Post by RodionGork
Friends, thanks for all the replies!
And for suggestions providing insight on techniques of which I wasn't
aware of, especially that "read trace".
Yep, I definitely meant something like [array element_exists ...] rather
than [array exists ...] which seems a bit inconsistent with [dict exists
..] but of course it is not a big issue.
My curiosity was mostly satisfied, thanks! The only small thing I still
haven't grasped well is why it happened that checking for element in
array found its way into [info exists...] rather than some [array ...]
subcommands. I suspected there was something hindering implementing it
there, but probably it just, well, so happened historically :)
[info exists] is used for determining if a variable exists.

A Tcl array is built such that it is a collection of individual
variables referenced together as a set (this is also the reason why you
can't "pass" an array to a proc, nor [return] an array from a proc in
the same way you can pass/return a dict [1]).

Given that the array is merely a set of individual variables, someone,
way back in the distant past, decided it was more consistent to use the
"does this variable exist" command to determine if an individual
variable existed in an array.


[1] You can pass in the name of the array, and use upvar to access it, or
you can convert to a string and pass in the string. And to [return]
you have to convert to a string ([array get]) to return one. But this
does not work:

proc handle {arr} {
puts "I have array [array get arr]"
}

set array(1) something

handle $array

You get "can't read "array": variable is array" as an error message.

This difference is also why you can [trace] individual array elements
(they are individual variables) but cannot [trace] individual dict
elements (they are just entries inside the dict variable).
RodionGork
2024-08-18 14:10:58 UTC
Reply
Permalink
Thank you, this definitely makes sense!

I remember about issue with passing/returning array in functions. By the
way I still am not sure why it works with global for example... if they
are actually different variables, every key-pair :) e.g. like here:

https://github.com/RodionGork/languages-benchmark/blob/main/02-primes/primes_a.tcl#L7
--
to email me substitute github with gmail please
Rich
2024-08-18 16:43:41 UTC
Reply
Permalink
Post by RodionGork
Thank you, this definitely makes sense!
I remember about issue with passing/returning array in functions. By the
way I still am not sure why it works with global for example... if they
https://github.com/RodionGork/languages-benchmark/blob/main/02-primes/primes_a.tcl#L7
Because the [global] command works with names of variables, not the
actual variables. It links the global variable with the given name, to
a local variable of the same name, inside the proc. If the global name
references an array, then the new local name inside the proc references
the same array.

Emiliano
2024-08-16 14:16:04 UTC
Reply
Permalink
On Fri, 16 Aug 2024 07:10:30 +0000
Post by RodionGork
Hi Friends!
Still making my first feeble steps in TCL so please excuse me if this is
naive or was asked multiple times.
Attempt to fetch by non-existing key in "associative array" results in
error, e.g.
set a(1) 5
puts $a(2) ;# yields error
the workaround seems to be [info exists ::a(2)] which feels a bit remote
from other "array" commands.
Is there some motivation why some command for get-with-default is not
implemented, e.g.
puts [array peek $a 2 "default value"]
Popular use-case for this would be creating map where elements are
updated (like counter of words etc) - though I found this is cleverly
covered by "incr" and "append" commands properly behaving
when element to be incremented or appended does not exist yet.
But I suspect there are other situations when such a command may be
handy.
Already there in 8.7/9.0

% array default set foo NOSUCHVALUE
% set foo(1)
NOSUCHVALUE
% info exists foo(1)
0

8.7/9.0 also adds [dict getwithdefault] (aka [dict getdef]) for dict
values.
Post by RodionGork
Also why [array exists ...] command does not exist (while [dict exists
..] does)? Perhaps there is something about no good syntax for it due
to how arrays are implemented?
The [array exists] command is there since ... forever

% array exists foo
1
% array exists nosucharray
0

Well, at least since 8.4 . See https://www.tcl-lang.org/man/tcl8.4/TclCmd/array.htm
--
Emiliano
Ralf Fassel
2024-08-16 14:54:01 UTC
Reply
Permalink
* Emiliano <***@example.invalid>
| On Fri, 16 Aug 2024 07:10:30 +0000
| RodionGork <***@github.com> wrote:
| > Also why [array exists ...] command does not exist (while [dict exists
| > ..] does)? Perhaps there is something about no good syntax for it due
| > to how arrays are implemented?
| The [array exists] command is there since ... forever
| % array exists foo
| 1
| % array exists nosucharray
| 0

Obviouosly in this context the OP meant "array exists" with respect to a
specific key, i.e.

array exists arrayname key
=> return true if arrayname is an array and the key exists in that array.

R'
Loading...