Yet Another Common Lisp Problems #5 take

●問題5

リスト xs の先頭から n 個の要素を取り出す関数 take xs n を定義してください。

> (take '(a b c d e) 3)
(a b c)
> (take '(a b c d e) 0)
()
> (take '(a b c d e) 6)
(a b c d e)

解答

ホームページ移転のお知らせ - Yahoo!ジオシティーズ

無駄に色んな書き方をしてみた。

;; 再帰で
(defun take-rec (xs n)
  (if (or (zerop n) (null xs))
    nil
    (cons (car xs) (take%0 (cdr xs) (1- n)))))

;; subseq は cheat な気分
(defun take-subseq#1 (xs n)
  (subseq xs 0 (min n (length xs))))

(defun take-subseq#2 (xs n)
  (if (>= n (length xs))
    xs
    (subseq xs 0 n)))

;; loop で #1
(require "cmu_loop")

(defun take-loop#1 (xs n)
  (loop for n from n above 0
        for x = xs then (cdr x)
    while x
    collect (car x)))

;; loop で #2
(defun take-loop#2 (xs n)
  (loop repeat n
        for x on xs ; for VAR on LIST で cdr を歩けるらしい
    while x
    collect (car x)))

;; do で
(defun take-do (xs n)
  (do ((xs xs (cdr xs))
       (n n (1- n))
       (acc nil (cons (car xs) acc)))
      ((or (zerop n) (null xs))
       (nreverse acc))))

;; mapcar + values + make-list
(defun take-mapcar (xs n)
  (mapcar #'values xs (make-list n)))

再帰か subseq が好きかな。mapcar は見た目はスッキリしてるけど、読んだとき (?_?) ってなりそう。