异常:主体中函数的多个定义

Exception: multiple definitions for function in body

我有一个简单的程序:

(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
Exception: multiple definitions for abs in body (top-level-program #<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 (...))> near line 1, char 1 of /home/scheme/main.ss

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

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

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

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

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

(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. Library form:

... no identifier can be imported multiple times, defined multiple times, or both defined and imported.

规范继续:

The expressions of variable definitions are evaluated from left to right, as if in an implicit letrec*....

但是,通过 11.4.6. Binding constructs,以 letrec* 形式:

(letrec* <bindings> <body>)‌‌ syntax

Syntax: <Bindings> must have the form

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

where each <init> is an expression, and <body> is as described in section 11.3. Any variable must not appear more than once in the <variable>s.

所以一个标识符不能多次导入,导入和定义,或者在库或顶级程序中定义和重新定义。 OP 代码违反了这一点,既导入了标识符的定义,又在顶级程序中重新定义了该标识符。