1

我有一个简单的程序:

(import (rnrs))
(define (abs x)
    (cond ((> x 0) x)
           ((= x 0) 0)
           ((< x 0) (- x))
           ))
(define (square x)
    (* x x))
(define (sum-sq x y)
    (+ (square x) (square y)))
(display
    (sum-sq (read) 3))

当我运行它时,我有一个例外;我做错了什么?

/home# scheme-script /home/scheme/main.ss
异常:body 中 abs 的多个定义(顶级程序 #<annotation /home/scheme/main.ss[0:15](import (... ))> #<annotation /home/scheme/main.ss)[17:122] (define (...) (...))> #<annotation /home/scheme/main.ss[124:156] (define (...) (...))> #<annotation /home/scheme/main.ss[158:210] (define (...) (...))> #<annotation /home/ scheme/main.ss[212:244] (display (...))> /home/scheme/main.ss 的第 1 行,字符 1 附近

4

2 回答 2

1

错误消息非常明确:您正在重新定义abs,这是一个内置过程。根据使用的 Scheme 解释器,这可能是个问题——特别是,您不能在 Chez Scheme 中重新定义过程。只需 delete abs,它已经由语言提供。

于 2021-04-03T08:53:04.580 回答
0

在 R6RS Scheme 的顶层程序中重新定义变量是非法的。scheme-scriptOP 已使用;执行脚本 在 Chez Schemescheme-script中相当于scheme --program,它将文件视为顶级程序。当您尝试定义时,可以在 REPL 中重新定义内容,除非您将该代码包装在一个top-level-program表单中。

scheme --script将文件视为 shell 脚本,而scheme-script(ie, scheme --program) 将其视为顶级程序。为了证明 OP 的问题是文件被视为顶级程序的结果,请使用 运行它scheme --script,然后将发布的代码包装在一个(top-level-program ...)表单中并尝试再次运行scheme --script。第一次尝试将成功执行,第二次将再次引发异常。

OP 问题的一种解决方案是使用scheme --script代替scheme-script(或scheme --program)。当然,可以简单地使用内置abs程序,或者将新程序重命名为,例如my-abs.

但是,有时您确实确实想使用之前已被您需要导入的某个库声明的标识符。对于那种情况有except。这是 OP 代码的一个版本,except格式import如下:

(import (except (rnrs) abs))

(define (abs x)
  (if (< x 0) (- x) x))

(define (my-abs x)
  (<))
(define (square x) (* x x))

(define (sum-sq x y)
  (+ (square x) (square y)))

(display "Enter a number: ")
(let ((x (read)))
  (display x) (display "^2 + 3^2 = ") (display (sum-sq x 3)) (newline))

(display "Enter a number: ")
(let ((x (read)))
  (display "|") (display x) (display "| = ") (display (abs x)) (newline))

;;; Easier with Chez Scheme `format`, which can be made available by
;;; changing the `import` form at the top to:
;;;
;;;  (import (except (chezscheme) abs))

;; (display "Enter a number: ")
;; (let ((x (read)))
;;   (format #t "~A^2 + 3^2 = ~A~%" x (sum-sq x 3)))

;; (display "Enter a number: ")
;; (let ((x (read)))
;;   (format #t "|~A| = ~A~%" x (abs x)))

该程序导入从rnrs库中导出的所有标识符,除了abs. 然后顶层程序可以自由定义abs标识符。

$ scheme-script top-level.ss 
Enter a number: 4
4^2 + 3^2 = 25
Enter a number: -42
|-42| = 42

标准怎么说

顶级程序就像一个库,只是它不能包含export表单。从R6RS 7.1。图书馆形式

...没有标识符可以多次导入,多次定义,或者既定义又导入。

规范继续:

变量定义的表达式是从左到右计算的,就好像在一个隐式的letrec* ....

但是,到11.4.6。绑定结构letrec*形式为:

( letrec* <bindings> <body>)‌‌语法

语法:<Bindings> 必须具有格式

((<variable1> <init1>) ...),

其中每个 <init> 都是一个表达式,而 <body> 如第 11.3 节所述。任何变量都不得在 <variable>s 中出现多次

所以一个标识符不能被多次导入,无论是导入和定义,还是在库或顶级程序中定义和重新定义。OP 代码通过导入标识符的定义和在顶级程序中重新定义该标识符来违反这一点。

于 2021-04-03T11:15:32.907 回答