Racket 中的 SICP Ch5 eceval 编译器:set-cdr!进入引用列表(不是重复的)

SICP Ch5 eceval compiler in Racket: set-cdr! into quoted list (not a dup)

这不是 set-car!, set-cdr! unbound in racket? 或 的 或属于 How to install sicp package module in racket?, 而是一个后续问题,因为其中提出的解决方案没有 为我工作。一、需要:Section 5.5.5 of SICP, compiler plus 显式控制评估器 (code here in "ch5-eceval-compiler.scm"),是 完全依赖于 set-car!set-cdr! 进入显式引用列表。我 想要复制和修改此代码而不进行完整的、自下而上的重写 不变的形式。我也接受对可以的方案实现的引用 运行 开箱即用的代码或经过一些最小的、直接的改编, 即,具有 set-car!set-cdr! 或一些的方案 解决方法。诡计和诡计都没有让我轻松 运行 编写此代码。

编辑:mit 方案将加载 eceval 编译器。我把这个问题留给那些可能想让它进入球拍的人(例如,我宁愿)。

这里有更深入的解释,包括我探索和尝试的东西,以及 我如何将引用列表诊断为最深层次的问题。当我手动转换 引用列表变成 mlistsmquoted 嵌套,代码崩溃得更糟 方式和兔子洞变得更深。几次后我不得不恢复 数小时精细的脑部手术失败了。

这里是5.5.5节所依赖的那种结构的MVE。这个很小,但是结构跟真的一样:

(define foo '(a b))
(set-cdr! foo '(c))

真正的事情是这样开始的:

(define eceval
  (make-machine
   '(exp env val proc argl continue unev
     compapp            ;*for compiled to call interpreted
     )
   eceval-operations ;;   ----------------------------------------------
  '(  ;; <<<<<<<<======== BIG QUOTED LIST CAUSING TROUBLE / NOT MCONSES!
;;SECTION 5.4.4, as modified in 5.5.7 ;; -------------------------------
;;*for compiled to call interpreted (from exercise 5.47)
  (assign compapp (label compound-apply))
;;*next instruction supports entry from compiler (from section 5.5.7)
  (branch (label external-entry))
read-eval-print-loop
  (perform (op initialize-stack))
  (perform
   (op prompt-for-input) (const ";;; EC-Eval input:"))
...

并持续了很长一段时间。评估者是引用中的 "machine-code" 列表,各种生成的代码将 set-car!set-cdr! 放入寄存器 和环境框架和其他东西。代码加载失败。

似乎没有简单的方法可以将计算器转换为不可变形式 没有完全重写,我正在努力避免这种情况。当然,set-car!set-cdr!#lang racket 中不可用,我认为它们不在 诡计,要么(至少诡计拒绝加载 "ch5-eceval-compiler.scm," 抛出一个可变性错误,对此我没有深入研究)。

中提出的一个解决方案 set-car!, set-cdr! unbound in racket? 是 根据 (require compatibility/mlist) (require rnrs/mutable-pairs-6) 使用 mconsmcarmlist 等重写代码。那些兼容性 包无法替代 quote,因此我尝试编写自己的 mquote。我 在这样的重构上花了几个小时,但练习不是 收敛,在兔子洞里越来越深,最后得到 甚至更深层次的问题。似乎要进行重构我必须 了解更多关于 "ch5-eceval-compiler.scm," 的语义,如果我必须,我 不妨以不可变的形式重写它。

中提出了更简单的解决方案 set-car!, set-cdr! unbound in racket? 是使用 #lang sicp#lang r5rs。下面进行三个实验 参考了堆栈溢出的其他答案:

#lang r5rs
(define foo '(a b))
(set-cdr! foo '(c))
foo
Error: struct:exn:fail:contract:variable

set-cdr!: undefined;
 cannot reference an identifier before its definition
  in module: "/usr/share/racket/pkgs/r5rs-lib/r5rs/main.rkt"
             -----------------------------------------------

指向set-cdr!明确定义的地方:

...
(module main scheme/base
  (require scheme/mpair
           racket/undefined
           (for-syntax scheme/base syntax/kerncase
                       "private/r5rs-trans.rkt")
           (only-in mzscheme transcript-on transcript-off))

  (provide (for-syntax syntax-rules ...
                       (rename-out [syntax-rules-only #%top]
                                   [syntax-rules-only #%app]
                                   [syntax-rules-only #%datum]))
           (rename-out
            [mcons cons]
            [mcar car]
            [mcdr cdr]
            [set-mcar! set-car!] ;; --------------------------
            [set-mcdr! set-cdr!] ;; <<<<<<<<======== LOOK HERE
            [mpair? pair?]       ;; --------------------------
            [mmap map]
            [mfor-each for-each])
           = < > <= >= max min + - * /
           abs gcd lcm exp log sin cos tan not eq?
           call-with-current-continuation make-string
           symbol->string string->symbol make-rectangular
           exact->inexact inexact->exact number->string string->number
...

这里有一个与 #lang sicp

类似的失败
#lang sicp
(define foo '(a b))
(set-cdr! foo '(c))
foo
Error: struct:exn:fail:contract:variable

set-cdr!: undefined;
 cannot reference an identifier before its definition
  in module: "/home/rebcabin/.racket/7.2/pkgs/sicp/sicp/main.rkt"
             ----------------------------------------------------

指向仅间接定义 set-cdr! 的代码,但在 合适的包裹:

....
#lang racket

(require racket/provide         ;; --------------------------------------------
         (prefix-in r5rs: r5rs) ;; <<<<<<<<======== PULL IN SET-CDR! ETC. HERE?
         (rename-in racket [random racket:random])) ;; ------------------------

(provide (filtered-out (λ (name) (regexp-replace #px"^r5rs:" name ""))
                       (except-out (all-from-out r5rs) r5rs:#%module-begin))
         (rename-out [module-begin #%module-begin]))

(define-syntax (define+provide stx)
  (syntax-case stx ()
    [(_ (id . args) . body) #'(begin
                                (provide id)
                                (define (id . args) . body))]
    [(_ id expr) #'(begin
                     (provide id)
                     (define id expr))]))
...

我深入挖掘 并找到

(require (only-in (combine-in rnrs/base-6
                              rnrs/mutable-pairs-6)
                  set-car!
                  set-cdr!))
(define foo '(a b))
(set-cdr! foo '(c))
foo

屈服

Error: struct:exn:fail:contract

set-mcdr!: contract violation
  expected: mpair?
  given: '(a b)
  argument position: 1st
  other arguments...:
   '(c)

此错误表明问题确实出在引用列表上。我没有 将 eceval 中的大引用列表变成 mlist 或链的简单方法 mcons。我试过了,它非常冗长且容易出错,而且我认为代码 加载 eceval 扫描和列出的补丁,因此它使用其他列表操作。 一路南下,不得不折返

也许我错过了一些自动化转换的方法,一个宏,但那是 更深的兔子洞(我的方案宏富太老了)

所以我卡住了。没有什么容易或推荐的作品。我想 (1) 知道一个方案实现,它将 运行 这段代码 (2) 我可以通过某种方式在球拍中实现 set-car!set-cdr! (3) 一些其他类型的解决 (4) 或者我只是犯了一个愚蠢的错误,你们中的好心人会很容易地改正。

Mit-scheme 将从问题中提到的代码删除中加载 eceval 编译器。在 Ubuntu 上,mit-scheme 加载 sudo apt-install mit-schemegeiser package of emacs 通过 run-mit 找到 mit。问题解决了。

我试过了(直接 运行 racket 或通过 DrRacket 运行)

#lang sicp

(define foo '(a b))
(set-cdr! foo '(c))
foo

并输出 (a c).

这也有效:

#lang r5rs

(define foo '(a b))
(set-cdr! foo '(c))
(display foo)

要回答有关 SICP(我目前正在维护)实施的问题,您是正确的,(prefix-in r5rs: r5rs) 将导入 set-cdr!


更新:我刚刚安装了 Geiser,现在遇到了与我 C-c C-b 时相同的问题。但是,C-c C-a 按预期工作。

就个人而言,我会使用 racket-mode 而不是 Geiser。