2 votes

Ressources pour apprendre les diagrammes Box and Pointer

Je suis actuellement en train de suivre le cours CS3L de l'été 2011 de Berkely et j'ai du mal à comprendre les diagrammes de boîtes et de pointeurs. Comment les construire et comment les interpréter.

Les instructions fournies sont ici. Cependant, je ne "comprends toujours pas".

Je comprends que les listes sont des combinaisons de paires et que le cdr d'une paire peut pointer vers une autre paire. Je comprends également que la paire vers laquelle pointe le cdr peut être une autre liste. Je ne comprends juste pas comment tout dessiner dans un diagramme.

Pour référence, voici un exemple de problème qui m'est posé :

(define cal 
  (list (append (list (cons (list 1 2) (cons 3 '())))
                (list (cons 4 (cons 5 '())))) 
        6 
        7))

Étant donné un code comme celui ci-dessus, je suis censé dessiner le diagramme de boîtes et de pointeurs, puis être capable de dire quelle combinaison de car et cdr est nécessaire pour obtenir n'importe quel nombre donné dans la liste.

Encore une fois, pour référence, voici le diagramme que j'aurais dû être capable de réaliser :

enter image description here

Pour réitérer, ce que je cherche est une vidéo ou un article qui pourrait expliquer la construction des diagrammes de boîtes et de pointeurs de manière plus claire.

Je vous remercie d'avance à toute personne prête à me guider dans la bonne direction.

5voto

tfb Points 2159

[Notez que cette réponse ne vous encourage pas à tricher : si vous suivez un cours qui vous demande de savoir dessiner des diagrammes en boîtes et en pointeurs, vous devriez être capable de le faire, et non pas avoir un programme le faire pour vous. Mais le programme peut vous aider à apprendre.]

Une bonne approche pour apprendre comment fonctionnent les diagrammes en boîtes et en pointeurs est de pouvoir parler à un programme qui sait comment les dessiner. À l'époque dorée du Lisp, nous avions d'excellentes interfaces conversationnelles sur nos machines Lisp qui permettaient d'entremêler graphiques et texte, ainsi que de beaux programmes de dessin de graphiques à partir desquels des outils pouvaient facilement être construits pour faire cela. En utilisant de tels outils, vous pouviez construire diverses structures à partir de paires et faire dessiner des diagrammes par le programme, et ainsi vous aviez une bonne compréhension du fonctionnement des structures.

Eh bien ... il se trouve que l'âge d'or du Lisp est maintenant. Si vous utilisez Racket (et vous pouvez utiliser Racket si vous ne l'utilisez pas déjà), il existe un package très merveilleux appelé sdraw qui fait ça. Il n'est pas inclus dans la distribution de Racket, mais pour l'installer vous pouvez soit utiliser le gestionnaire de packages de DrRacket, soit simplement faire

raco pkg install --auto sdraw

ce qui l'installera. Maintenant vous pouvez (dans une fenêtre de DrRacket, cela ne fonctionnera pas dans une session de terminal) simplement parler à REPL de Racket et lui demander de dessiner des arbres de paires pour vous :

sdraw session

En interagissant simplement avec le langage et en le laissant dessiner des choses pour vous, vous pouvez avoir une très bonne idée de comment les différentes structures se tiennent ensemble.

2voto

coredump Points 660

Cette réponse utilise Common Lisp pour ses exemples, mais ce n'est pas fondamentalement différent en Scheme. Notez également que vous pouvez jouer avec sdraw.lisp en Common Lisp (par exemple avec des programmes CCL ou SBCL) si vous voulez voir comment l'impression du diagramme peut réellement être implémentée.

Vous devez d'abord être clair sur le résultat que vous voulez imprimer, ce qui peut être difficile lorsque la source contient des opérations de cons/list/append. De plus, étant donné que le code source est également un arbre de cellules cons, vous devez faire attention à ne pas mélanger la source avec la valeur obtenue après son évaluation. Tout commence donc par évaluer correctement la forme.

Évaluer cal

Commençons par évaluer l'expression. Ensuite, je mentionne également comment dessiner directement les boîtes à partir de l'expression d'entrée, mais à mon avis, il est utile de détailler cette étape intermédiaire. Le résultat est le même en Scheme et en Common Lisp, après avoir évalué récursivement toutes les expressions :

((((1 2) 3) (4 5)) 6 7)

Voici comment vous pourriez demander au système de tracer le calcul, en Common Lisp. Tout d'abord, sachez que vous ne pouvez pas tracer les fonctions standard comme list, etc. Alors, masquons-les dans un package personnalisé avec des wrappers simples :

(defpackage :so
  (:use :cl)
  (:shadow #:list #:cons #:append))

(in-package :so)

(defun list (&rest args) (apply #'cl:list args))
(defun cons (&rest args) (apply #'cl:cons args))
(defun append (&rest args) (apply #'cl:append args))

Ensuite, dans le REPL, allez dans ce package :

CL-USER> (in-package :so)

#

Demandez de tracer ces fonctions :

SO> (trace list append cons) ;; celles qui ont été masquées
(LIST CONS APPEND)

Maintenant, vous pouvez entrer la valeur de cal directement, mais cette fois les symboles utilisés sont ceux que nous avons demandé de tracer.

SO> (list (append (list (cons (list 1 2) (cons 3 '())))
                (list (cons 4 (cons 5 '())))) 
        6 
        7)

L'environnement évalue ensuite la forme et affiche comment chaque fonction est appelée et quels sont les résultats qu'elle renvoie.

  0: (SO::LIST 1 2)
  0: LIST a renvoyé (1 2)
  0: (SO::CONS 3 NIL)
  0: CONS a renvoyé (3)
  0: (SO::CONS (1 2) (3))
  0: CONS a renvoyé ((1 2) 3)
  0: (SO::LIST ((1 2) 3))
  0: LIST a renvoyé (((1 2) 3))
  0: (SO::CONS 5 NIL)
  0: CONS a renvoyé (5)
  0: (SO::CONS 4 (5))
  0: CONS a renvoyé (4 5)
  0: (SO::LIST (4 5))
  0: LIST a renvoyé ((4 5))
  0: (SO::APPEND (((1 2) 3)) ((4 5)))
  0: APPEND a renvoyé (((1 2) 3) (4 5))
  0: (SO::LIST (((1 2) 3) (4 5)) 6 7)
  0: LIST a renvoyé ((((1 2) 3) (4 5)) 6 7)

((((1 2) 3) (4 5)) 6 7)

Visualiser en tant que cellules cons

Il peut être utile de voir les listes comme des chaînes de cellules cons, c'est-à-dire transformer (a b c) en (a . (b . (c . nil))). Définissons une fonction d'aide :

(defun consprint (x)
  (if (consp x)
    (format nil
            "(~a . ~a)" 
            (consprint (car x))
            (consprint (cdr x)))
    (prin1-to-string x)))

Voici le résultat :

SO> (consprint '((((1 2) 3) (4 5)) 6 7))
"((((1 . (2 . NIL)) . (3 . NIL)) . ((4 . (5 . NIL)) . NIL)) . (6 . (7 . NIL)))"

Dessiner : une approche de réécriture de terme

Utilisez une approche récursive ascendante pour le dessiner.

Définition. : Ici je définis une feuille comme une cellule cons qui a des atomes à la fois dans ses slots CAR et CDR : par exemple (0 . NIL) et (X . Y) sont tous deux des feuilles, mais pas ((0 . 1) . 2). Notez que cela inclut les listes incorrectes, quelque chose sur lequel je m'appuie pour expliquer la méthode de dessin lorsque je remplace les sous-termes par des symboles.

((((1 . (2 . NIL)) . (3 . NIL)) . ((4 . (5 . NIL)) . NIL)) . (6 . (7 . NIL)))
        ^^^^^^^^^    ^^^^^^^^^          ^^^^^^^^^                 ^^^^^^^^^

Au-dessus j'ai souligné toutes les feuilles : vous pouvez facilement dessiner ces boîtes, et les étiqueter avec des symboles (A, B, ...).

Ici dessous je remplace les cellules d'origine par les noms de leurs boîtes associées, et je souligne à nouveau les nouvelles feuilles :

((((1 . A) . B) . ((4 . C) . NIL)) . (6 . D))
   ^^^^^^^         ^^^^^^^           ^^^^^^^

Ensuite, lorsqu'il y a un symbole représentant une boîte, dessinez une flèche vers cette boîte. Par exemple, vous définissez une boîte nommée E qui correspond à (1 . A), donc vous dessinez [1/x] et connectez le x à la boîte A.

Vous obtenez :

(((E . B) . (F . NIL)) . G)

Maintenant, considérez (E . B) : son car est un symbole, donc la boîte que vous devez dessiner n'a pas de valeur, mais une flèche sortante du slot CAR qui pointe vers la cellule E (tout comme son CDR pointe vers B). Répétez ce processus jusqu'à la fin. Le reste est la mise en page visuelle, où typiquement les boîtes qui sont juste des listes liées d'atomes sont disposées horizontalement.

Dessin direct à partir d'expressions

Possiblement l'exercice original vous demande de dessiner des boîtes directement à partir de l'expression d'origine. La même chose peut être faite comme ci-dessus, en remontant à partir des expressions de feuilles et en remplaçant leurs valeurs par des symboles dénotant des boîtes existantes.

  • Un cons est directement mappé sur une boîte.
  • une liste est simplement une application répétée de cons, et vous pouvez aller rapidement en dessinant autant de boîtes qu'il y a d'éléments.
  • append en pratique fait une copie de tout sauf de la dernière liste dans les arguments, mais en dessinant vous pouvez "muter" les boîtes existantes. Pour chaque boîte existante, suivez le CDR jusqu'à ce que vous atteigniez une boîte sans flèche dans cdr, et reliez cette boîte à la suivante des arguments, en chaînant ainsi les différentes boîtes entre elles.

Il peut être intéressant de dessiner également la version réelle et purement fonctionnelle de append pour voir comment le partage de structure et la collecte de déchets fonctionnent.

2voto

David Bowling Points 9830

En partant du code, vous pouvez travailler du niveau supérieur vers le bas. Étant donné :

(define cal 
  (list (append (list (cons (list 1 2) (cons 3 '())))
                (list (cons 4 (cons 5 '())))) 
        6 
        7))

vous pouvez voir que le premier niveau est une liste contenant trois éléments : un objet complexe, un 6 et un 7. Cela peut être représenté par trois cellules cons :

entrer la description de l'image ici

Maintenant, il vous suffit de déterminer à quoi pointe le car de la première cellule cons. En revenant au code, vous verrez que cela pointe vers une liste composée de deux listes, concaténées. Si vous regardez les parenthèses, vous verrez que le premier appel à list ne prend qu'un argument, donc cela créera une liste contenant un seul élément. Lorsque deux listes sont concaténées, le nil qui ferme la première liste est remplacé par un pointeur vers le début de la deuxième liste, donc :

entrer la description de l'image ici

La première liste des deux à concaténer est créée par ce code :

(list (cons (list 1 2) (cons 3 '())))

Ici, la liste (1 2) est consée à l'avant de la liste (3) pour créer une nouvelle liste. Il s'agit d'une liste de deux éléments où le car de la liste pointe vers la liste (1 2) et le cdr de la liste est (3). Nous pouvons donc dessiner les boîtes pour cette couche :

entrer la description de l'image ici

Et comme le car de la dernière couche pointe simplement vers la liste (1 2), nous pouvons la compléter :

entrer la description de l'image ici

Maintenant, le deuxième argument de la fonction append est aussi une liste contenant un seul élément, donc :

entrer la description de l'image ici

Cet unique élément est le résultat de (cons 4 (cons 5 '())), qui est simplement la liste (4 5), donc nous pouvons enfin terminer notre diagramme en pointant vers cette liste depuis le car de la dernière cellule cons :

entrer la description de l'image ici

1voto

amalloy Points 29125

Essayez de commencer de l'intérieur vers l'extérieur, tout comme fera l'interprète. Dessinez le diagramme pour, disons, (cons 3 '()) - assez simple, non? Maintenant, est-ce que quelque chose doit pointer vers cela? Oui, c'est le cdr de (cons (list 1 2) (cons 3 '')). Donc, lorsque vous dessinez le diagramme pour cette expression plus grande, assurez-vous que son cdr pointe vers le premier sous-diagramme que vous avez dessiné. Pour finir de dessiner cette expression plus grande, vous devrez également dessiner un diagramme pour (list 1 2) - tout aussi facile que là où vous avez commencé.

Travaillez votre chemin vers l'extérieur à partir de là - l'opération append est la partie la plus difficile, mais les instructions que vous avez liées expliquent comment append fonctionne.

1voto

Will Ness Points 18581

Oubliez les listes. Il n'y a pas de listes. Il n'y a que des paires.

(définir cal 
  (liste (append (liste (cons (liste 1 2) (cons 3 '())))
                (liste (cons 4 (cons 5 '())))) 
        6 
        7))
=
(définir NIL '())
(définir A (cons 1 (cons 2 NIL)))  ; (liste 1 2)
(définir B (cons 3 NIL))           ; (cons 3 '')
(définir C (cons 5 NIL))           ; (cons 5 '')
(définir cal 
  (liste (append (liste (cons A B))
                (liste (cons 4 C))) 
        6 
        7))
=
(définir NIL '())
(définir A (cons 1 (cons 2 NIL)))  
(définir B (cons 3 NIL))           
(définir C (cons 5 NIL))           
(définir D (cons A B))
(définir E (cons 4 C))
(définir cal 
  (liste (append (liste D)
                (liste E)) 
        6 
        7))
=
(définir NIL '())
(définir A (cons 1 (cons 2 NIL)))  
(définir B (cons 3 NIL))           
(définir C (cons 5 NIL))           
(définir D (cons A B))
(définir E (cons 4 C))
(définir F (liste D E))             ; (append (liste D) (liste E)) 
(définir cal 
  (liste F 
        6 
        7))
=
(définir NIL '())
(définir A (cons 1 (cons 2 NIL)))  
(définir B (cons 3 NIL))           
(définir C (cons 5 NIL))           
(définir D (cons A B))
(définir E (cons 4 C))
(définir F (cons D (cons E NIL)))  ; (liste D E)
(définir cal 
  (cons F 
        (cons 6 
              (cons 7 NIL))))

Chaque cons est une boîte. Chaque nom est un pointeur.

Et c'est tout.

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