Настройка случайного состояния в CLISP

#lisp #common-lisp #clisp

Вопрос:

Поэтому я использовал функцию (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) , который отлично работает, поэтому моя проблема решена.

Комментарии:

1. Вы должны вложить решение в ответ, а не в вопрос.

Ответ №1:

«… каждый раз, когда я запускал программу, она выводила цифру 3….» Где код, который вызывает (random 3) ? (random 3) Повторный вызов должен возвращать случайные числа от 0 до 2 включительно и никогда не должен возвращать 3. Вы будете получать один и тот же шаблон каждый раз, когда начинаете новый сеанс, если не измените случайное состояние, используемое random .

random принимает необязательный аргумент случайного состояния; вам не нужно вручную устанавливать глобальную *random-state* переменную, но это может быть удобно. Не полагайтесь на то, что вы знаете из других языков, изучая общий Лисп (или любой другой новый язык), читайте документацию, когда у вас возникает вопрос. В Common Lisp случайное состояние не является целым числом; это объект случайного состояния, и вы можете создать его, вызвав make-random-state .

Разнесенный код вызывает (current-time) и устанавливает *random-state* возвращенное значение. Теперь current-time это не обычная функция 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 качестве аргумента, чтобы вернуть новый объект случайного состояния. Обратите внимание, что здесь 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* был установлен новый объект случайного состояния, и результирующие списки отличаются от предыдущих списков.