Рекурсивный диапазон в Lisp добавляет период?

(define ..
  (lambda (start stop)
    (cond ((> (add1 start) stop) (quote ()))
          ((eq? (add1 start) stop) (sub1 stop))
          (else (cons start (.. (add1 start) stop))))))

Я определил простую функцию диапазона. Цель состоит в том, чтобы

(.. 1 5)  -->  (1 2 3 4)

Вместо этого в мой кортеж добавляется странный период, и я понятия не имею, почему:

(.. 1 5)  -->  (1 2 3 . 4)

Я не понимаю, почему это происходит. Любая помощь приветствуется

Ответ 1

Список в Схеме - это либо пустой список () (также известный как nil в некоторых Lisps), либо ячейка cons, чей car (также известный как first) является элементом списка и чей cdr (также известный как rest) является либо остальным списком (т.е. другим списком), либо атомом, который завершает список. Обычный терминатор - пустой список (); списки, заканчивающиеся на (), называются "правильными списками". Списки, завершенные любым другим атомом, называются "неправильными списками". Список (1 2 3 4 5) содержит элементы 1, 2, 3, 4 и 5 и заканчивается на (). Вы можете построить его на

(cons 1 (cons 2 (cons 3 (cons 4 (cons 5 ())))))

Теперь, когда система печатает ячейку cons, общий случай заключается в ее печати на

(car . cdr)

Например, результат (cons 1 2) печатается как

(1 . 2)

Поскольку списки построены из cons cons, вы также можете использовать это обозначение для списков:

'(1 2 3 4 5) ==
'(1 . (2 . (3 . (4 . (5 . ())))))

Это довольно неуклюжий, так что большинство lisps (все, что я знаю) имеют особый случай для печати cons-ячеек: если cdr - это список (либо другая ячейка cons, или ()), 'напечатайте . и не печатайте окружную скобку cdr (которую в противном случае она имела бы, так как это список). Итак, если вы видите результат, например

(1 2 3 . 4)

это означает, что у вас есть неправильный список, который завершается атомом 4. Он имеет структуру

(1 . (2 . (3 . 4)))

Теперь встает вопрос: где в вашем коде построена конструкция списка? .. всегда должен возвращать правильный список, поэтому рассмотрим случаи: первый случай всегда возвращает правильный список (пустой список):

((> (add1 start) stop) (quote ()))

Второй случай выглядит так, что он может возвращать то, что не является списком (при условии, что (sub1 stop) == (- stop 1)):

((eq? (add1 start) stop) (sub1 stop))

Теперь, если .. работает правильно, тогда третий случай всегда будет возвращать правильный список (так как (cons x y) является правильным списком, если y is):

(else (cons start (.. (add1 start) stop)))

Сделайте свой второй случай возвратом списка, и все должно быть установлено.

Ответ 2

Ваше выражение (sub1 stop) должно читать (list (sub1 stop))

Чтобы cons создал правильный список, второй элемент должен быть самим списком. Таким образом, ваша функция .. должна возвращать список какого-либо типа для каждого предложения cond.

Ответ 3

Удалите эту часть cond

 ((eq? (add1 start) stop) (sub1 stop))

Это вызывает преждевременное завершение.