演習1.1

名前の末尾に「Jr.」や「MD」というサフィックスが付いているときに、それを無視してラストネームを抽出するプログラムを書け、というもの。

;;; Exercise 1.1

(defparameter *suffixes*
  '(MD Jr Sr II III)
  "A list of suffixes that can appear at the end of a name.")

(defun last-name (name)
  "Select the last name from a name represented as a list."
  (if (member (first (last name)) *suffixes*)
      (last-name (reverse (rest (reverse name))))
      (first (last name))))

(mapcar #'last-name '((Rex Morgan MD) (Morton Downey Jr)))

文中のfirst-nameを参考にして単純にやってみたけど、reverseが何回も出てくるのが気持ち悪い。リストの最後から順番に見る良い方法はないだろうか。

リストの前方から見ていって、「見ている要素の次の要素が*suffixes*に含まれていたら、いま見ている要素がラストネーム」というやり方もあるなぁ、と書き直したのが以下。

(defun last-name (name)
  "Select the last name from a name represented as a list."
  (if (or (null (rest name)) (member (first (rest name)) *suffixes*))
      (first name)
      (last-name (rest name))))

(null (rest name))をチェックしないと、(last-name '(first last))というサフィックス無しの名前を入れたら無限ループに陥ってびっくりしますよ。他にも何かバグが潜んでいそうな気がするプログラムです。

そのほか考えられるケースも想定してプログラムを適宜変更せよ、という問題なので、(First Last, Ph.D.)という場合を試してみたら、「クォートされたリスト中にコンマは入れられませんよ」とエラーが出ましたとさ。