user690182
user690182

Reputation: 279

TCL: how to return an array?

please find below a snippet of code that passes an array, manipulates the array, but i cannot return the new version of array.

here is the snippet :

    proc    get_mroute_active { &multicast }   {
        upvar   ${&multicast} MULTICAST ;

                set group   -1 ;
                set src -1 ;
                                set     mcast_group_source_id   -1 ;
                                set     MULTICAST($mcast_group_source_id,id) $mcast_group_source_id ;
                                set     MULTICAST($mcast_group_source_id,mcast_group) $group ;
                                set     MULTICAST($mcast_group_source_id,mcast_source) $src ;

        puts    [array size MULTICAST] ;
    parray  MULTICAST ;
}


array set     multicast { } ;

get_mroute_active [array get multicast] ;
puts    [array size multicast] ;
parray multicast ;

And the output of the code is :

3
MULTICAST(-1,id)           = -1
MULTICAST(-1,mcast_group)  = -1
MULTICAST(-1,mcast_source) = -1
0

Could you please help show me how the "MULTICAST" variable can be assigned to "multicast" ?

Upvotes: 3

Views: 5183

Answers (1)

kostix
kostix

Reputation: 55443

The short answer is: you can't return an array from a procedure as arrays are not values — they are peculiar named collections of named values.

There are several ways to deal with this:

The usual approach is to pass arrays by names and make the called procedure modify the array in place. For instance, the code

proc foo {arrayName} {
    upvar 1 $arrayName ary
    incr ary(one)
}
set x(one) 1
foo x
puts $x(one)

will print "2" as the procedure foo modified a specific value in the specified array in the caller's scope.

Notice how the caller passed the name of an array, "x", instead of "its value" (as you cannot extract a value from an array; but see below) and then the called procedure bound a local variable to that array by its name using the upvar command.

The other approach is to employ array get and array set commands to extract keys and values from arrays and populate arrays with keys and values, respectively. For instance:

set x(one) 1
set x(two) 2
array set another [array get x]
parray another

would print

another(one) = 1
another(two) = 2

The array get command, given an array, returns a flat list with its keys and their respective values interleaved. This way you can return the contents of an array from a procedure and then make the caller do whatever it wishes with these contents, for instance, use the array set command to populate another array in its scope (possibly the same which has been passed to that procedure in the first place).

Note that array set has merge semantics: it does not empty the target array before inserting/replacing its values using the source list.

The third (and may be the best) approach is to use dictionaries which are key/value maps as arrays do but are themselves values, so they can be passed around freely as other values. This requires Tcl 8.5 or later (in Tcl 8.4, you can use a backport of this code in the form of a package. With dicts you get it like this:

proc foo d {
    dict set d one 2
    return $d
}
set x [dict create]
dict set x one 1
set y [foo $x]
puts $y

which would print "one 2" as the procedure modified the original dictionary then returned it and then the caller assigned it to another variable.

Upvotes: 5

Related Questions