List 学习方框图和指针图的资源
我目前正在努力理解方框图和指针图。如何构建它们以及如何解释它们 提供的说明如下所示。 然而,我仍然没有“明白” 我知道列表是对的组合,一对的cdr可能指向另一对。我也知道cdr指向的一对可能是另一个列表。我只是不明白如何在一张图表中把它画出来 以下是一个问题示例,供参考:List 学习方框图和指针图的资源,list,linked-list,scheme,lisp,nested-lists,List,Linked List,Scheme,Lisp,Nested Lists,我目前正在努力理解方框图和指针图。如何构建它们以及如何解释它们 提供的说明如下所示。 然而,我仍然没有“明白” 我知道列表是对的组合,一对的cdr可能指向另一对。我也知道cdr指向的一对可能是另一个列表。我只是不明白如何在一张图表中把它画出来 以下是一个问题示例,供参考: (define cal (list (append (list (cons (list 1 2) (cons 3 '()))) (list (cons 4 (cons 5 '()))))
(define cal
(list (append (list (cons (list 1 2) (cons 3 '())))
(list (cons 4 (cons 5 '()))))
6
7))
给定一个类似于上面的代码,我假设画一个方框和指针图,然后能够说出要得到列表中的任何给定数字需要什么样的car和cdr组合
同样,以下是我本应能够得出的图表,以供参考:
重申一下,我要找的是一段视频或一篇文章,可以更清楚地解释方框图和指针图的构建
提前感谢所有愿意为我指出正确方向的人。试着从里到外开始,就像口译员会做的那样。画一个图表,比如说,
(cons 3'())
-很简单,对吧?现在,有什么东西可以指向它吗?是的,它是(cons(列表12)(cons 3’())
的cdr。因此,当您为这个更大的表达式绘制图表时,请确保它的cdr指向您绘制的第一个子图表。要完成这个更大的表达式的绘制,您还需要为(列表12)
绘制一个图表-与开始时一样简单
从那里开始向外工作。
append
操作是最棘手的部分,但是您链接的说明解释了append
的工作原理。忘记列表。没有列表。只有一对
(define cal
(list (append (list (cons (list 1 2) (cons 3 '())))
(list (cons 4 (cons 5 '()))))
6
7))
=
(define NIL '())
(define A (cons 1 (cons 2 NIL))) ; (list 1 2)
(define B (cons 3 NIL)) ; (cons 3 '())
(define C (cons 5 NIL)) ; (cons 5 '())
(define cal
(list (append (list (cons A B))
(list (cons 4 C)))
6
7))
=
(define NIL '())
(define A (cons 1 (cons 2 NIL)))
(define B (cons 3 NIL))
(define C (cons 5 NIL))
(define D (cons A B))
(define E (cons 4 C))
(define cal
(list (append (list D)
(list E))
6
7))
=
(define NIL '())
(define A (cons 1 (cons 2 NIL)))
(define B (cons 3 NIL))
(define C (cons 5 NIL))
(define D (cons A B))
(define E (cons 4 C))
(define F (list D E)) ; (append (list D) (list E))
(define cal
(list F
6
7))
=
(define NIL '())
(define A (cons 1 (cons 2 NIL)))
(define B (cons 3 NIL))
(define C (cons 5 NIL))
(define D (cons A B))
(define E (cons 4 C))
(define F (cons D (cons E NIL))) ; (list D E)
(define cal
(cons F
(cons 6
(cons 7 NIL))))
每个cons
都是一个框。每个名称都是一个指针
仅此而已。此答案使用Common Lisp作为示例,但在模式上并没有根本的不同。另外请注意,如果您想了解打印图表的实际实现方式,可以使用Common Lisp(例如CCL或SBCL程序)
首先必须清楚要打印的结果,当源包含cons/list/append操作时,这可能会很困难。此外,由于源代码也是cons单元树,因此必须注意不要将源代码与计算后获得的值混合。
因此,这一切都是从正确评估表单开始的
评估cal
让我们首先计算表达式。下面,我还提到直接从输入表达式绘制框,但在我看来,这有助于详细说明中间步骤。
递归计算所有表达式后,Scheme和Common Lisp中的结果相同:
((((1 2) 3) (4 5)) 6 7)
以下是如何使用公共Lisp要求系统跟踪计算。首先,要知道您无法跟踪标准函数,如list
等。因此,让我们使用简单的包装将它们隐藏在自定义包中:
(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))
然后,在REPL中,转到该软件包:
CL-USER> (in-package :so)
#<PACKAGE "SO">
现在,您可以直接输入cal
的值,但这次使用的符号是我们要求跟踪的符号
SO> (list (append (list (cons (list 1 2) (cons 3 '())))
(list (cons 4 (cons 5 '()))))
6
7)
然后,环境对表单求值,并打印如何调用每个函数以及它返回的结果
0: (SO::LIST 1 2)
0: LIST returned (1 2)
0: (SO::CONS 3 NIL)
0: CONS returned (3)
0: (SO::CONS (1 2) (3))
0: CONS returned ((1 2) 3)
0: (SO::LIST ((1 2) 3))
0: LIST returned (((1 2) 3))
0: (SO::CONS 5 NIL)
0: CONS returned (5)
0: (SO::CONS 4 (5))
0: CONS returned (4 5)
0: (SO::LIST (4 5))
0: LIST returned ((4 5))
0: (SO::APPEND (((1 2) 3)) ((4 5)))
0: APPEND returned (((1 2) 3) (4 5))
0: (SO::LIST (((1 2) 3) (4 5)) 6 7)
0: LIST returned ((((1 2) 3) (4 5)) 6 7)
((((1 2) 3) (4 5)) 6 7)
可视化为cons单元格
将列表视为cons单元格链可能会有所帮助,即将(a b c)
转换为(a.(b.(c.nil))
。让我们定义一个辅助函数:
(defun consprint (x)
(if (consp x)
(format nil
"(~a . ~a)"
(consprint (car x))
(consprint (cdr x)))
(prin1-to-string x)))
结果如下:
SO> (consprint '((((1 2) 3) (4 5)) 6 7))
"((((1 . (2 . NIL)) . (3 . NIL)) . ((4 . (5 . NIL)) . NIL)) . (6 . (7 . NIL)))"
Draw:一种术语重写方法
使用递归的、自底向上的方法来绘制它
定义。:这里我将叶子定义为一个cons细胞,它的CAR和CDR插槽中都有原子:例如(0.NIL)
和(X.Y)
都是叶子,但不是((0.1).2)
。请注意,这包括不正确的列表,这是我用符号替换子项时用来解释绘图方法的依据
((((1 . (2 . NIL)) . (3 . NIL)) . ((4 . (5 . NIL)) . NIL)) . (6 . (7 . NIL)))
^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^
上面我在所有的叶子下面画了线:你可以很容易地画出那些盒子,并用符号(A,B,…)给它们贴上标签
在下面,我用其关联框的名称替换原始单元格,并再次在新叶下划线:
((((1 . A) . B) . ((4 . C) . NIL)) . (6 . D))
^^^^^^^ ^^^^^^^ ^^^^^^^
然后,当有一个符号表示一个方框时,向该方框画一个箭头。例如,您定义了一个名为E的框,它对应于(1.a)
,因此您可以绘制[1/x]
,并将x
连接到框a
您获得:
(((E . B) . (F . NIL)) . G)
现在,考虑<代码>(E.B)< /代码>:它的车是一个符号,所以你需要绘制的框没有值,但是从车槽中指向箭头的输出箭头指向“<代码> e>代码>单元(就像它的CDR点到<代码> B<代码>)。 重复此过程直到终止。其余部分是可视化布局,通常是将只是原子链接列表的框水平布局
直接从表达式中提取 假设原始练习希望您从原始表达式中直接绘制方框。也可以如上所述,从叶表达式向上操作,并用表示现有框的符号替换它们的值- cons直接映射到长方体
- 列表只是cons的一个重复应用,您可以通过绘制尽可能多的框来快速完成
- append实际上会复制参数中除最后一个列表之外的所有列表,但在绘制时可以“修改”现有框。对于每个现有的框,一直跟随CDR,直到到达CDR中没有箭头的框,并将该框链接到参数中的下一个框,从而将不同的框链接在一起
还可以绘制append的实际纯功能版本,以了解结构共享和垃圾收集是如何工作的。[注意,这个答案并不鼓励您作弊:如果您正在学习一门要求您能够绘制box&pointer di的课程
(((E . B) . (F . NIL)) . G)
raco pkg install --auto sdraw
(define cal
(list (append (list (cons (list 1 2) (cons 3 '())))
(list (cons 4 (cons 5 '()))))
6
7))
(list (cons (list 1 2) (cons 3 '())))