common-lisp
Regroupement des formulaires
Recherche…
Quand le groupement est-il nécessaire?
Dans certains endroits de Common Lisp, une série de formulaires est évaluée dans l'ordre. Par exemple, dans le corps d'un défunt ou d'un lambda , ou dans le corps d'un pointime . Dans ces cas, l'écriture de plusieurs formulaires dans l'ordre fonctionne comme prévu. Dans quelques endroits, cependant, comme les parties then et else d'une expression if , une seule forme est autorisée. Bien sûr, on peut vouloir évaluer plusieurs expressions à ces endroits. Pour ces situations, une forme implicite de regroupement explicite est nécessaire.
Progn
L'objectif général opérateur spécial progn est utilisé pour l' évaluation de zéro ou de plusieurs formulaires. La valeur du dernier formulaire est renvoyée. Par exemple, dans ce qui suit, (print 'hello) est évalué (et son résultat est ignoré), puis 42 est évalué et son résultat ( 42 ) est renvoyé:
(progn
(print 'hello)
42)
;=> 42
S'il n'y a pas de formulaires dans le progn , alors rien n'est renvoyé:
(progn)
;=> NIL
En plus de regrouper une série de formulaires, progn a également pour caractéristique importante que si le formulaire de progn est un formulaire de niveau supérieur , tous les formulaires qu'il contient sont traités comme des formulaires de niveau supérieur. Cela peut être important lorsque vous écrivez des macros qui se développent dans plusieurs formulaires qui doivent tous être traités comme des formulaires de niveau supérieur.
Progn est également précieux car il renvoie toutes les valeurs de la dernière forme. Par exemple,
(progn
(print 'hello)
(values 1 2 3))
;;=> 1, 2, 3
En revanche, certaines expressions de regroupement renvoient uniquement la valeur primaire du formulaire produisant des résultats.
Progns implicites
Certaines formes utilisent des progiciels implicites pour décrire leur comportement. Par exemple, le quand et à moins que des macros, qui sont essentiellement à sens unique si les formes, décrivent leur comportement en termes d'un progn implicite. Cela signifie qu'une forme comme
(when (foo-p foo)
form1
form2)
est évalué et la condition (foo-p foo) est vraie, alors form1 et form2 sont regroupés comme s'ils étaient contenus dans un progn . L'expansion de la macro quand est essentiellement:
(if (foo-p foo)
(progn
form1
form2)
nil)
Prog1 et Prog2
Souvent, il est utile d'évaluer plusieurs expressions et de renvoyer le résultat de la première ou de la seconde forme plutôt que de la dernière. Ceci est facile à réaliser en utilisant let et, par exemple:
(let ((form1-result form1))
form2
form3
;; ...
form-n-1
form-n
form1-result)
Comme cette forme est courante dans certaines applications, Common Lisp inclut prog1 et prog2 qui sont comme progn , mais renvoient respectivement le résultat des première et seconde formes. Par exemple:
(prog1
42
(print 'hello)
(print 'goodbye))
;; => 42
(prog2
(print 'hello)
42
(print 'goodbye))
;; => 42
Une distinction importante entre prog1 / prog2 et progn est cependant que progn renvoie toutes les valeurs de la dernière forme, alors que prog1 et prog2 ne renvoient que la valeur primaire de la première et de la deuxième forme. Par exemple:
(progn
(print 'hello)
(values 1 2 3))
;;=> 1, 2, 3
(prog1
(values 1 2 3)
(print 'hello))
;;=> 1 ; not 1, 2, 3
Pour les valeurs multiples avec évaluation de style prog1 , utilisez plutôt multiple-value-prog1 . Il n'y a pas de prog2 multi-value similaire, mais il n'est pas difficile à mettre en œuvre si vous en avez besoin.
Bloc
Le bloc opérateur spécial permet le regroupement de plusieurs formes Lisp (comme un progn
implicite) et prend également un nom pour nommer le bloc. Lorsque les formulaires du bloc sont évalués, l'opérateur de retour spécial peut être utilisé pour quitter le bloc. Par exemple:
(block foo
(print 'hello) ; evaluated
(return-from foo)
(print 'goodbye)) ; not evaluated
;;=> NIL
Le retour peut également être fourni avec une valeur de retour:
(block foo
(print 'hello) ; evaluated
(return-from foo 42)
(print 'goodbye)) ; not evaluated
;;=> 42
Les blocs nommés sont utiles lorsqu'un morceau de code a un nom significatif ou lorsque des blocs sont imbriqués. Dans certains contextes, seule la capacité de revenir d’un bloc tôt est importante. Dans ce cas, vous pouvez utiliser nil comme nom de bloc et retourner . Return est comme un retour , sauf que le nom du bloc est toujours nul .
Remarque: les formulaires fermés ne sont pas des formulaires de premier niveau. C'est différent de progn
, où les formulaires ci - joints d'un haut niveau progn
forme sont toujours considérés comme des formes de haut niveau.
Tagbody
Pour beaucoup de contrôle dans les formulaires de groupe, l'opérateur spécial tagbody peut être très utile. Les formes à l'intérieur d'une forme de tagbody sont soit des balises go (qui ne sont que des symboles ou des entiers), soit des formulaires à exécuter. Dans un tagbody , l'opérateur spécial go est utilisé pour transférer l'exécution à un nouvel emplacement. Ce type de programmation peut être considéré comme assez bas car il permet des chemins d'exécution arbitraires. Voici un exemple détaillé de ce à quoi pourrait ressembler une for-loop lorsqu'elle est implémentée en tant que tagbody :
(let (x) ; for (x = 0; x < 5; x++) { print(hello); }
(tagbody
(setq x 0)
prologue
(unless (< x 5)
(go end))
begin
(print (list 'hello x))
epilogue
(incf x)
(go prologue)
end))
Alors que tagbody et go ne sont pas couramment utilisés, peut-être à cause de "GOTO considéré comme dangereux", mais peuvent être utiles lors de l'implémentation de structures de contrôle complexes comme les machines à états. De nombreuses constructions d'itération se développent également dans un tagbody implicite . Par exemple, le corps d'un dotimes est spécifié comme une série d'étiquettes et de formes.
Quelle forme utiliser?
Lorsque vous écrivez des macros qui se développent dans des formulaires pouvant impliquer un regroupement, il est utile de prendre le temps de réfléchir à la structure de regroupement à développer.
Pour les formes de style de définition, par exemple, une macro de définition de widget qui apparaîtra généralement comme une forme de niveau supérieur, et que plusieurs définitions de def , structures de flux , etc., il est préférable d'utiliser un progn , traitées comme des formulaires de niveau supérieur. Pour les formulaires d'itération, un tagbody implicite est plus commun.
Par exemple, le corps de dotimes , dolist , et font chacun développer un tagbody implicite.
Pour les formulaires qui définissent un "morceau" de code nommé, un bloc implicite est souvent utile. Par exemple, alors que le corps d'un defun est à l'intérieur d'un progn implicite, ce progn implicite est dans un bloc partageant le nom de la fonction. Cela signifie que le retour peut être utilisé pour quitter la fonction. Une telle comp