;;; shell-sort-functional-with-sedgewick.lisp
;;; Чисто функциональная сортировка Шелла для списков с последовательностью Седжвика.
;;; Нет setf/setq/loop/nth/length — только car/cdr/cons/recurse.
(defun list-length (l)
(if (null l) 0 (1+ (list-length (cdr l)))))
(defun sedgewick-sequence (n)
"Генерация последовательности Седжвика (вариант по вашей формуле) для n.
Возвращает список шагов в порядке убывания (большие шаги позже можно обработать по порядку)."
(labels ((sedgewick-iter (k seq)
(let ((next (+ (* 4 (expt 2 k))
(* 3 (expt 2 (1- k)))
1)))
(if (> next n)
(reverse seq)
(sedgewick-iter (1+ k) (cons next seq))))))
(if (<= n 1)
'(1)
(sedgewick-iter 1 '(1)))))
;;; Создать список из k пустых списков
(defun make-nils (k)
(if (zerop k) nil (cons nil (make-nils (1- k)))))
;;; Добавить x в pos-ю позицию списка списков acc (чисто функционально)
(defun add-to-pos (acc pos x)
(if (zerop pos)
(cons (append (car acc) (list x)) (cdr acc))
(cons (car acc) (add-to-pos (cdr acc) (1- pos) x))))
;;; Развести lst на `step` подпоследовательностей (i-я содержит элементы i, i+step, ...)
(defun split-by-step (lst step)
(labels ((split-rec (lst pos acc)
(if (null lst)
acc
(split-rec (cdr lst)
(if (= pos (1- step)) 0 (1+ pos))
(add-to-pos acc pos (car lst))))))
(split-rec lst 0 (make-nils step))))
;;; Вставка с пользовательским компаратором
(defun insert-into-sorted-cmp (x sorted cmp)
(if (or (null sorted) (funcall cmp x (car sorted)))
(cons x sorted)
(cons (car sorted) (insert-into-sorted-cmp x (cdr sorted) cmp))))
(defun insertion-sort-cmp (lst cmp)
(if (null lst) nil
(insert-into-sorted-cmp (car lst) (insertion-sort-cmp (cdr lst) cmp) cmp)))
;;; Рекурсивный map
(defun map-rec (fn lst)
(if (null lst) nil
(cons (funcall fn (car lst)) (map-rec fn (cdr lst)))))
;;; Проверка, что все списки пусты
(defun all-empty-p (slist)
(if (null slist) t
(and (null (car slist)) (all-empty-p (cdr slist)))))
;;; Взять головы всех непустых списков последовательностей (по порядку),
;;; пропуская пустые
(defun take-heads-once (slist)
(if (null slist) nil
(let ((h (car slist)))
(if (null h)
(take-heads-once (cdr slist))
(cons (car h) (take-heads-once (cdr slist)))))))
;;; Получить хвосты всех подпоследовательностей
(defun tails (slist)
(if (null slist) nil
(let ((h (car slist)))
(cons (if (null h) nil (cdr h)) (tails (cdr slist))))))
;;; Интерлив: собрать список обратно, циклически беря первые элементы из подпоследовательностей
(defun interleave (slist)
(labels ((inter-rec (slist)
(if (all-empty-p slist) nil
(append (take-heads-once slist)
(inter-rec (tails slist))))))
(inter-rec slist)))
;;; Один шаг сортировки: разбить по step, отсортировать каждую подпоследовательность вставками с cmp,
;;; затем собрать обратно
(defun insertion-sort-step (lst step cmp)
(interleave (map-rec (lambda (sub) (insertion-sort-cmp sub cmp))
(split-by-step lst step))))
;;; Сортировка с заданной последовательностью шагов (список steps),
;;; обрабатываем шаги от больших к меньшим (если нужно — передайте в нужном порядке)
(defun shell-sort-with-steps (lst steps cmp)
(if (null steps) lst
(shell-sort-with-steps
(insertion-sort-step lst (car steps) cmp)
(cdr steps)
cmp)))
(defun shell-sort (lst cmp)
"Сортировка Шелла для списка LIST с компаратором CMP.
CMP — функция двух аргументов (a b) -> T если a должно быть перед b."
(let ((n (list-length lst)))
(if (<= n 1) lst
(let ((steps (sedgewick-sequence n)))
(shell-sort-with-steps lst steps cmp)))))
;;; -----------------------------
;;; Примеры использования
;;; -----------------------------
(defun show-sedgewick-sequence (n)
(format t "Последовательность Седжвика для n=~S: ~S~%" n (sedgewick-sequence n)))
(defun run-shell-sort-examples ()
(format t "~%=== Shell-sort: примеры ===~%~%")
;; Числа по возрастанию
(let ((numbers '(5 2 8 1 9 4 0 7 3 6)))
(format t "Original numbers: ~S~%" numbers)
(format t "Sorted numbers (asc): ~S~%" (shell-sort numbers #'<)))
;; Строки по алфавиту
(let ((strings '("banana" "apple" "cherry" "date" "fig")))
(format t "~%Original strings: ~S~%" strings)
(format t "Sorted strings (string<): ~S~%" (shell-sort strings #'string<)))
;; Числа по убыванию
(let ((numbers2 '(5 2 8 1 9 4)))
(format t "~%Original numbers2: ~S~%" numbers2)
(format t "Sorted numbers2 (desc): ~S~%" (shell-sort numbers2 #'>)))
;; Сравнение по длине строки
(let ((strings2 '("a" "bb" "ccc" "d" "eeeee")))
(format t "~%Original strings2: ~S~%" strings2)
(format t "Sorted by length: ~S~%" (shell-sort strings2 (lambda (a b) (< (length a) (length b))))))
;; Пустой и единичный
(format t "~%Empty list -> ~S~%" (shell-sort '() #'<))
(format t "Single -> ~S~%" (shell-sort '(42) #'<))
;; Показать последовательности Седжвика для разных n
(show-sedgewick-sequence 10)
(show-sedgewick-sequence 50)
(show-sedgewick-sequence 100)
;; Структуры: точки по координате x
(defstruct point x y)
(let ((points (list (make-point :x 3 :y 5)
(make-point :x 1 :y 2)
(make-point :x 4 :y 7)
(make-point :x 2 :y 1))))
(format t "~%Original points: ~S~%" points)
(format t "Sorted points by x: ~S~%" (shell-sort points (lambda (p1 p2) (< (point-x p1) (point-x p2))))))
(format t "~%=== Конец примеров ===~%")
nil)
(eval-when (:load-toplevel :execute)
(run-shell-sort-examples))