问题描述
所以我使用函数 (random 3)
在我猜测的 0 和 3 之间生成一个随机数,但是每次我运行程序时,它每次都输出数字 3,而当我尝试 (random 2)
时,它总是输出 1.我查找了我需要做的事情,堆栈溢出的另一篇文章说我需要初始化随机状态,我认为我需要这样做,所以我把这段代码放在我的程序的顶部,那就是它的时候失控了。
(setf *random-state* (current-time))
在我将这段代码放在顶部并运行程序后,我首先从控制台收到了一个类似这样的错误。
*** - Program stack overflow. RESET
我再次尝试运行该程序,但出现了这个超级古怪的错误。
*** - handle_fault error2 ! address = 0x137c9500 not in [0x1a920000,0x1aaa7acc) !
SIGSEGV cannot be cured. Fault address = 0x137c9500.
GC count: 0
Space collected by GC: 0 0
Run time: 0 625000
Real time: 0 543820
GC time: 0 0
Permanently allocated: 92512 bytes.
Currently in use: 2734320 bytes.
Free space: 5 bytes.
然后我从 Windows 收到一条错误消息,该消息以 lisp.exe 结束,我不确定我做了什么,但是当十六进制出现时,我吓坏了。我环顾了论坛,但找不到使我的随机状态代码工作的解决方案,有人可以帮助我。
解决办法:
我没有使用 (current-time)
,而是使用了 (make-random-state t)
,它工作得很好,所以我的问题得到了解决。
解决方法
“...每次我运行程序时,它都会输出数字 3....” 调用 (random 3)
的代码在哪里?重复调用 (random 3)
应该返回 0 到 2 之间的随机数,包括 0 和 2,并且永远不应该返回 3。除非您更改 random
使用的随机状态,否则每次开始新会话时您将获得相同的模式.
random
接受一个可选的随机状态参数;您不需要手动设置全局 *random-state*
变量,但它可以很方便。在学习 Common Lisp(或任何新语言)时不要依赖你从其他语言中知道的东西,当你有问题时read the documentation。在 Common Lisp 中,随机状态不是一个整数;它是一个随机状态对象,您可以通过调用 make-random-state
创建一个新对象。
发布的代码调用 (current-time)
并将 *random-state*
设置为返回值。现在,current-time
不是 Common Lisp 函数;它特定于 Clisp,它打印当前时间作为副作用,返回 nil
。将*random-state*
设置为调用current-time
(nil
)的结果后,*random-state*
不再绑定随机状态对象,调用random
为不再有效。这可能就是您的程序“失控”的原因。
这是一个示例函数,它返回 0 到 n
之间的随机数列表(在 n
下面):
(defun random-list (n max)
(loop for i from 1 to n
collecting (random max)))
从 REPL 运行它以创建 10 个介于 0 和 2(含)之间的随机数列表,并使用默认随机状态。第一行显示 *random-state*
的值:
CL-USER> *random-state*
#S(RANDOM-STATE #*0101001111100011110111001111101110101101110011101101001010001101)
CL-USER> (random-list 10 3)
(2 1 1 0 2 2 0 1 1 0)
CL-USER> (random-list 10 3)
(0 2 2 2 2 1 1 2 0 2)
CL-USER> (random-list 10 3)
(2 0 2 1 0 0 2 2 1 1)
可以看到,每次调用该函数时,都会生成不同的列表。这是因为随机状态 (*random-state*
) 尚未在调用 random
之间重置。重新启动 REPL 并再次运行程序将产生完全相同的结果,因为随机状态被重置为其初始值:
CL-USER> *random-state*
#S(RANDOM-STATE #*0101001111100011110111001111101110101101110011101101001010001101)
CL-USER> (random-list 10 3)
(2 1 1 0 2 2 0 1 1 0)
CL-USER> (random-list 10 3)
(0 2 2 2 2 1 1 2 0 2)
CL-USER> (random-list 10 3)
(2 0 2 1 0 0 2 2 1 1)
再次重启,我们可以用setf
试试make-random-state
:
CL-USER> (setf *random-state* (make-random-state))
#S(RANDOM-STATE #*0101001111100011110111001111101110101101110011101101001010001101)
CL-USER> (random-list 10 3)
(2 1 1 0 2 2 0 1 1 0)
CL-USER> (random-list 10 3)
(0 2 2 2 2 1 1 2 0 2)
CL-USER> (random-list 10 3)
(2 0 2 1 0 0 2 2 1 1)
这没有按预期工作!我们得到了与使用默认随机状态时完全相同的结果;这是因为调用带有 make-random-state
参数或不带参数的 nil
会返回当前随机状态对象的副本。您可以使用 make-random-state
作为参数调用 t
以返回 new 随机状态对象。注意这里Clisp在调用setf
之后打印了随机状态对象;在 SBCL 中做同样的事情会导致一个非常不同的随机状态对象,需要打印更多的行。
CL-USER> (setf *random-state* (make-random-state t))
#S(RANDOM-STATE #*0101101000011011111101010101100110010101010100001011001111101100)
CL-USER> (random-list 10 3)
(0 1 0 0 1 1 1 2 2 1)
CL-USER> (random-list 10 3)
(0 0 2 2 0 2 1 0 2 1)
CL-USER> (random-list 10 3)
(1 2 1 2 0 0 1 0 2 0)
现在您可以看到 *random-state*
已设置为 new 随机状态对象,并且生成的列表与之前的列表不同。