首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >标准过程的重新定义能改变其他标准过程的行为吗?

标准过程的重新定义能改变其他标准过程的行为吗?
EN

Stack Overflow用户
提问于 2022-06-13 01:27:24
回答 3查看 111关注 0票数 1

假设我们使用以下之一重新定义标准过程car

代码语言:javascript
复制
(define (car)
  (display "vroom vroom!"))

代码语言:javascript
复制
(set! car
  (lambda ()
    (display "vroom vroom!")))

在符合标准的Scheme实现中,以这种方式重新定义car是否会影响其他使用car定义的标准过程的行为?

例如,方案实现是否允许在内部将cadr定义为(define (cadr x) (car (cdr x))),这样用户在REPL上对car的重新定义就会改变REPL中cadr的行为吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2022-06-14 01:45:09

不同的修订有不同的答案。

R5RS及更早版本:

您不允许define现有绑定。因此,您的第一次尝试是不正确的方案,根据报告和运行的结果,它可以是任何东西。例如:分段错误是可以的,发出错误信号是可以的,忽略它是可以的。

您只允许set顶级绑定,但它需要向后兼容原始绑定。例如:不允许以您的方式定义car,但允许这样做:

代码语言:javascript
复制
(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的方式。这使得它不需要向后兼容。

通常,您可以创建自己的列表库,并在代码中不使用标准,而是使用新版本,它将使用该标准。该链接相当静态(您可以导入显式链接),因此绑定永远不会与标准库绑定混淆。

票数 1
EN

Stack Overflow用户

发布于 2022-06-13 09:50:20

不是的。第一个示例((define (car) ...)将"car“绑定到一个新位置,从而隐藏标准绑定。在"car“((define (cadr x) ...)的实现中,"car”有其原始的绑定。

具有"car“标准绑定的(set! car ...必须发出错误信号:

代码语言:javascript
复制
% 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)

票数 1
EN

Stack Overflow用户

发布于 2022-06-13 14:43:44

是。可以更改整个系统的行为。但不是你做的那样。

代码语言:javascript
复制
% 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环境指向的相同环境)。

代码语言:javascript
复制
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那样使用技巧,并以非常复杂的方式操作环境(创建模块的库)。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72596987

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档