• Array get element with default (no error if not exist)

    From RodionGork@rodiongork@github.com to comp.lang.tcl on Fri Aug 16 07:10:30 2024
    From Newsgroup: comp.lang.tcl

    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
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Ralf Fassel@ralfixx@gmx.de to comp.lang.tcl on Fri Aug 16 11:11:29 2024
    From Newsgroup: comp.lang.tcl

    * RodionGork <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'
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Emiliano@emil.g@example.invalid to comp.lang.tcl on Fri Aug 16 11:16:04 2024
    From Newsgroup: comp.lang.tcl

    On Fri, 16 Aug 2024 07:10:30 +0000
    RodionGork <rodiongork@github.com> wrote:

    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.


    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
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Ralf Fassel@ralfixx@gmx.de to comp.lang.tcl on Fri Aug 16 16:54:01 2024
    From Newsgroup: comp.lang.tcl

    * Emiliano <emil.g@example.invalid>
    | On Fri, 16 Aug 2024 07:10:30 +0000
    | RodionGork <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'
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From RodionGork@rodiongork@github.com to comp.lang.tcl on Sat Aug 17 06:13:34 2024
    From Newsgroup: comp.lang.tcl

    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
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Rich@rich@example.invalid to comp.lang.tcl on Sat Aug 17 16:17:23 2024
    From Newsgroup: comp.lang.tcl

    RodionGork <rodiongork@github.com> wrote:
    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).

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From RodionGork@rodiongork@github.com to comp.lang.tcl on Sun Aug 18 14:10:58 2024
    From Newsgroup: comp.lang.tcl

    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
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Rich@rich@example.invalid to comp.lang.tcl on Sun Aug 18 16:43:41 2024
    From Newsgroup: comp.lang.tcl

    RodionGork <rodiongork@github.com> wrote:
    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

    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.

    --- Synchronet 3.20a-Linux NewsLink 1.114