lisp - Scheme中的Push和Pop怎么写?

标签 lisp scheme racket

现在我有

(define (push x a-list)
  (set! a-list (cons a-list x)))

(define (pop a-list)
  (let ((result (first a-list)))
    (set! a-list  (rest a-list))
    result))

但我得到了这个结果:

Welcome to DrScheme, version 4.2 [3m].
Language: Module; memory limit: 256 megabytes.
> (define my-list (list 1 2 3))
> (push 4 my-list)
> my-list
(1 2 3)
> (pop my-list)
1
> my-list
(1 2 3)

我做错了什么?有没有更好的方法来编写 push 以便在末尾添加元素并 pop 以便从第一个删除元素?

最佳答案

这是关于在代码中使用突变的要点:没有必要为此跳转到宏。我现在假设堆栈操作:要获得一个可以传递和改变的简单值,您所需要的只是列表周围的包装器,其余代码保持不变(好吧,进行微小的更改它正确地执行堆栈操作)。在 PLT Scheme 中,这正是 boxes 的用途:

(define (push x a-list)
  (set-box! a-list (cons x (unbox a-list))))

(define (pop a-list)
  (let ((result (first (unbox a-list))))
    (set-box! a-list (rest (unbox a-list)))
    result))

另请注意,您可以使用 begin0 代替 let:

(define (pop a-list)
  (begin0 (first (unbox a-list))
    (set-box! a-list (rest (unbox a-list)))))

至于把它变成一个队列,你可以使用上面的方法之一,但是除了Jonas写的最后一个版本,其他解决方案都非常低效。例如,如果您按照 Sev 的建议进行操作:

(set-box! queue (append (unbox queue) (list x)))

然后这会复制整个队列——这意味着将项目添加到队列的循环将在每次添加时复制所有项目,为 GC 生成大量垃圾(考虑附加一个字符到循环中字符串的末尾)。 “unknown (google)”解决方案修改了列表并在其末尾添加了一个指针,因此它避免了生成垃圾以进行收集,但仍然效率低下。

Jonas 编写的解决方案是执行此操作的常用方法——保留指向列表末尾的指针。但是,如果你想在 PLT Scheme 中这样做,你将需要使用可变对:mconsmcarmcdr set-mcar!, set-mcdr!.自 4.0 版问世以来,PLT 中的常用对是不可变的。

关于lisp - Scheme中的Push和Pop怎么写?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1041603/

相关文章:

macros - 编译后,假设程序可以在不调用 eval 的情况下运行吗?

lisp - 如何在列表中放置一个元素? (口齿不清)

ubuntu - Ubuntu 方案和 XWin 方案的区别

syntax - 如何在本地更改 Racket 中的阅读规则?

servlets - 如何根据 Racket Web servlet 中的路径显示不同的内容?

syntax - Lisp:使用 AND 运算符的 IF 条件

recursion - list 方案总和

debugging - 将构造函数和选择器定义为 cons、car 和 cdr 是否仍然不可取?

Scheme Sum from a to b 迭代

racket - 生成 :custom-write for Racket classes