P28: リストの長さで並び替え

サブリストの長さで並び替える。
さらに、サブリストの長さの頻度で並び替える。

まずは、sort関数を定義。

(defun my-sort (lis smaller-p &key (key #'identity))
  (if (null lis)
      nil
    (do ((x (car lis))
	 (bigger '())
	 (smaller '())
	 (l (cdr lis) (cdr l)))
	((null l)
	 (append (my-sort smaller smaller-p :key key)
		 (list x)
		 (my-sort bigger smaller-p :key key)))
      (if (funcall smaller-p (funcall key x) (funcall key (car l)))
	  (push (car l) bigger)
	(push (car l) smaller)))))

(my-sort '(1 -5 2 -9 3) #'<)
;; => (-9 -5 1 2 3)
(my-sort '(1 -5 2 -9 3) #'< :key #'abs)
;; => (1 2 3 -5 -9)

P28-a はkeyを長さにして昇順に並び替えるだけ。

;; P28 a
(defun sort-by-length (lis)
  (my-sort lis #'< :key #'length))

(sort-by-length '((a b) (c d e) (f) (g h) (i j k l) (g h) (m)))
;; => ((M) (F) (A B) (G H) (G H) (C D E) (I J K L))

P28-bを解くために、補助関数を定義。
collect-and-frequencyは、要素をget-keyが返すkey毎にまとめ、keyの出現回数を計上する。

(defun collect-and-frequency (lis get-key)
  (if (null lis)
      nil
    (do ((table (make-hash-table))
	 (l lis (cdr l))
	 (acc '()))
	((null l)
	 (maphash (lambda (key val)
		    (push (list key (length val) val) acc)) table)
	 acc)
      (push (car l) (gethash (funcall get-key (car l)) table)))))

(collect-and-frequency '(a b c d 1 2 3 4 "a" "b" "c" "d") #'class-of)
;; => ((#<BUILT-IN-CLASS SB-KERNEL::SIMPLE-CHARACTER-STRING> 4 ("d" "c" "b" "a"))
;;    (#<BUILT-IN-CLASS FIXNUM> 4 (4 3 2 1))
;;    (#<BUILT-IN-CLASS SYMBOL> 4 (D C B A)))

(collect-and-frequency '((a b c) (d e) (f g h) (d e) (i j k l) (m n) (o)) #'length)
;; => ((1 1 ((O))) (4 1 ((I J K L))) (2 3 ((M N) (D E) (D E)))
;;     (3 2 ((F G H) (A B C))))

collect-and-frequencyを使って、リストの長さの出現回数順に並べ替える関数を定義する。

;; P28 b
(defun sort-by-frequency (lis)
  (apply #'append
	 (mapcar (lambda (entry)
		   (third entry))
		 (my-sort (collect-and-frequency lis #'length) #'< :key #'second))))

(sort-by-frequency '((a b c) (d e) (f g h) (d e) (i j k l) (m n) (o)))
;; => ((I J K L) (O) (F G H) (A B C) (M N) (D E) (D E))

お、これでList系の問題は完了の模様。
次はP31から、算術系の問題。
# あれ?P29,P30は?