假设我们使用以下之一重新定义标准过程car:
(define (car)
(display "vroom vroom!"))或
(set! car
(lambda ()
(display "vroom vroom!")))在符合标准的Scheme实现中,以这种方式重新定义car是否会影响其他使用car定义的标准过程的行为?
例如,方案实现是否允许在内部将cadr定义为(define (cadr x) (car (cdr x))),这样用户在REPL上对car的重新定义就会改变REPL中cadr的行为吗?
发布于 2022-06-14 01:45:09
不同的修订有不同的答案。
R5RS及更早版本:
您不允许define现有绑定。因此,您的第一次尝试是不正确的方案,根据报告和运行的结果,它可以是任何东西。例如:分段错误是可以的,发出错误信号是可以的,忽略它是可以的。
您只允许set顶级绑定,但它需要向后兼容原始绑定。例如:不允许以您的方式定义car,但允许这样做:
(let ((orig-car car))
(set! car
(lambda (o)
(if (vector? o)
(vector-ref o 0)
(orig-car o)))))
(car '(1 2 3)) ; ==> 1
(car (vector 1 2 3)) ; ==> 1该实现可以自由地进行不断折叠和编译。因此,在现实中,它可能永远不会使用您的新版本,但是报告并没有阻止实现cadr的方式,而是使用car的新实现。只要您保持标准并使其兼容,结果是可预测的。
R6RS及更高版本:
在R6RS中,我们有库,我们可以重命名和退出,并且重写它不会在其他库中泄漏。因此,您可以使用set! car,但是它不会泄漏,这样cadr就可以开始使用它,即使这是实现实现cadr的方式。这使得它不需要向后兼容。
通常,您可以创建自己的列表库,并在代码中不使用标准,而是使用新版本,它将使用该标准。该链接相当静态(您可以导入显式链接),因此绑定永远不会与标准库绑定混淆。
发布于 2022-06-13 09:50:20
不是的。第一个示例((define (car) ...)将"car“绑定到一个新位置,从而隐藏标准绑定。在"car“((define (cadr x) ...)的实现中,"car”有其原始的绑定。
具有"car“标准绑定的(set! car ...必须发出错误信号:
% scheme
Chez Scheme Version 9.5.7.6
Copyright 1984-2021 Cisco Systems, Inc.
> (display car)
#<procedure car>
> (set! car cdr)
Exception: attempt to assign immutable variable car在标准报告中搜索“不可变”,例如r6rs:“所有显式导出的变量在导出和导入库中都是不可变的。”(因此基库不能导出可变的car)
发布于 2022-06-13 14:43:44
是。可以更改整个系统的行为。但不是你做的那样。
% mit-scheme
MIT/GNU Scheme running under GNU/Linux
....
1 ]=> (pe)
;Value: (user)
1 ]=> (ge system-global-environment)
;Package: ()
;Value: #f
1 ]=> (pe)
;Value: ()
1 ]=> (define + 1)
;Value: +移回用户环境(这是初始REPL环境指向的相同环境)。
1 ]=> (ge user-initial-environment)
;Package: (user)
;Value: #[environment 12]
1 ]=> (pe)
;Value: (user)
1 ]=> (+ 1 2) ; no more
;The object 1 is not applicable.
;To continue, call RESTART with an option number:
; (RESTART 2) => Specify a procedure to use in its place.
; (RESTART 1) => Return to read-eval-print level 1.
2 error> (assoc 'car
(environment-bindings
system-global-environment))
;Value: (car #[compiled-procedure 1351
("list" #x1) #x1c #xe0f67c])因此,您可以用我更改+的方式更改汽车(在+中定义的)。
但是,当然,这是个坏主意,你的系统不能再工作了。
有一些内部过程正在使用car或+进行编译。那些被编译的人不会引用system-global-environment并继续工作。那些没有编译的将会崩溃。现在,取决于您的系统是如何引导的,看看哪些系统引用了系统env。如果在没有编译的情况下在系统中加载了一些库过程,则肯定会崩溃。
请注意,当我说“编译”时,它通常意味着除了调用解释/eval函数之外的其他解释方式,它意味着绕过标准解释器。如果使用其虚拟机引用系统环境的字节编译器,则作为字节码编译的过程将崩溃。如果代码被完全编译成x86或mips,它有自己的引用到用汇编程序编写的标准库过程,并且不会引用全局环境。在一个计划系统中,一切都是可能的。
如果您在用户环境中重新定义过程(正如您所做的那样),系统将继续工作,因为标准库过程指向系统环境,而没有系统过程将看到用户环境。但是,在用户环境中加载的所有过程都将使用您自己的汽车定义。在这种情况下,重新定义事物是可以的。
注意,命令(load "xxx")有一个名为environment的可选参数,您可以使用这个选项通过加载到哪个环境来告诉load要更改什么环境。默认情况下,在批处理模式和REPL模式下,load将写入父为system的用户环境。但是,您可以像库load-option那样使用技巧,并以非常复杂的方式操作环境(创建模块的库)。
https://stackoverflow.com/questions/72596987
复制相似问题