Je veux créer une commande de commodité array values arrayName comme un «revers» à la commande «noms de tableau».

Il est simple de créer un processus simple:

proc array_values {arrayName} {
    upvar 1 $arrayName ary
    set values {}
    foreach {name value} [array get ary] {lappend values $value}
    return $values
}

array set a {foo bar baz qux}
puts [array_values a]           ;# => bar qux

Cependant, j'ai des difficultés à créer une commande dans l'espace de noms ::tcl::array:

  • d'abord quelques devoirs:

    1. array est-il un ensemble d'espaces de noms? Oui.

       % ensemble d'espace de noms existe tableau 1 
    2. quel est l'espace de noms?

       % namespace ensemble configure array -namespace :: tcl :: tableau 
    3. Quelles sont les sous-commandes?

       % namespace ensemble configure array -souscommands % namespace ensemble configure array -map more :: tcl :: array :: more donesearch :: tcl :: array :: donesearch existe :: tcl :: array :: existe get :: tcl :: array :: get names :: tcl :: array :: names nextelement :: tcl :: array :: nextelement set :: tcl :: array :: set size :: tcl :: array :: size startsearch :: tcl :: array :: startsearch statistiques :: tcl :: array :: statistics unset :: tcl :: array :: unset 

OK, tout va bien si var. Ajoutons ce proc array_values dans l'espace de noms

% namespace eval ::tcl::array {
    proc values {arrayName} {
        upvar 1 $arrayName ary
        set values {}
        foreach {name value} [array get ary] {lappend values $value}
        return $values
    }
}
% array set a {foo bar baz qux}
% puts [::tcl::array::values a]
can't set "values": variable is array

D'où vient cette erreur? J'ai essayé de renommer la variable "values" dans le proc en d'autres noms, mais elle émet toujours l'erreur "variable is array".


Une note: je peux ajouter le premier proc à l'ensemble:

% namespace ensemble config array -map [list values ::array_values {*}[namespace ensemble config array -map]]
% array values a
bar qux

Mais quel est le problème avec mon ::tcl::array::values proc?

tcl
1
glenn jackman 19 nov. 2018 à 20:40

3 réponses

Meilleure réponse

Votre commande set values {} s'exécute dans l'espace de noms :: tcl :: array, donc elle exécute la commande :: tcl :: array :: set. En d'autres termes, il fait l'équivalent de array set values {}. Donc, cela fait des valeurs un tableau sans membres. Ensuite, la commande lappend values $value échoue car values est un tableau à ce stade.

La solution devrait être d'utiliser ::set values {}

Ou vous pouvez complètement éviter le problème en utilisant:

proc array_values {arrayName} {
    upvar 1 $arrayName ary
    return [lmap {name value} [get ary] {string cat $value}]
}
3
Schelte Bron 19 nov. 2018 à 19:48

Pour les curieux, voici ce que j'ai obtenu:

$ HOME / tcl / lib / monkeypatches / monkeypatches.tcl

# a set of useful additions to built-in ensembles

package provide monkeypatches 0.1

namespace eval ::monkeypatches {
    # https://wiki.tcl-lang.org/page/wrapping+commands
    proc append_subcommand {cmd subcmd procname} {
        set map [namespace ensemble configure $cmd -map]
        dict set map $subcmd [namespace which $procname]
        namespace ensemble configure $cmd -map $map
    }


    # array foreach
    # to be subsumed by https://core.tcl.tk/tips/doc/trunk/tip/421.md
    #
    # example:
    #   array set A {foo bar baz qux}
    #   array foreach {key val} A {puts "name=$key, value=$val"}
    #
    proc array_foreach {vars arrayName body} {
        if {[llength $vars] != 2} {
            error {array foreach: "vars" must be a 2 element list}
        }
        lassign $vars keyVar valueVar

        # Using the complicated `upvar 1 $arrayName $arrayName` so that any
        # error messages propagate up with the user's array name
        upvar 1 $arrayName $arrayName \
                $keyVar    key \
                $valueVar  value

        set sid [array startsearch $arrayName]
        # If the array is modified while a search is ongoing, the searchID will
        # be invalidated: wrap the commands that use $sid in a try block.
        try {
            while {[array anymore $arrayName $sid]} {
                set key [array nextelement $arrayName $sid]
                set value [set "${arrayName}($key)"]
                uplevel 1 $body
            }
            array donesearch $arrayName $sid
        } trap {TCL LOOKUP ARRAYSEARCH} {"" e} {
            return -options $e "detected attempt to modify the array while iterating"
        }
        return
    }
    append_subcommand ::array foreach array_foreach


    # array values arrayName
    # https://stackoverflow.com/q/53379995/7552
    #
    # example:
    #   array set x {foo bar baz qux}
    #   array get x             ;# => foo bar baz qux
    #   array names x           ;# => foo baz
    #   array values x          ;# => bar qux
    #
    proc array_values {arrayName} {
        upvar 1 $arrayName ary
        set values [list]
        array foreach {name value} ary {lappend values $value}
        return $values
    }
    append_subcommand ::array values array_values


    # info formalargs procName
    # https://core.tcl.tk/tips/doc/trunk/tip/65.md
    #
    # example:
    #   proc test {one {two 2} {three {3 4 5}} args} {return}
    #   info args test          ;# => one two three args
    #   info formalargs test    ;# => one {two 2} {three {3 4 5}} args
    #
    proc info_formalargs {procname} {
        # [info args] throws an error if $procname is not a procedure.
        return [lmap arg [info args $procname] {
            set has_d [info default $procname $arg value]
            if {$has_d} then {list $arg $value} else {set arg}
        }]
    }
    append_subcommand ::info formalargs info_formalargs
}

Avec son pkgIndex.tcl associé

Et $ HOME / .tclshrc

set lib_dir [file join $env(HOME) tcl lib]
if {$lib_dir ni $auto_path} {lappend auto_path $lib_dir}
unset lib_dir
package require monkeypatches
0
glenn jackman 28 juin 2019 à 14:41

Je voudrais ajouter que, étant donné que la présence de commandes d'ensemble éventuellement conflictuelles est une cible mouvante, la correction d'un ensemble est susceptible de se produire de partout, j'ai vu des développeurs principaux conserver des commandes d'ensemble supplémentaires en dehors de l'espace de noms ::tcl::array::*:

proc arrayValues {arrayName} {
    upvar 1 $arrayName ary
    set values {}
    foreach {name value} [array get ary] {lappend values $value}
    return $values
}

# implant "arrayValues" into [array] ensemble as "values"
namespace ensemble configure ::array -map \
    [dict replace [namespace ensemble configure ::array -map] \
     values [namespace which arrayValues]]

De cette façon, vous n'avez pas à vous soucier des conflits de résolution involontaires (quoi que cela signifie dans Tcl, pour commencer).

1
mrcalvin 19 nov. 2018 à 21:27