我正在尝试根据着名的书籍“#34;计算机程序的结构和解释”来实施计划中的元认知评估员。由Harold Abelson和Gerald Jay Sussman撰写。
http://mitpress.mit.edu/sicp/full-text/sicp/book/node79.html,http://mitpress.mit.edu/sicp/full-text/sicp/book/node80.html
作者建议以这种方式设置环境:
(define (define-variable! var val env)
(let ((frame (first-frame env)))
(define (scan vars vals)
(cond ((null? vars)
(add-binding-to-frame! var val frame))
((eq? var (car vars))
(set-car! vals val))
(else (scan (cdr vars) (cdr vals)))))
(scan (frame-variables frame)
(frame-values frame))))
(define (setup-environment)
(let ((initial-env
(extend-environment (primitive-procedure-names)
(primitive-procedure-objects)
the-empty-environment)))
(define-variable! 'true true initial-env)
(define-variable! 'false false initial-env)
initial-env))
然而,我无法理解为什么
(define myenv (setup-environment))
应该像我们在Scheme中所期望的那样工作,因为,据我所知,Scheme默认情况下将变量按值传递给函数,因此在将“define-variable!”应用于initial-env两次之后,initial-env赢了'每次都要更改,setup-environment函数将在extend-environment返回时返回值。
我的理解错误在哪里,你能告诉我吗?
提前谢谢!
答案 0 :(得分:5)
你的问题可能是一个更具体的 teensy ,但我相信我理解它。
具体来说,您的问题似乎是:
“我对
的行为感到惊讶(define myenv (setup-environment))
(define-variable! 'a 13 myenv)
(lookup myenv 'a)
具体来说,我希望它会失败,因为Scheme是按值调用的。“这是你的问题吗?
如果是这样,那么我想我可以回答。按值调用不表示值无法更改。它只是意味着函数调用涉及将值从调用者传递给被调用者。实际上,几乎所有语言都是按值调用的;这个词被广泛误解了。例如,Java也是一种按值调用的语言。
然后,没有什么可以阻止你改变或“改变”一个值。在此示例中,set-car!
调用会改变它所引用的列表。然后,任何可以“看到”此值的代码都可以看到此更改。
我认为你的根本问题实际上与“价值呼唤”的含义有关,我希望我已经对它有所了解。
答案 1 :(得分:2)
要了解其工作原理,首先您必须了解变量initial-env
将指向环境的第一帧,此引用 never < / em>已修改。环境本身是一个帧列表,每个帧是一对列表,第一帧的car
是变量列表的头部,第一帧的cdr
是价值清单的负责人。
一旦明确,您需要了解程序scan
和add-binding-to-frame!
的工作原理。对于与Scan
同名的变量,var
将在变量列表中查看当前帧内部;如果发现它将替换值列表中的相应值。如果找不到变量,add-binding-to-frame!
将在相应列表的头部添加一个新变量和一个新值,更新帧以指向这个新头。请注意initial-environment
仍然指向第一帧,第一帧仍然指向其变量和值列表的头部(但是使用新的绑定)。
所以你现在看到,即使initial-env
从未改变,它包含的列表也会被修改,因此添加了具有各自值的新变量。我认为理解整个过程的最好方法是抓笔和纸,逐步绘制修改所涉及的利弊细胞的结果。