• confused about lists and strings...

    From Mark Summerfield@m.n.summerfield@gmail.com to comp.lang.tcl on Wed Jul 2 08:39:27 2025
    From Newsgroup: comp.lang.tcl

    Given this test.tcl code:

    proc main {} {
    set argv [lrange $::argv 1 end]
    process1 $argv
    process2 $argv
    process3 $argv
    }

    proc process1 args {
    set first [lindex $args 0]
    puts "first='$first' args='$args' list? [string is list -strict $args]"
    set rest [lrange $args 1 end]
    puts "rest='$rest' list? [string is list -strict $rest]"
    }

    proc process2 args {
    set first [lindex $args 0 0]
    puts "first='$first' args='$args' list? [string is list -strict $args]"
    set rest [lrange $args 1 end]
    puts "rest='$rest' list? [string is list -strict $rest]"
    }

    proc process3 args {
    set args {*}[lrange $args 0 end]
    set first [lindex $args 0]
    puts "first='$first' args='$args' list? [string is list -strict $args]"
    set rest [lrange $args 1 end]
    puts "rest='$rest' list? [string is list -strict $rest]"
    }

    main

    I get the following output:

    $ ./passlist.tcl ignored a b c d
    first='a b c d' args='{a b c d}' list? 1
    rest='' list? 1
    first='a' args='{a b c d}' list? 1
    rest='' list? 1
    first='a' args='a b c d' list? 1
    rest='b c d' list? 1

    Only process3 works as I expect, but it seems a bit clumsy having
    to use the line
    set args {*}[lrange $args 0 end]
    although it does have the virtue of doing what I want.

    What am I misunderstanding about process1 and process2?
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Harald Oehlmann@wortkarg3@yahoo.com to comp.lang.tcl on Wed Jul 2 11:27:54 2025
    From Newsgroup: comp.lang.tcl

    Am 02.07.2025 um 10:39 schrieb Mark Summerfield:
    proc process1 args {
    set first [lindex $args 0]
    puts "first='$first' args='$args' list? [string is list -strict $args]"
    set rest [lrange $args 1 end]
    puts "rest='$rest' list? [string is list -strict $rest]"
    }

    The name "args" is special in TCL returning all remaining arguments as a
    list.

    As you call:
    process1 $argv

    The one argument is put in a list. The result is a matrix (list in list).

    How to solve:

    a) don't use "args":

    proc process1 myargs {
    set first [lindex $myargs 0]
    puts "first='$first' args='$myargs' list? [string is list -strict $myargs]"
    set rest [lrange $myargs 1 end]
    puts "rest='$rest' list? [string is list -strict $rest]"
    }

    b) use the delist operator:

    process {*}$argv

    Hope this helps,
    Harald
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Mark Summerfield@m.n.summerfield@gmail.com to comp.lang.tcl on Wed Jul 2 09:34:12 2025
    From Newsgroup: comp.lang.tcl

    On Wed, 2 Jul 2025 11:27:54 +0200, Harald Oehlmann wrote:

    Am 02.07.2025 um 10:39 schrieb Mark Summerfield:
    proc process1 args {
    set first [lindex $args 0]
    puts "first='$first' args='$args' list? [string is list -strict $args]" >> set rest [lrange $args 1 end]
    puts "rest='$rest' list? [string is list -strict $rest]"
    }

    The name "args" is special in TCL returning all remaining arguments as a list.

    As you call:
    process1 $argv

    The one argument is put in a list. The result is a matrix (list in list).

    How to solve:

    a) don't use "args":

    proc process1 myargs {
    set first [lindex $myargs 0]
    puts "first='$first' args='$myargs' list? [string is list -strict $myargs]"
    set rest [lrange $myargs 1 end]
    puts "rest='$rest' list? [string is list -strict $rest]"
    }

    b) use the delist operator:

    process {*}$argv

    Hope this helps,
    Harald

    Thanks, I hadn't realised that using `args` would give me a list in a list.
    I now just pass the list as-is (and called `rest` to avoid confusion!).
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From et99@et99@rocketship1.me to comp.lang.tcl on Wed Jul 2 02:53:02 2025
    From Newsgroup: comp.lang.tcl

    On 7/2/2025 2:34 AM, Mark Summerfield wrote:
    On Wed, 2 Jul 2025 11:27:54 +0200, Harald Oehlmann wrote:

    Am 02.07.2025 um 10:39 schrieb Mark Summerfield:
    proc process1 args {
    set first [lindex $args 0]
    puts "first='$first' args='$args' list? [string is list -strict $args]"
    set rest [lrange $args 1 end]
    puts "rest='$rest' list? [string is list -strict $rest]"
    }

    The name "args" is special in TCL returning all remaining arguments as a
    list.

    As you call:
    process1 $argv

    The one argument is put in a list. The result is a matrix (list in list).

    How to solve:

    a) don't use "args":

    proc process1 myargs {
    set first [lindex $myargs 0]
    puts "first='$first' args='$myargs' list? [string is list -strict
    $myargs]"
    set rest [lrange $myargs 1 end]
    puts "rest='$rest' list? [string is list -strict $rest]"
    }

    b) use the delist operator:

    process {*}$argv

    Hope this helps,
    Harald

    Thanks, I hadn't realised that using `args` would give me a list in a list.
    I now just pass the list as-is (and called `rest` to avoid confusion!).

    This used to confuse me totally. Now I understand (I think) that args and {*} are inverses of each other. If you can get your head around this, then you got it :)


    % proc p args {puts [list {*}$args]} ;# so an inverse of an inverse is an identity

    % p 1 2 3
    1 2 3

    % p {1 2 3}
    {1 2 3}

    % p {1 2 3} {4 5 6}
    {1 2 3} {4 5 6}

    % p {*}{1 2 3}
    1 2 3



    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Rich@rich@example.invalid to comp.lang.tcl on Wed Jul 2 12:55:14 2025
    From Newsgroup: comp.lang.tcl

    et99 <et99@rocketship1.me> wrote:
    On 7/2/2025 2:34 AM, Mark Summerfield wrote:
    Thanks, I hadn't realised that using `args` would give me a list in a list. >> I now just pass the list as-is (and called `rest` to avoid confusion!).

    This used to confuse me totally. Now I understand (I think) that
    args and {*} are inverses of each other. If you can get your head
    around this, then you got it :)

    If you understand what 'xargs' does on a Unix CLI, {*} is essentially a
    "Tcl xargs".

    It (when used for calling a proc) converts "a list of things" into
    "individual parameters to the proc".

    What it really does is "unwraps" the lists contents. So instead of
    having "one list containing 5 things" you end up with the internal "5
    things" each individually.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Rich@rich@example.invalid to comp.lang.tcl on Wed Jul 2 12:59:32 2025
    From Newsgroup: comp.lang.tcl

    Mark Summerfield <m.n.summerfield@gmail.com> wrote:
    Thanks, I hadn't realised that using `args` would give me a list in a
    list.

    The proc man page documents this 'special nature' of 'args' (note the
    third sentence below):

    There is one special case to permit procedures with variable numbers
    of arguments. If the last formal argument has the name “args”, then
    a call to the procedure may contain more actual arguments than the
    proce‐ dure has formal arguments. In this case, all of the actual
    arguments starting at the one that would be assigned to args are
    combined into a list (as if the list command had been used); this
    combined value is as‐ signed to the local variable args.

    Note also from the above that it is only 'special' when it is the "last argument" to the proc.

    $ rlwrap tclsh
    % proc abc {args y} {puts "args='$args' y='$y'" }
    % abc 1 2
    args='1' y='2'
    %

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Christian Gollwitzer@auriocus@gmx.de to comp.lang.tcl on Wed Jul 2 19:40:08 2025
    From Newsgroup: comp.lang.tcl

    Am 02.07.25 um 11:34 schrieb Mark Summerfield:

    Thanks, I hadn't realised that using `args` would give me a list in a list.
    I now just pass the list as-is (and called `rest` to avoid confusion!).

    Actually this is very similar in Python:

    def fun1(*args):
    # now you have all arguments in the list args
    pass
    fun1(1,2,3)


    and the inverse:

    def fun2(a,b,c):
    pass

    list=[1, 2, 3]
    fun2(*list)

    That'd be in Tcl

    proc fun1 {args} {
    }

    fun1 1 2 3

    proc fun2 {a b c} { }

    set list "1 2 3"
    fun2 {*}$list


    Christian
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From et99@et99@rocketship1.me to comp.lang.tcl on Wed Jul 2 12:41:34 2025
    From Newsgroup: comp.lang.tcl

    On 7/2/2025 5:55 AM, Rich wrote:
    et99 <et99@rocketship1.me> wrote:
    On 7/2/2025 2:34 AM, Mark Summerfield wrote:
    Thanks, I hadn't realised that using `args` would give me a list in a list. >>> I now just pass the list as-is (and called `rest` to avoid confusion!).

    This used to confuse me totally. Now I understand (I think) that
    args and {*} are inverses of each other. If you can get your head
    around this, then you got it :)

    If you understand what 'xargs' does on a Unix CLI, {*} is essentially a
    "Tcl xargs".

    It (when used for calling a proc) converts "a list of things" into "individual parameters to the proc".

    What it really does is "unwraps" the lists contents. So instead of
    having "one list containing 5 things" you end up with the internal "5
    things" each individually.


    Yes, it was your explanation, some years back which opened my brain to {*}.

    One pleasant implementation detail I discovered, is that {*}{} does not produce an argument, which is useful in say, passing in some optional arguments that are then passed into the middle of some other command, such as lsort, for example:

    proc mysort {alist args} {
    ...
    lsort {*}$args $alist
    ...
    }

    If args is null, then lsort only sees one arg; no need to test for args being null.



    --- Synchronet 3.21a-Linux NewsLink 1.2