Вам вообще не нужно использовать eval
здесь.Это делает решение более сложным (и небезопасным ), чем необходимо.Кроме того, логика «зацикливания» неверна, потому что вы не обновляете позицию в lst-nouns
, и в любом случае она переопределяется при каждом вызове процедуры.Кроме того, посмотрите ссылку , предоставленную Сорави, чтобы понять, почему eval
не может видеть локальные привязки.
В Схеме мы стараемся по возможности избегать состояния мутации, но для этой процедуры я думаю,это оправдано.Хитрость заключается в том, чтобы сохранить состояние, которое должно быть обновлено внутри замыкания;это один из способов сделать это:
(define nouns '(time
year
people
way
day
man))
; notice that `next-noun` gets bound to a `lambda`
; and that `lst-nouns` was defined outside of it
; so it's the same for all procedure invocations
(define next-noun
; store list position in a closure outside lambda
(let ((lst-nouns '()))
; define `next-noun` as a no-args procedure
(λ ()
; if list is empty, reset with shuffled original
(when (null? lst-nouns)
(set! lst-nouns (shuffle nouns)))
; obtain current element
(let ((noun (car lst-nouns)))
; advance to next element
(set! lst-nouns (cdr lst-nouns))
; return current element
noun))))
@ PetSerAl предложил более идиоматическое решение в комментариях.Я предполагаю, что вы хотите реализовать это с нуля, в учебных целях - но в реальной жизни мы бы сделали что-то подобное, используя генераторы Racket :
(require racket/generator)
(define next-noun
(infinite-generator
(for-each yield (shuffle nouns))))
В любом случаеработает как положено - повторный вызов next-noun
вернет все элементы в nouns
до тех пор, пока не будет исчерпан, в этот момент список будет переставлен, и итерация будет перезапущена:
(next-noun)
=> 'day
(next-noun)
=> 'time
...