;;; shell-sort.lisp
;;; Чисто функциональная реализация сортировки Шелла для списков
;;; (без SET/SETQ/SETF и без индексации), последовательность шагов Р. Седжвика.
(defun list-length (l)
(if (null l) 0 (1+ (list-length (cdr l)))))
;; Формула шагов по Р. Седжвику:
;; для k чётного: g(k) = 1 + 9*2^k - 9*2^(k/2)
;; для k нечётного: g(k) = 1 + 8*2^k - 6*2^((k+1)/2)
(defun sedgewick-gap (k)
(if (evenp k)
(+ 1 (- (* 9 (expt 2 k)) (* 9 (expt 2 (floor k 2)))))
(+ 1 (- (* 8 (expt 2 k)) (* 6 (expt 2 (floor (+ k 1) 2)))))))
;; Генерация всех шагов < n (в порядке убывания — большие шаги сначала)
(defun sedgewick-gaps (n)
(labels ((gen (k acc)
(let ((g (sedgewick-gap k)))
(if (>= g n) acc (gen (1+ k) (cons g acc))))))
(gen 0 nil)))
;; Создать список из k пустых списков: (nil nil ...)
(defun make-nils (k)
(if (zerop k) nil (cons nil (make-nils (1- k)))))
;; Добавить элемент x в pos-ю подпоследовательность (без мутаций)
(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 на "gap" подпоследовательностей:
;; i-я подпоследовательность содержит элементы на позициях i, i+gap, i+2*gap, ...
(defun split-by-gap (lst gap)
(labels ((split-rec (lst pos acc)
(if (null lst)
acc
(split-rec (cdr lst)
(if (= pos (1- gap)) 0 (1+ pos))
(add-to-pos acc pos (car lst))))))
(split-rec lst 0 (make-nils gap))))
;; Вставка элемента в отсортированный список (возврат нового списка)
(defun insert-into-sorted (x sorted)
(if (or (null sorted) (< x (car sorted)))
(cons x sorted)
(cons (car sorted) (insert-into-sorted x (cdr sorted)))))
;; Рекурсивная сортировка вставками
(defun insertion-sort (lst)
(if (null lst) nil
(insert-into-sorted (car lst) (insertion-sort (cdr lst)))))
;; Рекурсивный map
(defun map-rec (f lst)
(if (null lst) nil (cons (funcall f (car lst)) (map-rec f (cdr lst)))))
(defun all-empty-p (slist)
(if (null slist) t
(if (null (car slist)) (all-empty-p (cdr slist)) nil)))
(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)))
(defun shell-sort-by-gaps (lst gaps)
(if (null gaps) lst
(let ((g (car gaps)))
(shell-sort-by-gaps
(interleave (map-rec #'insertion-sort (split-by-gap lst g)))
(cdr gaps)))))
(defun shell-sort (lst)
"Чисто функциональная сортировка Шелла для списка LIST (по возрастанию)."
(let ((n (list-length lst)))
(shell-sort-by-gaps lst (sedgewick-gaps n))))
;;; -----------------------------
;;; Примеры использования
;;; -----------------------------
(defun run-shell-sort-examples ()
"Показать примеры работы shell-sort. Возвращает NIL после вывода."
(format t "~%Shell-sort examples:~%~%")
(format t "1) ~S -> ~S~%" '(3 1 4 1 5 9 2 6 5) (shell-sort '(3 1 4 1 5 9 2 6 5)))
(format t "2) ~S -> ~S~%" '(10 9 8 7 6 5 4 3 2 1) (shell-sort '(10 9 8 7 6 5 4 3 2 1)))
(format t "3) ~S -> ~S~%" '() (shell-sort '()))
(format t "4) ~S -> ~S~%" '(42) (shell-sort '(42)))
(format t "5) ~S -> ~S~%" '(5 3 5 3 2 1 4) (shell-sort '(5 3 5 3 2 1 4)))
nil)