【问题标题】:Scheme redefine a listScheme重新定义一个列表
【发布时间】:2011-02-03 03:02:20
【问题描述】:
我有一个名为 hand 的列表和另一个名为 deck 的列表,这里的主要目标是在我调用函数 draw 时取出列表卡组中的第一张卡(或元素)并将其放入列表手...
> (draw hand deck)
(2 C)
> (draw hand deck)
(2 C) (3 H)
> (draw hand deck)
(2 C) (3 H) (K D)
但每次我称它为手时,它都不会改变价值......
我不知道有没有像 O-Object 这样的方法来永久改变手的内容?
我最初定义手是空的,因为玩家没有牌可以开始。
(define hand '())
【问题讨论】:
标签:
scheme
blackjack
playing-cards
【解决方案1】:
功能性解决方案,draw 无副作用:
;; Returns a cons whose car will be the new hand and cdr being the
;; rest of the original deck
(define (draw deck hand)
(if (not (null? deck))
(cons (cons (car deck) hand) (cdr deck))
(cons hand ())))
;; Gets the new hand from the cons returned by draw.
(define (hand cards) (car cards))
;; Gets the new deck from the cons returned by draw.
(define (deck cards) (cdr cards))
;; test
(define cards (draw '(1 2 3 4 5) ()))
cards
=> ((1) 2 3 4 5)
(hand cards)
=> (1)
(deck cards)
=> (2 3 4 5)
;; draw again
(set! cards (draw (deck cards) (hand cards)))
cards
=> ((2 1) 3 4 5)
(hand cards)
=> (2 1)
(deck cards)
=> (3 4 5)
【解决方案2】:
您无法更改列表的内容,但可以更改名称所指的列表。所以:
(let ((some-list '("post")))
(display "initially: ")
(display some-list)
(newline)
(set! some-list (cons "first" some-list))
(display "added first: ")
(display some-list)
(newline)
(set! some-list '(a completely new list))
(display "finally: ")
(display some-list)
(newline)
some-list)
现在每个列表 '("post") '("first" "post") 和 '(一个全新的列表) 都是不可更改的("immutable")列表,但名称 some-list first 指向一个,然后是另一个,然后是第三个。
警告:对于许多问题,您将要避免设置!并尝试以不同的方式思考问题。例如,如果您在游戏中使用世界和宇宙,
http://pre.plt-scheme.org/plt/doc/teachpack/2htdpuniverse.html
那么你会希望你的更新程序返回一个新的世界状态而不是使用 set!修改旧的。
【解决方案3】:
哦,接下来你会发现,从函数调用者的角度来看,更改名称在函数内部所指的列表不会改变名称所指的内容。所以:
(define (foo lst)
(set! lst '(hi))
(display "within foo: ")
(display lst)
(newline)
lst)
(define my-list '(hello))
(foo my-list)
(display "after foo: ")
(display my-list)
(newline)
(set! my-list (foo my-list))
(display "using the result of foo: ")
(display my-list)
(newline)
【解决方案4】:
Vijay 为 Scheme 提供了最佳解决方案。但是,如果您真的想通过永久更改列表来完成这项工作,则需要使用set-car! 和set-cdr!。这在 Scheme 中并不自然,需要一些 hack 才能使其工作:
首先定义hand和deck:
(define hand '(dummy))
(define deck '((2 C) (3 H) (K D)))
hand 必须从现有元素开始,以便它有一些现有的列表结构需要修改。您不能将 set-car! 和 set-cdr! 与 nil ('()) 一起使用。
现在写draw:
(define (draw from to)
; push the top element of `from` onto `to`
(set-cdr! to (copy to))
(set-car! to (car from))
; pop the top element of `from` off
(set-car! deck (cadr deck))
(set-cdr! deck (cddr deck)))
; also we need to define copy
(define (copy l)
(map (lambda (x) x) l))
这意味着你手上的最后一个元素将永远是虚拟的。最好为初始案例添加检查并覆盖它而不是推送:
(define (draw from to)
; push the top element of `from` onto `to` (just overwrite the first time)
(when (pair? (cdr to))
(set-cdr! to (copy to)))
(set-car! to (car from))
; pop the top element of `from` off
(set-car! deck (cadr deck))
(set-cdr! deck (cddr deck)))
您还应该在做任何事情之前检查from 是否为空。