3
\$\begingroup\$

My implementation is based on the incremental prime sieve described on Wikipedia. I'm relatively new to lisp so I would appreciate any comments on my coding style or on the algorithm.

The below code implements a min-heap and uses it in the prime number generation algorithm:

(defstruct heap
  ; (cmp x y) should return true if x is less than y.
  (cmp '())
  (arr '())
  (size 0))


(defun resize (heap)
  (let* ((old-arr (heap-arr heap))
         (old-cap (length old-arr))
         (new-cap (max 10 (* 2 old-cap)))
         (new-arr (make-array (list new-cap))))
    (loop for i from 0 to (- old-cap 1)
          do (setf (aref new-arr i) (aref old-arr i)))
    (setf (heap-arr heap) new-arr)))


(defun insert (x heap)
  (let ((size (heap-size heap))
        (cmp (heap-cmp heap)))
    (when (= size (length (heap-arr heap)))
      (resize heap))
    (setf (aref (heap-arr heap) size) x)
    (setf (heap-size heap) (+ 1 size))
    (loop with arr = (heap-arr heap)
          for i = size then parent
          for parent = (floor (- i 1) 2)
          while (and (> i 0)
                     (funcall cmp x (aref arr parent)))
          do (setf (aref arr i) (aref arr parent))
             (setf (aref arr parent) x))))


(defun peek (heap)
  (aref (heap-arr heap) 0))


(defun extract (heap)
  (let* ((arr (heap-arr heap))
         (cmp (heap-cmp heap))
         (size (heap-size heap))
         (x (aref arr 0)))
    (setf (heap-size heap) (- size 1))
    (when (> size 1)
      (setf (aref arr 0) (aref arr (- size 1)))
      (loop for i = 0 then small-child-idx
            for child1-idx = (+ 1 (* 2 i))
            and child2-idx = (+ 2 (* 2 i))
            for small-child-idx = (if (and (< child2-idx (heap-size heap))
                                           (funcall cmp (aref arr child2-idx)
                                                        (aref arr child1-idx)))
                                    child2-idx
                                    child1-idx)
            while (and (< child1-idx (heap-size heap))
                       (funcall cmp (aref arr small-child-idx) (aref arr i)))
            do (let ((tmp (aref arr i)))
                 (setf (aref arr i) (aref arr small-child-idx))
                 (setf (aref arr small-child-idx) tmp))))
    x))


(defun primes ()
  (format t "3~%")
  (loop with multiples = (make-heap :cmp (lambda (x y) (< (car x) (car y))))
        initially (insert '(9 . 3) multiples)
        for n from 5 by 2
        if (< n (car (peek multiples)))
          do (format t "~d~%" n)
             (insert (cons (* n n) n) multiples)
        else
          do (loop while (= n (car (peek multiples)))
                   do (let* ((pair (extract multiples))
                             (m (car pair))
                             (p (cdr pair)))
                        (insert (cons (+ m (* 2 p)) p) multiples)))))


(primes)
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Nit: 1- and 1+ exist. \$\endgroup\$
    – Ehvince
    Commented Mar 4, 2022 at 0:22

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.