【问题标题】:scheme:: contract violation: recursive procedure方案:: 违反合同:递归程序
【发布时间】:2016-11-22 18:38:40
【问题描述】:

我需要编写一个以列表为参数的方案过程,它定义了得分、玩家 A 得分和玩家 B 得分。该函数应根据分数确定谁是获胜者:

例如,这是我在下面使用的分数列表:

(define scores '(
                 (5 . (5 . 3)) ; points awarded = (car (car scores)) 5; player A score = cadr (car scores)) 5; player B score (cddr (car scores)) 3;
                 (5 . (6 . 2))
                 (5 . (8 . 4))
                 (5 . (5 . 1))))

所以为了澄清,分解列表中的第一个列表:

5 = 积分(car (car scores)) ;

A = 玩家 A 分数 (cadr (car scores)) ;(第一个元素为 5,第二个元素为 6,依此类推)

B = 玩家 B 分数 (cddr (car scores)) ;(第一个元素为 3,第二个元素为 2,依此类推)

问题是我做了一个递归函数,它在递归的第一次迭代中爆炸。但我不明白为什么?

#lang scheme

(define (game-winner scores)  
  (define A 0)
  (define B 0)
  (cond
    ((empty? scores) '()))
  (if ( > (cadr (car scores)) (cddr (car scores)) )
      (+ A (car (car scores)))
      (+ B (car (car scores))))
  (game-winner (cdr scores)))

输出:

car: contract violation
  expected: pair?
  given: ()

让我困惑的部分是,当我模拟运行第一次递归迭代并获得函数 应该 具有的值时,我得到正确的值:

;first loop through function

    (car (car scores))  ; 5
    (cadr (car scores)) ; 5
    (cddr (car scores)) ; 3

;second loop (1st recursive call)

    (cadr (car (cdr scores))) ; 6
    (cddr (car (cdr scores))) ; 2

如果我不明白它为什么会爆炸?它应该以与递归之前的第一次调用相同的方式工作。但我显然错过了一些东西。有什么想法吗?

附言 如果我只返回 A 或 B 而不是递归调用,我会得到 0:

(define (game-winner scores)  
  (define A 0)
  (define B 0)
  (cond
    ((empty? scores) '()))
  (if ( > (cadr (car scores)) (cddr (car scores)) )
      (+ A (car (car scores)))
      (+ B (car (car scores))))
  A)

输出: 0

当我输出 A 时,为什么第一次调用后 A 的值(应该是 5)没有显示? A 是否仅在 if 循环的范围内?如果是这样,我如何让它存在于该范围之外?

根据@Sylwester 的反馈,我通过程序修改为:

#lang scheme

(define (game-winner scores)  
  (define A 0)
  (define B 0)
  (cond
    ((empty? scores) '())
    (( > (cadr (car scores)) (cddr (car scores)))    
     (cons (+ A (car (car scores))) (game-winner (cdr scores))))
    (cons (+ B (car (car scores))) (game-winner (cdr scores)))))

输出:

(5 5 5 5)

所以我觉得我越来越近了。但是我需要能够为 A 或 B 将所有这些加在一起并输出获胜者(A 或 B)。我如何在此基础上构建以使其发挥作用?

【问题讨论】:

    标签: recursion scheme lisp racket


    【解决方案1】:

    无论结果如何,您的代码都有很多永远不会使用的死代码,最后最后一个表达式将执行(cdr scores),无论它是否为空。

    (define (game-winner scores)  
      ;; Two constants that never change from 0
      (define A 0)
      (define B 0)
    
      ;; Dead code. Werther it's '() or #<undefined>
      (cond
        ((empty? scores) '()))
    
      ;; Dead code. Becomes (+ 0 (car scores)) but never used
      (if ( > (cadr (car scores)) (cddr (car scores)) )
          (+ A (car (car scores)))  ; + never mutates A
          (+ B (car (car scores)))) ; + never mutates B
    
      ;; Infinite recursion. Happens no matter what is the outcome of the dead code
      (game-winner (cdr scores)))
    

    因此,当您编写 condif 时,它应该处理所有发生的事情,使其成为最后一个表达式:

    (define (game-winner scores)
      ; local defines
      (define some-var some-expression)
      ...
    
      ; one expression, the result of this is the result of the procedure.
      (if (empty? scores)
          '()
          (if ....)))
    

    编辑

    这是一个使用参数来累积分数并最终确定谁得分最高的递归示例:

    (define (game-winner scores)
      (let loop ((scores scores) (a 0) (b 0))
        (if (empty? scores)
            (cond ((> a b) 'A)
                  ((< a b) 'B)
                  (else 'TIE)))
            (loop (cdr scores)
                  (+ a (cadar scores))
                  (+ b (cddar scores))))))
    
    (game-winner scores)
    ; ==> A
    

    【讨论】:

    • 感谢@Sylwester 我是菜鸟。我尝试修改我的程序(添加到我原始问题的底部)。我觉得我可能越来越近了,但还没有。请问您还有什么可以提供的帮助吗?
    • @mdo123 不完全确定它应该如何工作,但我添加了一个示例,说明如何使用额外参数在迭代之间保持状态。
    猜你喜欢
    • 1970-01-01
    • 2016-03-19
    • 1970-01-01
    • 2017-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多