随机程序

时间:2013-02-27 01:48:05

标签: scheme racket

我正在尝试编写一些代码,但我的回复程序存在问题。忽略前两个三个随机数的过程,但问题出在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)) 

它抛出了这个错误:

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

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

5 个答案:

答案 0 :(得分:2)

错误只是说清单' length为零,random期望值介于1和4294967087之间。调用pick-random时传递非空列表。这就是发生的事情:

(random 0)

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

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

答案 1 :(得分:2)

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

答案 2 :(得分:1)

我想你知道现在几点了。

[dons code-review hat /]

首先,在我们得到其他任何内容之前,您使用的是什么版本的Racket?

我这里有5.2.1,所以我可能有些落后,但nthwrite-line似乎没有在这里定义函数。我认为你想要的是list-refwrite。请注意,list-ref具有不同的参数顺序,并期望列表中的零索引引用。换句话说(list-ref (list 1 2 3) 0) => 1。因此,您可能希望将pick-random定义为

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

您还有一些未定义的函数,我假设您在程序的其他位置定义了hedgequalifierchange-person)。我会在进一步的评论中忽略这些。


正如Óscar所提到的,random取1到4294967087范围内的整数。这意味着你想在将数字传递给它之前考虑到这一点。请注意,(random n)似乎返回0(- n 1)之间的整数,因此您无需自行添加或减去某些内容以从列表中获取随机元素。

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

正如user448810所提到的,你不想重新计算random-of-three,因为这不会给你每个选择的相同概率。由于您将预先计算它,因此您可能根本不需要random-of-three函数。

(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实际上并没有返回任何内容。它只是向REPL输出内容,因此else函数的reply子句不能是您的意思,除非write-line执行与write根本不同的事情。

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

你要么

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

如果您只想要打印输出,或

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

如果您打算保留返回值以供将来在某处使用。


接下来,这可能是我看到的最大的错误,也是Scheme新手最容易犯的错误(所以不要感觉不好),你不会在任何地方改变earlier-responsesdoctor-driver-loop

中的else子句
(else (list user-response earlier-responses)                    
      (write (reply user-response earlier-responses))
      (doctor-driver-loop name earlier-responses))

创建一个新列表,其中user-response为头部,earlier-responses为尾部,但您不对其执行任何操作。您也不会在下次调用doctor-driver-loop时传递不同的值。如果你真的想跟踪输入历史记录,你会希望它看起来像

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

但是,这似乎不是你想要的那样。以上内容总是将两个元素的列表传递给驱动程序循环的下一次迭代;第一个元素是最近的响应,第二个元素是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))))

如果您希望能够使用pick-random选择随机的过去回复,那么真正想要想要的是所有回复的平面列表。这意味着cons新的回复而不是list

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

所以你的else条款应该是

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

答案 3 :(得分:1)

(定义随机4位数   (foldr(λ(n结果)            (cons((map(λ(y)(if(> = y n)(add1 y)y))                         结果)))          “()          (地图随机'(10 9 8 7))))

答案 4 :(得分:0)

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

an "or" contract - 意思是,要被此合约接受,价值(random function的参数)必须是以下之一:an integer between 14294967087,包括or object(pseudo-random-generator? object)返回#t