1

我正在尝试编写一些代码,但我的回复程序有问题。忽略前两个随机数的过程,但问题出else在调用pick-random过程时。这是代码:

(define earlier-responses '()) 

(define (doctor-driver-loop name earlier-responses)
  (newline)
  (write '**)
  (let ((user-response (read)))
    (cond 
      ((equal? user-response '(goodbye))
         (write-line (list 'goodbye name))
         (write-line '(see you next week)))
      (else 
         (list user-response earlier-responses)                    
         (write-line (reply user-response earlier-responses))
         (doctor-driver-loop name earlier-responses)))))

(define (reply user-response earlier-responses)
  (cond 
    ((= (random-of-three) 0)
      (append (qualifier)
              (change-person user-response)))
    ((= (random-of-three) 1)
      (hedge))
    (else
      (append (write-line '(earlier you said that))
              (pick-random earlier-responses)))))

(define (random-of-three)
  (random 3))

(define (pick-random lst)
  (nth (+ 1 (random (length lst))) lst))

它突出了

            (random (length lst))) lst)) 

它抛出这个错误:

  随机:违反合同
  预期:(或/c(integer-in 1 4294967087)伪随机生成器?)
  给定:0

我不确定这个错误是什么意思或如何解决它......

4

5 回答 5

2

错误只是说明列表的长度为零,并且random需要一个介于 1 和 4294967087 之间的值。调用时传递一个非空列表pick-random。这就是正在发生的事情:

(random 0)

=> random: contract violation expected: (or/c (integer-in 1 4294967087)
   pseudo-random-generator?) given: 0

尝试earlier-responses在开头使用非空列表进行定义。

于 2013-02-27T01:57:01.427 回答
2

Oscar 正确回答了您提出的问题,但是您还没有意识到一个更严重的错误:您应该在进入 cond 表达式之前计算一次随机数,而不是在每个 cond 子句中重新计算它。使用您的方法,您不会以相同的概率选择三种可能性中的每一种。第一个选项将被选中大约 1/3 的时间,第二个选项将被选中大约剩余 2/3 时间的 1/3,或 2/9,第三个选项将被选中剩余的 4/ 9 次。您可能希望所有三个选择以相等的概率发生。

于 2013-02-27T05:01:25.193 回答
1

I think you know what time it is.

[dons code-review hat /]

Firstly, before we get to anything else, what version of Racket are you using?

I've got 5.2.1 here, so I may be a bit behind, but nth and write-line don't seem to be defined functions here. I think what you want is list-ref and write. Note that list-ref has a different argument order, and expects a zero-indexed reference into the list. In other words (list-ref (list 1 2 3) 0) => 1. You therefore probably want to define pick-random as

(define (pick-random lst)
  (list-ref lst (random (length lst))))

You also have a couple of undefined functions there which I assume you define elsewhere in your program (hedge, qualifier and change-person). I'll elide these in further comments.


As Óscar mentions, random takes an integer in the range of 1 to 4294967087. Which means that you want to account for that before passing a number to it. As a note, (random n) seems to return an integer between 0 and (- n 1) inclusive, so you don't need to add or subtract things yourself to get a random element out of a list.

(define (pick-random lst)
  (if (null? lst)
      lst
      (list-ref lst (random (length lst)))))

As user448810 mentions, you don't want to re-calculate random-of-three because that won't give you equal probabilities of each choice being made. Since you'll be pre-calculating it, you probably don't need the random-of-three function at all.

(define (reply user-response earlier-responses)
  (let ((rand (random 3)))
    (cond ((= rand 0)
           (append (qualifier)
                   (change-person user-response)))
          ((= rand 1)
           (hedge))
          (else
           (append (write '(earlier you said that))
                   (pick-random earlier-responses))))))

write doesn't actually return anything. It just prints something to the REPL, so the else clause of your reply function can't be what you mean unless write-line does something fundamentally different from write.

(else
 (append (write '(earlier you said that))
         (pick-random earlier-responses)))

You either want

(else
   (write '(earlier you said that))
   (write (pick-random earlier-responses)))

if you just want printed output, or

(else
   (let* ((res (list '(earlier you said that) (pick-random earlier-responses))))
     (write res)
     res))

if you meant to preserve the return value for future use somewhere.


Next, and this is probably the biggest bug I see as well as the easiest for Scheme newbies to make (so don't feel bad), you're not changing earlier-responses anywhere. The else clause in your doctor-driver-loop

(else (list user-response earlier-responses)                    
      (write (reply user-response earlier-responses))
      (doctor-driver-loop name earlier-responses))

creates a new list with your user-response as the head and earlier-responses as the tail, but you don't do anything with it. You also don't pass a different value into the next call to doctor-driver-loop. If you actually wanted to track input history, you would want that to look something like

(else (write (reply user-response earlier-responses))
      (doctor-driver-loop name (list user-response earlier-responses)))

But that wouldn't quite do what you seem to want it to either. The above would always pass a two-element list to the next iteration of your driver loop; the first element would be the most recent response and the second element would be earlier-responses.

> (list '(I need a perscription) '((how are you doing?) (hello))) 
((I need a perscription) ((how are you doing?) (hello)))

> (list '(are you even listening?) '((I need a perscription) ((how are you doing?) (hello))))
((are you even listening?) ((I need a perscription) ((how are you doing?) (hello))))

What you'd really want, if you want to be able to pick a random past response using pick-random, is a flat list of all responses. That means consing new responses rather than listing them.

> (cons '(I need a perscription) '((how are you doing?) (hello)))
((I need a perscription) (how are you doing?) (hello)))

> (cons '(are you even listening?) '((I need a perscription) (how are you doing?) (hello)))
((are you even listening?) (I need a perscription) (how are you doing?) (hello))

So your else clause should be

(else (write (reply user-response earlier-responses))
      (doctor-driver-loop name (cons user-response earlier-responses)))
于 2013-02-27T17:08:24.007 回答
1

(define random-4-digits (foldr (λ (n result) (cons n (map (λ (y) (if (>= yn) (add1 y) y)) result))) '() (map random' (10 9 8 7))))

于 2016-01-22T12:09:18.607 回答
0

(or/c (integer-in 1 4294967087) pseudo-random-generator?)

一个“或”合约——意思是,要被这个合约接受,值(random函数的一个参数)必须是以下之一:或者是一个介于和之间的整数 ,包括在内,或者一个这样的返回。14294967087object(pseudo-random-generator? object)#t

于 2013-02-27T23:23:28.427 回答