#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*
был установлен новый объект случайного состояния, и результирующие списки отличаются от предыдущих списков.