• Re: work with nested struct in proc in TCL

    From Oleg Nemanov@21:1/5 to All on Fri Apr 22 07:15:23 2022
    In C i just pass a pointer to a nested struct into an internal functions and, thus, this function works only with a data they know and need.
    This function can change it during a work. How can i achieve something like this in TCL?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Oleg Nemanov@21:1/5 to All on Fri Apr 22 07:03:47 2022
    Hi, all.

    I have some package where some procs get as argument a dict with nested dicts. These procs call another internal procs that get as argument this dict too, but needs only a subdict to do its work. I can't pass this subdict by name,
    because i can't modify it in place, in this case. And now my internal procs should know the full path to subdict they need to do its work. E.g.:

    set ctx [dict create src [dict create SOME_DATA] dst [dict create SOME_DAT]] proc do_work {_ctx} {
    upvar $_ctx ctx
    ...
    src_proc1 ctx
    dst_proc1 ctx
    ...
    }

    proc src_proc1 {_ctx} {
    upvar $_ctx ctx
    dict set ctx src SOME_KEY 1
    }

    proc dst_proc1 {_ctx} {
    upvar $_ctx ctx
    dict set ctx dst SOME_KEY 1
    }

    I want to pass to an internal proc just needed subdist like(thus, internal procs will be know only keys its needed for work):

    proc do_work {_ctx} {
    upvar $_ctx ctx
    ...
    src_proc1 [dict get $ctx src]
    dst_proc1 [dict get $ctx dst]
    ...
    }

    But i can't modify this data in src_proc1/dst_proc1 in place, in this case.

    So, how you solve these situations?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Oleg Nemanov@21:1/5 to All on Fri Apr 22 07:47:02 2022
    пятница, 22 апреля 2022 г. в 17:32:46 UTC+3, [email protected]:
    Hello,

    Instead of upvars, you could use "global ctx" in the proc's. Then your specialized proc's can work with the full dict or you can send the key
    names as you need to.

    I thought about something like this. But operation like 'dict get' where we need '$' before a first argument make this
    method harder.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to Oleg Nemanov on Fri Apr 22 15:02:20 2022
    Oleg Nemanov <[email protected]> wrote:
    Hi, all.

    I have some package where some procs get as argument a dict with
    nested dicts. These procs call another internal procs that get as
    argument this dict too, but needs only a subdict to do its work. I
    can't pass this subdict by name, because i can't modify it in place,
    in this case.

    I'm somewhat confused by the above. If you don't want to modify the
    subdict, then there is no need to pass it by name. Just pass in the
    subdict.

    i.e.:


    set ctx [dict create src [dict create SOME_KEY SOME_DATA] \
    dst [dict create SOME_KEY SOME_DAT]]

    proc do_work {ctx} {
    src_proc1 [dict get $ctx src]
    dst_proc1 [dict get $ctx dst]
    ...
    }

    proc src_proc1 {ctx} {
    # do things with the 'src' sub dict
    }


    However, if you in fact 'do' want to modify the subdict, you still do
    not /need/ to pass by name, doing so just makes the call site a bit
    more complex:

    set ctx [dict create src [dict create SOME_KEY SOME_DATA] \
    dst [dict create SOME_KEY SOME_DAT]]

    proc do_work {ctx} {
    dict set ctx src [src_proc1 [dict get $ctx src]]
    dict set ctx dst [dst_proc1 [dict get $ctx dst]]
    ...
    }

    proc src_proc1 {ctx} {
    dict set ctx NEW_KEY NEW_DATA
    dict set ctx SOME_KEY NEW_DATA
    return $ctx
    }

    The runtime cost is a bit more copying and garbage collection, but as
    you have omitted any information as to the size of 'ctx', I can't know
    whether that cost is excessive or not.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Schelte@21:1/5 to Oleg Nemanov on Fri Apr 22 17:09:44 2022
    On 22/04/2022 16:03, Oleg Nemanov wrote:
    I want to pass to an internal proc just needed subdist like(thus, internal procs will be know only keys its needed for work):

    proc do_work {_ctx} {
    upvar $_ctx ctx
    ...
    src_proc1 [dict get $ctx src]
    dst_proc1 [dict get $ctx dst]
    ...
    }

    But i can't modify this data in src_proc1/dst_proc1 in place, in this case.

    So, how you solve these situations?

    You can use [dict with] (if src and dst are guaranteed to be present),
    or [dict update]:

    proc sub_proc {_sub} {
    upvar $_sub sub
    dict set sub SOME_KEY 1
    }

    proc do_work {_ctx} {
    upvar $_ctx ctx
    # ...
    dict update ctx src src dst dst {
    sub_proc src
    sub_proc dst
    }
    # ...
    }

    set ctx {}
    do_work ctx
    puts $ctx

    Result: src {SOME_KEY 1} dst {SOME_KEY 1}


    Schelte

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From [email protected]@21:1/5 to Oleg Nemanov on Fri Apr 22 10:32:41 2022
    On 4/22/22 10:03 AM, Oleg Nemanov wrote:

    But i can't modify this data in src_proc1/dst_proc1 in place, in this case.

    So, how you solve these situations?

    Hello,

    Instead of upvars, you could use "global ctx" in the proc's. Then your specialized proc's can work with the full dict or you can send the key
    names as you need to.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From [email protected]@21:1/5 to Oleg Nemanov on Fri Apr 22 10:54:57 2022
    On 4/22/22 10:47 AM, Oleg Nemanov wrote:

    I thought about something like this. But operation like 'dict get' where we need '$' before a first argument make this
    method harder.

    Hello,

    I am not really clear here but I suspect you need the ability to work
    with different keys. You can make the key name a parameter to the
    proc's. You can even parameterize dict name itself if you have multiple
    ones with a similar structure.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Oleg Nemanov@21:1/5 to All on Fri Apr 22 09:22:00 2022
    пятница, 22 апреля 2022 г. в 18:02:24 UTC+3, Rich:
    The runtime cost is a bit more copying and garbage collection, but as
    you have omitted any information as to the size of 'ctx', I can't know whether that cost is excessive or not.

    In my case a size of ctx is small. Just 2 nested dicts with few keys in each. But this case prompted me to think about more general cases and how others solve this :-). And also what to do if a size of dict is big.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Oleg Nemanov@21:1/5 to All on Fri Apr 22 09:18:09 2022
    пятница, 22 апреля 2022 г. в 17:55:02 UTC+3, [email protected]:
    On 4/22/22 10:47 AM, Oleg Nemanov wrote:

    I thought about something like this. But operation like 'dict get' where we need '$' before a first argument make this
    method harder.
    Hello,

    I am not really clear here but I suspect you need the ability to work
    with different keys. You can make the key name a parameter to the
    proc's. You can even parameterize dict name itself if you have multiple
    ones with a similar structure.

    The things is even more complecated if there is nested list with dict like this:
    set ctx [dict create srcs [list [dict create SOME_DATA] [dict create SOME_DATA]]]

    And we want to process each list item in a loop. In this case, Rich and Schelte method isn't work and, it seems, the more simple way is:

    set src [lindex [dict get $ctx srcs] 0]
    src_proc1 src
    dict set ctx srcs [lreplace [dict get $ctx srcs] 0 0 $src]

    And i thinkg this is not very effectively :-).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From [email protected]@21:1/5 to [email protected] on Fri Apr 22 14:50:49 2022
    On 4/22/22 2:24 PM, [email protected] wrote:
    So now you can individually process the keys. You can parameterize the computation too.



    Hello,
    Here is an expanded version. You can work with multiple dict's and keys,
    and apply different proc's to each.


    set ctx [dict create src {10 20 30} dst {100 200 300}]
    set ctx_2 [dict create src {10 20 30 40 50} dst {100 200 300 400 500}]

    proc flexible {dname key pname} {
    global ctx
    global ctx_2

    # process it.
    dict set $dname $key [$pname [dict get [set $dname] $key]]

    }

    proc add {l} {
    set new_l {}
    foreach x $l {
    lappend new_l [expr $x + 1]
    }
    return $new_l
    }

    proc multiply {l} {
    set new_l {}
    foreach x $l {
    lappend new_l [expr $x * 2]
    }
    return $new_l
    }


    # You can now test it:

    flexible ctx src add
    flexible ctx src multiply
    flexible ctx dst add


    flexible ctx_2 src add
    flexible ctx_2 src multiply
    flexible ctx_2 dst add

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to Oleg Nemanov on Fri Apr 22 18:53:03 2022
    Oleg Nemanov <[email protected]> wrote:
    ???????, 22 ?????? 2022 ?. ? 17:55:02 UTC+3, [email protected]:
    The things is even more complecated if there is nested list with dict
    like this: set ctx [dict create srcs [list [dict create SOME_DATA]
    [dict create SOME_DATA]]]

    And we want to process each list item in a loop. In this case, Rich
    and Schelte method isn't work and, it seems, the more simple way is:

    set src [lindex [dict get $ctx srcs] 0]
    src_proc1 src
    dict set ctx srcs [lreplace [dict get $ctx srcs] 0 0 $src]

    And i thinkg this is not very effectively :-).

    Tcl's [time] command can be a great way to see if something seems not efficient.

    In this case, for the set src and src_proc1 src lines, only an
    additional pointer, to the list element, should be created. Should
    src_proc1 modify (via upvar) the contents of src, then a copy of what
    is in 'src' will be made.

    The last line should then replace just the one list element (not copy
    and GC the entire dict). So while some copying and GC will happen, it
    should be limited to just the parts that got changed, not the entire
    'ctx' dict.

    The tcl::unsupported::representation can be very illuminating for
    discovering how the runtime deals with manipulating data under the
    hood.

    Note:

    $ rlwrap tclsh
    % set ctx [dict create srcs [list [dict create a b] [dict create c d]] dst [dict create e f]]
    srcs {{a b} {c d}} dst {e f}
    % tcl::unsupported::representation $ctx
    value is a dict with a refcount of 2, object pointer at 0x115b780,
    internal representation 0x11967e0:(nil), string representation "srcs
    {{a b} {..."
    % tcl::unsupported::representation [lindex [dict get $ctx srcs] 0]
    value is a dict with a refcount of 4, object pointer at 0x1156a70,
    internal representation 0x113df80:(nil), string representation "a b"
    % set src [lindex [dict get $ctx srcs] 0]
    a b
    % tcl::unsupported::representation $src
    value is a dict with a refcount of 5, object pointer at 0x1156a70,
    internal representation 0x113df80:(nil), string representation "a b"
    %

    src is now pointing to the same object that five other references are
    also pointing towards.

    Now, modify via lreplace:

    % set new [dict create y z]
    y z
    % tcl::unsupported::representation $new
    value is a dict with a refcount of 4, object pointer at 0x115b000, internal repr esentation 0x1196de0:(nil), string representation "y z"
    % dict set ctx srcs [lreplace [dict get $ctx srcs] 0 0 $new]
    srcs {{y z} {c d}} dst {e f}
    % tcl::unsupported::representation $ctx
    value is a dict with a refcount of 2, object pointer at 0x115b780, internal repr esentation 0x11967e0:(nil), string representation "srcs {{y z} {..."
    % tcl::unsupported::representation $new
    value is a dict with a refcount of 5, object pointer at 0x115b000,
    internal representation 0x1196de0:(nil), string representation "y z"
    % tcl::unsupported::representation [lindex [dict get $ctx srcs] 0]
    value is a dict with a refcount of 5, object pointer at 0x115b000,
    internal representation 0x1196de0:(nil), string representation "y z"
    %

    ctx is the same object pointer as before (0x115b780).

    the new dict began life as poinnter at 0x115b000, and remained the same
    after 'splicing'

    The item spliced in is the same obj pointer as new when it began life (0x115b000). So the thing that was in 'new' just got 'spliced into'
    the list by having the first list cell point at the same thing "new"
    was pointing at.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From [email protected]@21:1/5 to Oleg Nemanov on Fri Apr 22 14:24:39 2022
    On 4/22/22 12:18 PM, Oleg Nemanov wrote:

    The things is even more complecated if there is nested list with dict like this:
    set ctx [dict create srcs [list [dict create SOME_DATA] [dict create SOME_DATA]]]

    And we want to process each list item in a loop. In this case, Rich and Schelte
    method isn't work and, it seems, the more simple way is:


    Hello,

    Your explanation could use some help. In any case, it looks like using
    global would solve your dilemma:

    set ctx [dict create src {10 20 30} dst {100 200 300}]

    proc add_one {key} {
    global ctx

    # process it. see note at the end.
    set new_x {}
    foreach x [dict get $ctx $key] {
    lappend new_x [expr $x + $x]
    }

    dict set ctx $key $new_x
    }

    add_one src
    add_one src
    add_one dst


    So now you can individually process the keys. You can parameterize the computation too.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Oleg Nemanov@21:1/5 to All on Tue Apr 26 07:25:38 2022
    пятница, 22 апреля 2022 г. в 21:53:07 UTC+3, Rich:
    Tcl's [time] command can be a great way to see if something seems not efficient.

    To see if something seems not efficient we need an another implementation
    to compare with :-). [time] by itself is useless in a case where we can't compare with
    something. My question was more about how others are solving this large nested structure issue.
    To compare with my method :-).

    [CUT]

    Thanks for the detailed explanation with examples.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)