4 votes

Pourquoi avons-nous besoin de procédures imbriquées en tcl

Bonjour, j'ai travaillé avec le script Tcl depuis presque un an et maintenant je comprends presque complètement les bases. Mais aujourd'hui, je suis tombé sur des procédures imbriquées qui sont un peu étranges car je n'en ai pas saisi l'utilité.

Quoi qu'il en soit, j'ai lu sur les procédures imbriquées ici mais je n'ai pas vraiment compris pourquoi nous en avons besoin.

L'article dit que puisque les procédures sont globales dans un espace de noms, pour créer une procédure locale, vous créez des procédures imbriquées.

    proc parent {} {
        proc child {} {
            puts "À l'intérieur de la procédure enfant";
        }
        ...   
    }

Maintenant, une utilisation à laquelle je pense est la suivante

    proc parent {} {
        proc child {intVal} {
            puts "intVal est $intVal";
        }
        puts "[::child 10]";
        ... #quelques traitements
        puts "[::child 20]";
        ... #quelques traitements
        puts "[::child 30]";
        ... #quelques traitements
        puts "[::child 40]";
        ... #quelques traitements
        puts "[::child 50]";
        ... #quelques traitements
    }

Ainsi, la procédure enfant est locale à la procédure parent et ne peut être utilisée que à l'intérieur de la procédure parent. Et aussi, si je comprends bien, elle est utile lorsque vous voulez effectuer le même traitement à plusieurs endroits à l'intérieur de cette procédure parent.

Maintenant, ma confusion est la suivante : est-ce la seule utilisation des procédures imbriquées ou y a-t-il autre chose que je n'ai pas compris ??? J'ai l'impression que les procédures imbriquées semblent juste être une sorte de procédure privée.

Alors s'il vous plaît, éclairez-moi à ce sujet et aidez-moi à comprendre l'utilisation des procédures imbriquées.

7voto

Donal Fellows Points 56559

Tcl n'a pas de procédures imbriquées. Vous pouvez appeler proc à l'intérieur d'une définition de procédure, mais cela crée simplement une procédure normale (l'espace de noms utilisé pour la résolution du nom de la procédure à créer sera l'espace de noms actuel de l'appelant, tel que rapporté par namespace current).

Pourquoi mettre proc à l'intérieur de proc? Eh bien, la vraie raison de le faire est lorsque vous voulez que la commande extérieure agisse comme une usine, pour créer la commande lorsqu'elle est appelée. Parfois, le nom de la commande à créer sera fourni par l'appelant, parfois il sera généré internalement (dans ce dernier cas, il est normal de renvoyer le nom de la commande créée). L'autre cas qui se présente est celui où la commande extérieure est une sorte de proxy pour celle intérieure (réelle), permettant de différer la création de la commande réelle car elle est coûteuse de quelque manière que ce soit; si c'est le cas, la procédure interne aura tendance à être réellement créée avec le même nom que la externe, et elle la remplacera (bien que pas le cadre de pile d'exécution; Tcl est prudent à ce sujet car ce serait sinon fou).

Dans le cas où vous avez vraiment besoin d'une "procédure interne", il est en fait préférable d'utiliser un terme lambda que vous pouvez apply à la place. C'est parce que c'est une valeur réelle qui peut être stockée dans une variable locale et qui disparaîtra automatiquement lorsque la procédure extérieure se termine (ou si vous unset explicitement la variable ou remplacez son contenu). Ce code interne n'aura pas accès aux variables du code extérieur sauf via upvar; si vous voulez renvoyer la valeur tout en liant les variables, vous devriez utiliser un préfixe de commande et inclure un peu de ruse supplémentaire pour lier les variables en tant qu'arguments préfournis :

proc multipliers {de à} {
    set result {}
    for {set i $de} {$i <= $à} {incr i} {
        lappend result [list apply {{i n} {
            return [expr {$i * $n}]
        }} $i]
    }
    return $result
}

set mults [multipliers 1 5]
foreach m $mults {
    puts [{*}$m 2.5]
}
# Affiche (un par ligne) : 2.5  5.0  7.5  10.0  12.5

Utilisation d'une proc interne pour simuler apply

Remarquez que la commande apply peut en fait être simulée par une procédure interne. C'était une technique utilisée dans Tcl 8.4 et avant :

# Omettant la gestion des erreurs...
proc apply {termeLambda args} {
    foreach {arguments corps espaceDeNoms} $termeLambda break
    set cmd ${espaceDeNoms}::___applyLambad
    proc $cmd $arguments $corps
    set result [uplevel 1 [linsert 0 $args $cmd]]; # Syntaxe 8.4 pour l'expansion sécurisée !
    rename $cmd ""
    return $result
}

C'était assez sujet aux erreurs et très lent car cela recompilait à chaque invocation; nous ne faisons plus ça !

1voto

glenn jackman Points 69748

Tcl n'a pas de procs imbriqués. De la page de manuel de proc:

Normalement, le nom n'est pas qualifié (ne comprend pas les noms de tout namespace contenant), et la nouvelle procédure est créée dans le namespace actuel.

(soulignement de ma part)

Pour démontrer:

% namespace eval foo {
    proc parent {} {
        proc child {} {
            puts "l'enfant"
        }
        puts "le parent, qui détient [child]"
    }
}
% foo::parent
l'enfant
le parent, qui détient 
% foo::child
l'enfant

Nous pouvons toujours appeler la proc "interne" directement -- elle n'est pas locale à la procédure englobante.

Une chose que vous avez manquée dans la discussion sur cette page wiki est que pour rendre une proc réellement locale à la procédure englobante, il faut la supprimer à la fin de la procédure englobante:

% namespace eval foo {
    proc parent {} {
        proc child {} {
            puts "l'enfant"
        }
        puts "le parent, qui détient [child]"
        # maintenant, détruisez la proc interne
        rename child ""
    }
}
% foo::parent
l'enfant
le parent, qui détient 
% foo::child
nom de commande invalide "foo::child"

Quant à l'utilisation d'une proc locale, je suis d'accord avec vous sur le fait qu'il est bénéfique d'encapsuler des tâches répétitives qui ne sont utiles que dans la procédure actuelle. Cependant, je ne m'en préoccuperais pas trop : une documentation claire ou des conventions de code fonctionneront tout aussi bien.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X