首页 > DrRacket中的"not a proper list"报错

DrRacket中的"not a proper list"报错

在看SICP,在3.3.3的位置写了一个table。(使用DrRacket)

code_0.scm

#lang scheme
(require rnrs/base-6)
(require rnrs/mutable-pairs-6)

(define nil '())

(define (make-table)
  (list '*table*))

(define (assoc key records)
  (cond ((null? records)
         false)
        ((equal? key (caar records))
         (car records))
        (else
         (assoc key (cdr records)))))

(define (insert! key value table)
  (let ((record (assoc key (cdr table))))
    (if record
        (set-cdr! record value)
        (set-cdr! table
                  (cons (cons key value)
                        (cdr table)))))
  'OK)

(define (lookup key table)
  (let ((record (assoc key (cdr table))))
    (if record
        (cdr record)
        false)))


(define table (make-table))

(insert! 0 0 table)
(insert! 1 1 table)
(insert! 2 2 table)

正常运行。

我想将此代码用成一个库文件供引用,将code_0.scm中的"#lang scheme"删掉,然后写了下面的code_1.scm:

#lang scheme/load

(load "code_0.scm")

(define table-0 (make-table))

(insert! 0 0 table-0)
(insert! 1 0 table-0)
(insert! 2 0 table-0)

报错:

assoc: not a proper list: {{0 . 0}}    

问题出在哪,LIST,DrRacket的load,还是语言版本?


原因就是那个 #lang scheme/load

scheme/load这个和其他模块相比有点特殊,它的identifier绑定机制导致你在递归时候有些意外发生,我举个例子

scheme#lang scheme/load
(define (+ a b)
    (+ 10 ( + a b)))  
(display (+ 4 6)) 

在这个例子中,递归了+这个procedure,如果按照常理来说,这应该是个死循环,但是在#lang scheme/load的模式下,奇迹般的运行正常,输出了结果20

如果你把上面的#lang scheme/load换成 #lang scheme或者 #lang racket,都会无限递归下去,那么为何#lang scheme/load就能正常工作了呢?

这就是内部绑定的问题了,在我上面定义的内部,那个+并不是外面我定义的,而是scheme原本的那个+,这就是#lang scheme/load的绑定的特殊表现了。

所以楼主的问题就很清晰了,你这个和list或者load都无关,纯粹是因为#lang scheme/load,你的那个assoc是scheme本身就存在的procedure,你重新定义一个assoc以后,内部递归的时候assoc还是用的scheme本身的,而它要求参数是个list,而你递归到那一步以后已经是个pairs了。

所以最简单的修改方法就是给你定义的assoc改名,随便改个scheme不存在的名就好了,比如说叫assoc_x.

还有一种方法就是不要用load了(实际上load这个东西太古老了),换用require/provide

你在code_0.scm文件里导出要用的,记住前面得加个#lang scheme

scheme#lang scheme
(provide make-table insert!)

在code_1.scm文件里把load换成require,这就就能完美执行了。

如果你偏要用load,那你可以在code_0.scm把#lang scheme那一行改掉,比如说这样处理

scheme(module code1 scheme
    (provide make-table insert!)
    ;下面是你自己的那一串定义
)
(require 'code1)

然后在code_1.scm里可以照着原样写了

scheme#lang scheme/load

(load "code_0.scm")

(define table-0 (make-table))

(insert! 0 0 table-0)
(insert! 1 0 table-0)
(insert! 2 0 table-0)

还有一种方式就是弃用DrRacket,该用其他scheme实现来做习题。

【热门文章】
【热门文章】