Проблемы с кодом
В отличие от большинства конструкций в Common Lisp, синтаксис LOOP специально использует несколько скобок.Часть (while ...)
не подходит в этом контексте.Кроме того, вы можете использовать until (= x a)
, который я считаю более читабельным.Взгляните на §22.LOOP для черных поясов .
Кроме того, (- x b)
только вычисляет вычитание, но не влияет ни на одну переменную.Если вы хотите уменьшить x
, как x -= b
в C, используйте (decf x b)
.
Имя вашей функции и переменные также не помогают понять, что должно происходить.
Наконец, ваш код может работать неправильно, если у вас очень большой шаг, потому что простое вычисление (- x b)
может дать результат, который все еще больше, чем b
.Кроме того, могут быть проблемы с отрицательными входными данными.
Первая попытка
Я пытался позаботиться обо всех угловых случаях, о которых я могу подумать, например, об отрицательных шагах и т. Д. Есть также тест, которыйпредотвращает бесконечные циклы, проверяя, существует ли текущий номер в списке.Проверка является линейной по времени, что делает весь цикл квадратичным.Для очень больших списков это может быть проблемой.
(defun circular-range (from to step)
(loop
with low = (min from to) and high = (max from to)
with divisor = (- high low)
for value = from then (+ wrapped step)
for wrapped = (if (<= low value high)
value
(+ low (mod (- value low) divisor)))
until (member wrapped numbers)
collect wrapped into numbers
until (= wrapped to)
finally (return numbers)))
Использование большего количества математик
Благодаря математике можно узнать размер периода из охватываемого диапазона и шага: Прогрессии по модулю n .Это позволяет убрать некоторые проверки, в частности список уже увиденных чисел.
(defun circular-range (from to step)
(loop
with low = (min from to) and high = (max from to)
with range = (- high low)
with period = (/ range (gcd range step))
repeat (1+ period)
for value = from then (+ wrapped step)
for first = t then nil
for wrapped = (if (<= low value high)
value
(+ low (mod (- value low) range)))
when (or first (/= wrapped from))
collect wrapped))
Однако нам нужно повторить еще раз, чтобы удовлетворить спецификации и собрать значение to
, за исключением случаев, когда это значение равнодо from
.
Тесты
Следующие результаты идентичны для обеих версий.
(circular-range 0 5 0)
=> (0)
(circular-range 0 5 2)
=> (0 2 4 1 3 5)
(circular-range 0 -5 2)
=> (0 -3 -1 -4 -2)
(circular-range 10 -5 2)
=> (10 -3 -1 1 3 5 7 9 -4 -2 0 2 4 6 8)
(circular-range 10 50 13)
=> (10 23 36 49 22 35 48 21 34 47 20 33 46 19 32 45 18 31 44 17 30 43 16 29 42 15
28 41 14 27 40 13 26 39 12 25 38 11 24 37 50)
(circular-range 30 35 -2)
=> (30 33 31 34 32)
(circular-range 30 30 -5)
=> (30)