开发者

Scheme redefine a list

I have a list called hand and another one called deck, the main goal here is to take the first card (or element ) in the list deck and put it in the list hand when i call the fnction draw...

> (draw hand deck)
(2 C)
> (draw hand deck)
(2 C) (3 H) 
> (draw hand deck)
(2 C) (3 H) (K D)

but everytime i call it the hand never changes value... I'm cluel开发者_高级运维ess is there a way like in O-Object to change the content of hand permenantly?

and i initialy define hand empty because the player has no card to start.

(define hand '())


A functional solution, with draw being side-effect free:

;; 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)


You cannot change the contents of a list, but you can change which list a name refers to. So:

(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)

Now each of the lists '("post") '("first" "post") and '(a completely new list) are unchangeable ("immutable") lists, but the name some-list first points to one, then another, then the third.

Caveat: For many problems, you will want to avoid set! and try to think about the problem a different way. For example, if you work with worlds and universes for your game, http://pre.plt-scheme.org/plt/doc/teachpack/2htdpuniverse.html then you'll want your updaters to return a new state of the world rather than using set! to modify the old one.


Oh, and next you will find that changing what list a name refers to inside a function will not change what the name refers to from the perspective of whoever called the function. So:

(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)


Vijay has the best solution for Scheme. However, if you really want to make this work by changing the lists permanently, you'll need to use set-car! and set-cdr!. This is not natural in Scheme, and requires a couple of hacks to make it work:

First define hand and deck:

(define hand '(dummy))
(define deck '((2 C) (3 H) (K D)))

hand has to start with an existing element so that it has some existing list structure to modify. You can't use set-car! and set-cdr! with nil ( '() ).

Now write 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))

This means the last element of your hand will always be dummy. It would be better to add a check for the initial case and overwrite it instead of pushing:

(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)))

Also you should check that from isn't empty before doing anything.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜