Shammer's Philosophy

My private adversaria

要素から最大/最小のものを取得したい

Lisp には、渡されたパラメータの中から最大のものを返すmaxと最小のものを返す min という関数がある。

? (max 10 11 12 13 14 15)
15
? (min 10 11 12 13 14 15)
10
? 

しかし、これは引数がlistになると動作しない。

? (max '(10 11 12 13 14 15))
> Error: The value (10 11 12 13 14 15) is not of the expected type REAL.
> While executing: MAX, in process Listener(4).
> Type cmd-. to abort, cmd-\ for a list of available restarts.
> Type :? for other options.
1 > q
? (min '(10 11 12 13 14 15))
> Error: The value (10 11 12 13 14 15) is not of the expected type REAL.
> While executing: MIN, in process Listener(4).
> Type cmd-. to abort, cmd-\ for a list of available restarts.
> Type :? for other options.
1 > q
? 

なんか設計間違えてないか?と思うが。Common Lisp HyperSpec (TM)の中から、該当しそうなものを探してみたが特定できなかった(あるのかもしれないが見つけられなかった)ので書いてみた。

(defun max-number (l)
  (when (listp l)
    (let ((x (car l)))
      (dolist (n (cdr l))
	(setf x (max x n)))
      x)))

(defun min-number (l)
  (when (listp l)
    (let ((x (car l)))
      (dolist (n (cdr l))
	(setf x (min x n)))
      x)))

ここから dolist を排除。

(defun max-number (l)
  (labels ((comp (n l)
		 (if (null l)
		     n
		   (comp (max n (car l)) (cdr l)))))
    (when (listp l)
      (comp (car l) (cdr l)))))

(defun min-number (l)
  (labels ((comp (n l)
		 (if (null l)
		     n
		   (comp (min n (car l)) (cdr l)))))
    (when (listp l)
      (comp (car l) (cdr l)))))

これは両方似ているので重複分をまとめる。

(defun select-if (l condition)
  (labels ((compare (n l)
		    (if (null l)
			n
		      (compare (funcall condition n (car l)) (cdr l)))))
    (when (listp l)
      (compare (car l) (cdr l)))))

(defun max-number (l)
  (select-if l #'max))

(defun min-number (l)
  (select-if l #'min))

以下、実行結果。想定通りに動作していそうだ。

? (let ((n (get-random-list 10)))
  (format t "Original List is ~A, Min is ~A, Max is ~A~%" n (min-number n) (max-number n)))

Original List is (78 27 80 61 70 49 17 32 26 60), Min is 17, Max is 80
NIL
? (let ((n (get-random-list 10)))
  (format t "Original List is ~A, Min is ~A, Max is ~A~%" n (min-number n) (max-number n)))

Original List is (30 67 25 100 6 29 13 32 80 75), Min is 6, Max is 100
NIL
? (let ((n (get-random-list 10)))
  (format t "Original List is ~A, Min is ~A, Max is ~A~%" n (min-number n) (max-number n)))

Original List is (91 5 93 83 35 38 81 84 30 4), Min is 4, Max is 93
NIL
?