Rah*_*ahn 3 scheme sicp racket
我按照SICP 3.3.3的说明创建一个表.
这是code_0.scm:
;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)
Run Code Online (Sandbox Code Playgroud)
code_0.scm工作得很好,但在成为外部参考文件之后却没有code_1.scm:
;我删除#lang scheme在code_0.scm这一刻
;code_1.scm
#lang scheme/load
(load "code_0.scm")
(define table-0 (make-table))
(insert! 0 0 table-0)
(insert! 1 1 table-0)
(insert! 2 2 table-0)
Run Code Online (Sandbox Code Playgroud)
DrRacket中显示错误:
assoc:不是一个合适的列表:{{0.0}}
根据我之前提出的问题,这是因为"assoc"函数已经在Scheme库(或DrRacket库?)中定义,编译器选择先将标准/系统链接到我的.
那么,是否可以更改DrRacket/Scheme搜索/引用库的顺序?
如果是的话,怎么样?
如果它是否,这是编译器或语言的缺陷吗?
如果我必须构建一个名称复制函数,还有另一种方法可以避免这种情况,除了在"主"文件中实现它吗?
Eli*_*lay 10
这不是你问题的直接答案,而是对你自己陷入困境的描述,最终解决了这个问题,并且还会导致其他问题.
我将首先解释一下Racket如何非常简短地评估模块,因为它与理解混乱有关.看起来你正试图使用"只是方案",所以你可能对此不太感兴趣,但是你仍然应该阅读它来理解你所处的问题.(即使你解决了那个问题.)
在通常的.rkt文件情况下,每个文件都在其自己的命名空间中进行评估,该命名空间由(1)#lang您指定的绑定和(2)您的各种库(=其他模块)填充require.所以,当你有
#lang foo
(require bar)
Run Code Online (Sandbox Code Playgroud)
你从一个新的命名空间开始,从所有命名空间中获取所有绑定foo
,然后从中添加绑定bar.在碰撞的情况下,require绑定将从语言中隐藏,因此如果它们都提供了一些功能f,则代码将使用的名称将来自bar.如果您需要多个库:
#lang foo
(require bar baz)
Run Code Online (Sandbox Code Playgroud)
两者bar并baz提供f,那么你会得到一个错误,如果
这些不同f秒.例如,如果bar
提供内置cons并baz提供cons创建可变对的(即,提供内置mcons的cons),则可能发生这种情况.你会
不会,如果他们都提供相同的得到一个错误,f虽然.
再次注意,这与"初始绑定"不同,#lang后者require只是在使用相同的名称时会影响它们.#lang不同处理绑定的原因是它们提供了代码在基本级别使用的一些基本绑定.例如,语言绑定将为require您提供稍后使用的语言绑定.另一个例子是一个#%module-begin包装整个模块体的
宏 - 一个可以用来将所有表达式转换成其他表达式的工具,例如,这就是如何在racket语言中使用表达式来获取它们的值.
粗略地说,球拍中的库(=模块)被分成语言模块和库模块.这不是正式的,因为两者都以相同的方式实现:提供东西的模块.所不同的是在那种东西,他们提供,其中语言模块通常会提供大量的绑定,包括像那些基本的require和#%module-begin,以及东西,你从像lispish语言想到define,if,cond,+,cons,等.
因此,在通常的情况下,由于库试图避免使用常见名称,因此不要过多地遇到名称冲突问题.但是如果你尝试将require一个语言模块看作是一个库,你会很快遇到这样的问题,因为语言模块往往会提供很多名字,包括那些非常常见的名字,比如我上面列出的名字.
现在您可以看到这是一个问题code0.scm:
#lang scheme
(require rnrs/base-6)
(require rnrs/mutable-pairs-6)
Run Code Online (Sandbox Code Playgroud)
这样做首先使用scheme绑定.这种scheme
语言不是标准的方案 - 它是racket
语言的前身,可以追溯到名称改变之前,当Racket被称为PLT Scheme时,因此scheme被认为是"PLT Scheme默认使用的方案方言".除此之外,它具有不可变对,与#lang racket文件中的对齐相同.
但是后来你堆积了大部分rnrs绑定 - 而那些使用
可变对,正如你所发现的那样,它们是一种不同的类型.你需要一个通常用作语言的模块,所以
你使用的大多数绑定都可以正常工作,但是迟早你会遇到一个绑定scheme,不会被一个绑定rnrs,如果它是与成对有关,你会遇到问题.
因此,这里的结论是为了避免两种语言像这样混合起来:无论是坚守#lang scheme还是#lang r6rs.在前一种情况下,您也可以切换到#lang racket并使用通常的球拍库,在后一种情况下,您应该小心并尽量避免导入期望不可变对的球拍库(以及更多).或者,如果你的目标是做一些SICP,那么使用Neil Van Dyke所写的SICP语言.
但是你还有更多问题.在code_1.scm你使用
scheme/load的语言,这是一些可能使人们有可能做你想做什么-但它与它带来的其他问题一大堆.我查找文档
scheme/load(将发送给你的文档racket/load,更现代的名称),你会看到一些关于eval和的东西load.这是因为在某些时候人们希望能够编写一个包含多个模块的文件,但这是不可能的.(现在是,你得到了子模块 - 但racket/load仍然存在.)
scheme/load实现是为了解决这个问题:使用它就好像你在一个REPL中输入表达式一样,所以你可以定义一堆模块并使用它们.但是由于这个原因,这是一种奇怪的语言,如果你不想要那个特殊的功能,你应该避免使用它.
该load名称中的是应该有实际上是一些不鼓励使用它的人......问题是,
load对于结构化的代码,这是标准的语言多达R5RS唯一可用的东西一个古老而原始的工具.它所做的事情(再次,这是一个粗略的描述)read来自文件的表达式并且eval使它们更好.问题是你得到了一个名称空间,更糟糕的是,如果存在一个定义,每个定义实际上可能是先前定义的变异.这意味着即使是简单的定义
(define (add1 x) (+ 1 x))
Run Code Online (Sandbox Code Playgroud)
不安全,因为load文件的后期可以+以破坏此定义的方式重新定义.简而言之,在许多库为您提供相同名称但具有不同语义的世界中,这非常混乱.IOW,在Racket中通常是一团糟.Racket(以及后来的R6RS)以一种更好的方式对整个事物进行排序的方式使用模块 - 没有单一的命名空间和变异,只是从封闭模块提供的名称.实际上,变异的东西仍然存在,但是你可以获得的伤害更受限制(它仅在REPL中使用); 并且load也是eval,它们通常被避免作为组织源文件的工具.
使用load也解释了为什么你不得不删除#lang线,当你使用它-当你load有一个模块定义文件时,你得到的只是那个定义-模块的.要实际使用模块提供的东西,您还需要添加一个(require 'code_0).还有一件事就是放弃这#lang条线通常是一场灾难,因为你没有为任何合理的代码片段留下必要的绑定 - 但在你的情况下,你require以后会使用整个语言,这就是事情继续工作的方式,有微妙的差异.
因此,第二个高级结论是避免load- 这是一个糟糕的工具.此外,除非您真正了解他们正在做什么并需要该功能,否则请避免使用scheme/load或racket/load语言.