misc:software:Common Lisp:PAIP:3章

misc:software:Common Lisp:PAIP:3章

演習

前の章より

演習3.1

(let* ((x 6)
       (y (* x x)))
  (+ x y))
; => 42

((lambda (x)
   ((lambda (y)
      (+ x y))
    (* x x)))
 6)
; => 42

演習3.2


(setf y '(1 2 3))

(list* 0 y)
; => (0 1 2 3)
(cons 0 y)
; => (0 1 2 3)


(list* y 4)
; => ((1 2 3) . 4)
(cons y 4)
; => ((1 2 3) . 4)


(list* y 4 5)
; => ((1 2 3) 4 . 5)
(cons y 4 5)
; => Evaluation aborted on #<SB-INT:SIMPLE-PROGRAM-ERROR "invalid number of arguments: ~S" {100427BF53}>

演習3.3

(defun dprint (x)
  (cond
    ((atom x) (princ x))
    (t 
     (princ "(")
     (dprint (first x))
     (pr-rest (rest x))
     (princ ")"))))

(defun pr-rest (x)
  (princ " . ")
  (dprint x))

(dprint '(a b))
; => (A . (B . NIL))

(dprint '(a b c d))
; => (A . (B . (C . (D . NIL))))

演習3.4

演習3.3のdprint関数を用い、pr-rest関数を再定義する。

(defun pr-rest (x)
  ;; (null x) の返り値として nil を明示的に指定する
  (cond ((null x) nil)
        ((atom x)
         (princ " . ")
         (princ x))
        (t (princ " ")
           (dprint (first x))
           (pr-rest (rest x)))))

(dprint '(a b))
; => (A B)
 
(dprint '(a . b))
; => (A . B)

(dprint '(a b c d))
; => (A B C D)

(dprint '(a b c . d))
; => (A B C . D)

演習3.5

(defstruct node
  name
  (yes nil)
  (no nil))
  
(defvar *db*
  (make-node :name 'animal
             :yes (make-node
                   :name 'mammal)
             :no (make-node
                   :name 'vegetable
                   :no (make-node :name 'mineral))))
 
(setf *counter* 1)
(setf *limit* 20)
  
(defun questions (&optional (node *db*) (counter *counter*))
  (cond
    ((< *limit* counter)
     (format t "I have questioned ~A times, but I can't give the correct answer." *limit*)
     (setf (node-no node) (give-up)
           counter 1)
    (t
     (incf counter)
     (format t "~&Is it a ~A? " (node-name node))
     (case (read)
       ((y yes) (ask-next-question-or-get-answer node-yes node counter))
       ((n no) (ask-next-question-or-get-answer node-no node counter))
       (it 'aha!)
       (otherwise (format t "Reply with YES, NO, or IT if I have guessed it.")
                  (questions node counter))))))
  
(defmacro ask-next-question-or-get-answer (node-yes/no node count)
  `(if (not (null (,node-yes/no ,node)))
       (questions (,node-yes/no ,node) ,count)
       (setf (,node-yes/no ,node) (give-up))))
  
(defun give-up ()
  (format t "~&I give up - what is it? ")
  (make-node :name (read)))

演習3.6

(setf a 'global-a)
(defvar *b* 'global-b)

(defun fn () *b*)

(let ((a 'local-a)
      (*b* 'local-b))
  (list a *b* (fn) (symbol-value 'a) (symbol-value '*b*)))
; => (LOCAL-A LOCAL-B LOCAL-B GLOBAL-A LOCAL-B)

演習3.7

演習3.8

(defun find-all (item sequence &rest keyword-args
                 &key (test #'eql) test-not &allow-other-keys)
  "this function can be used with even KCL (Kyoto Common Lisp)."
  (if test-not
      (apply #'remove item sequence 
             :test-not (complement test-not)
             ;; (append keyword-args (list :test-not (complement test-not)))
             `(,@keyword-args :test-not ,(complement test-not)))
      (apply #'remove item sequence
             :test (complement test)
             ;; (append keyword-args (list :test (complement test)))
             `(,@keyword-args :test ,(complement test)))))
 
(setf nums '(1 2 3 2 1))
(find-all 1 nums :test #'= :key #'abs)
(defun remove-key-value (key list &optional (acc '()))
  (if (member key list)
      (let ((first (first list)))
        (if (and (keywordp first) (eql key first))
            (append (reverse acc) (remove-key-value key (cddr list)))
            (remove-key-value key (cdr list) (cons first acc))))
      list))

(defun find-all (item sequence &rest keyword-args
                 &key (test #'eql) test-not &allow-other-keys)
  "this function can be used with even KCL (Kyoto Common Lisp)."
  (let* ((my-test (cadr (member :test keyword-args)))
         (my-test-not (cadr (member :test-not keyword-args)))
         (test (if my-test my-test test))
         (test-not (if my-test-not my-test-not test-not)))
    (if test-not
        (apply #'remove item sequence
               :test-not (complement test-not) (remove-key-value :test-not keyword-args))
        (apply #'remove item sequence
               :test (complement test) (remove-key-value :test keyword-args)))))


(remove-key-value :test '(:foo 1 :test 2))
(remove-key-value :test '(:test 2 :piyo 3))
(remove-key-value :test '(:foo 1 :test 2 :piyo 3))
(remove-key-value :test '(:foo 1 :test 2 :piyo 3 :test 4 :bar 5))
(remove-key-value :testa '(:foo 1 :test 2 :piyo 3 :test 4 :bar 5))

(setf nums '(1 2 3 2 1))
(find-all 1 nums :test #'= :key #'abs)

演習3.9

(defun unique-plist (plist)
  (labels
      ((rec (list acc)
         (cond
           ((null list) acc)
           ((member (car list) acc :test #'equal)
            (rec (cddr list) acc))
           (t
            (rec (cddr list)
                 (append acc (list (car list) (cadr list))))))))
    (rec plist nil)))

(unique-plist '(:hoge foo :bar piyo :hoge bbb :bar aaa))
; => (:HOGE FOO :BAR PIYO)

(defun find-all-for-kcl (item sequence &rest keyword-args
                         &key (test #'eql) test-not &allow-other-keys)
  (if test-not
      (apply #'remove item sequence
             (unique-plist (append (list :test-not (complement test-not)) keyword-args)))
      (apply #'remove item sequence
             (unique-plist (append (list :test (complement test)) keyword-args)))))

演習3.10

(defun length-r (list)
  ;; y の値は利用しない
  (reduce (lambda (x y) (+ x 1))
          list
          ;; :initial-balue ではなくて :initial-value
          :initial-value 0))
(defun length-r (list)
  (reduce #'+ list
          ;; 以下のように :key を指定すれば
          ;; (mapcar #'(lambda (x) 1) list) と同じことを行う
          :key (lambda (x) 1)))

演習3.11

(describe 'lcm)
(describe 'nreconc)

演習3.12

「3.1 Lisp スタイルガイド」にてacons関数が説明されている。

(defparameter a-list (acons 'foo 'bar nil))
a-list
; => ((FOO . BAR))
(acons 'piyo 'hoge a-list)
; => ((PIYO . HOGE) (FOO . BAR))
a-list
; => ((FOO . BAR))

演習3.13

(defparameter words '(this is a test))

(format t "~a" words)
; => (THIS IS A TEST)
(format t "~{~a~}" words)
; => THISISATEST
(format t "~{~a~^ ~}." words)
; => THIS IS A TEST.
(format t "~@(~{~a~^ ~}.~)" words)
; => This is a test.

Last modified : 2014/07/05 00:22:35 JST