Yet Another Common Lisp Problems #6 drop

再開。

●問題6

リスト xs の先頭から n 個の要素を取り除く関数 drop xs n を定義してください。なお、Common Lisp には同等の機能を持つ関数 nthcdr があります。

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

解答

ホームページ移転のお知らせ - Yahoo!ジオシティーズ
;; 再帰で
(defun drop (xs n)
  (if (<= n 0)
    xs
    (drop (cdr xs) (1- n))))
=> drop

(test-drop)
drop: 3 assertions passed, 0 failed.

;; do で
(defun drop (xs n)
  (do ((xs xs (cdr xs))
       (n  n  (1- n)))
      ((<= n 0) xs)))
=> drop

(test-drop)
drop: 3 assertions passed, 0 failed.

;; loop で
(require "cmu_loop")
=> t

(defun drop (xs n)
  (loop repeat n
        for x on xs
    finally (return x)))
=> drop

(test-drop)
drop: 3 assertions passed, 0 failed.

答え合わせ

●解答6

リスト : リストの先頭から n 個の要素を削除する

(defun drop (ls n)
  (if (or (<= n 0) (null ls))
      ls
    (drop (cdr ls) (1- n))))

; 別解
(defun drop1 (ls n)
  (do ((n n (1- n))
       (ls ls (cdr ls)))
      ((or (<= n 0) (null ls)) ls)))

drop は簡単です。引数 n が 0 以下または引数 ls が空リストになるまで drop再帰呼び出しするだけです。別解は do による繰り返しバージョンです。

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

ls が nil になったらそこで止めていいのに気付いてなかった。

余談: lisp-unit を使ってみた

(require "lisp-unit")
=> t

(use-package :lisp-unit)
=> t

(define-test drop
  (assert-equal '(d e)
      (drop '(a b c d e) 3))
  (assert-equal '(a b c d e)
      (drop '(a b c d e) 0))
  (assert-equal '()
      (drop '(a b c d e) 6)))
=> drop

(defun test-drop ()
  (let ((*trace-output* *standard-output*))
    (run-tests 'drop)))
=> test-drop

(test-drop)
    drop: 関数が定義されていません: drop

;; ちゃんと定義して...
(test-drop)
drop: 3 assertions passed, 0 failed.