Search code examples
listsortinglispcommon-lisp

Sort a list by number and char


I have the list in LISP:

((1 b) (1 a) (2 D) (1 z) (1 t) (2 a) (3 n))

I have to order it first on the number and if equal on the lexichographic order of the char, the output should be:

((1 a) (1 b) (1 t) (1 z) (2 a) (2 d) (3 n))

I have tried to sort on one parameter and then the other, how can I compose the two function ?

;;(sort '((1 b) (1 a) (2 D) (1 z) (1 t) (2 a) (3 n)) #'< :key #'car )
;;(sort '((1 b) (1 a) (2 D) (1 z) (1 t) (2 a) (3 n)) #'string< :key #'second )

Are there easier ways to do it?

Thanks


Solution

  • As coredump says, since you really need a list comparison function, a nice approach is do the meta thing: don't write one, but write a function which makes functions which compare lists. Here is such a function:

    (defun make-list-comparator (&rest predicates)
      (labels ((tails< (l1t l2t pt)
                 (let ((< (first pt))
                       (e1 (first l1t))
                       (e2 (first l2t)))
                   (cond
                    ((funcall < e1 e2) t)
                    ((funcall < e2 e1) nil)
                    ((and (null l1t) (null l2t) (null pt)) nil)
                    ((or (null l1t) (null l2t) (null pt))
                     (error "crashed into the end"))
                    (t (tails< (rest l1t) (rest l2t) (rest pt)))))))
        (lambda (l1 l2)
          (tails< l1 l2 predicates))))
    

    And now

    > (sort (copy-list '((1 b) (1 a) (2 D) (1 z) (1 t) (2 a) (1)))
            (make-list-comparator
             #'<
             (lambda (s1 s2)
               (string< (string s1) (string s2)))))
    ((1 a) (1 b) (1 t) (1 z) (2 a) (2 d) (3 n))