【问题标题】: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 才能使其工作:

          首先定义handdeck

          (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 是否为空。

          【讨论】:

            猜你喜欢
            • 2014-10-12
            • 1970-01-01
            • 1970-01-01
            • 2014-03-15
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-01-15
            • 1970-01-01
            相关资源
            最近更新 更多