ANSI Common Lisp入門 1

春休みなので、Lispの勉強を始めました。Lispそのものを理解できれば良いですが、ひとまずJSのスキルアップにつなげるのが目標です。

教科書

ANSI Common Lisp (スタンダードテキスト)

ANSI Common Lisp (スタンダードテキスト)

2.Lispの世界へようこそ 練習問題

1.

以下の式が評価されたときに何が起こるか説明せよ。

(x -> yは「xが評価されてyになる」の略)

* (+ (- 5 1) (+ 3 7))
; 5 -> 5, 1 -> 1, 3 -> 3, 7 -> 7
; (- 5 1) -> 4, (+ 3 7) -> 10
; (+ 4 10) -> 14
14

* (list 1 (+ 2 3))
; 2 -> 2, 3 -> 3
; 1 -> 1, (+ 2 3) -> 5
; (list 1 5) -> (1 5)
(1 5)

* (if (listp 1) (+ 1 2) (+ 3 4))
; 1 -> 1
; (listp 1) -> nil
; 3 -> 3, 4 -> 4
; (+ 3 4) -> 7
7

* (list (and (listp 3) t) (+ 1 2))
; 3 -> 3, 1 -> 1, 2 -> 2
; (listp 3) -> nil
; (and (listp 3) t) -> nil, (+ 1 2) -> 3
; (list nil 3) -> (nil 3)
(NIL 3)
2.

(a b c)を返すcons式を3通り示せ。

* (cons 'a '(b c))
(A B C)

* (cons 'a (cons 'b '(c)))
(A B C)

* (cons 'a (cons 'b (cons 'c ())))
(A B C)
3.

carとcdrを使って、リストの4番目の要素を返す関数を定義せよ。

* (defun my-fourth (lst)
    (car (cdr (cdr (cdr lst)))))

* (my-fourth '(1 2 3 4 5 6))
4

* (my-fourth '(a b c d e))
D
4.

2つの数を引数として、大きい方を返す関数を定義せよ

* (defun my-max (x y)
    (if (> x y)
        x
        y))

* (my-max 4 9)
9
5.

以下の関数は何をするものであるか

; 引数として渡されたリストxの要素にnilがあればtを返す。
(defun enigma (x)
  (and (not (null x))
       (or (null (car x))
           (enigma (cdr x)))))



; リストy内のxの位置(0始まり)を返す。
; リストyが空、xが含まれないならnilを返す。
(defun mystery (x y)
  (if (null y)
      nil
      (if (eql (car y) x)
          0
          (let ((z (mystery x (cdr r))))
            (and z (+ z 1))))))
6.

以下のそれぞれの応答でxは何をしているか。(xをどんなオペレータで置換すればよいだろうか)

; x -> car
* (car (x (cdr '(a (b c) d))))
B

; x -> or
* (x 13 (/ 1 0))
13

; x -> apply
* (x #'list 1 nil)
(1)
7.

2章で導入したオペレータだけを使い、1つのリストを引数とし、そのリストの要素にリストが含まれる場合に真とする関数を定義する。

* (defun hasList (lst)
    (if lst
        (or (listp (car lst))
            (hasList (cdr lst))))
)

* (hasList '(a b '(c d) e f))
T

* (hasList '(a b c d))
NIL

* (defun hasList (lst)
    (if lst
      (if (listp (car lst))
          t
          (hasList (cdr lst)))))

* (hasList '(a b '(c d) e f))
T

* (hasList '(a b c d))
NIL
8.

以下の様な関数を反復と再帰の2通りの方法で定義する。

a) 正の整数を引数とし、その数のドット(ピリオド)を表示する。
b) 1つのリストを引数とし、aというシンボルがいくつあるかを返す。

; a) 再帰
* (defun dot-rec (n)
    (if (> n 0)
        (progn
          (format t ".")
          (dot-rec (- n 1)))))

DOT-REC
* (dot-rec 3)
...
NIL

* (dot-rec 5)
.....
NIL

; a) 反復
* (defun dot-rep (n)
     (do ((i 0 (+ i 1))) ((= i n))
       (format t ".")))

* (dot-rep 3)
...
NIL

* (dot-rep 5)
.....
NIL


; b) 再帰
* (defun hasA-rec (lst)
    (if lst
        (+ (if (eq (car lst) 'a) 1 0) (hasA-rec (cdr lst)))
        0))

HASA-REC

* (hasA-rec '(a b c d a))
2

* (hasA-rec '(a a a b c))
3

* (hasA-rec '(b c d))
0

; b) 反復
* (defun hasA-rep (lst)
    (do ((cplst lst (cdr cplst))
         (n 0 (+ n (if (eq (car cplst) 'a) 1 0))))
        ((not cplst) n)))

HASA-REP

* (hasA-rep '(a b c d))
1

* (hasA-rep '(a b c d a))
2

* (hasA-rep '(a a a b c))
3

* (hasA-rep '(b c d))
0
9.

リスト中のnilでない要素の合計を返す関数について、以下の関数を間違っている点を直す。

; (a)
(defun summit (lst)
  (remove nil lst)
  (apply #'+ lst))

;>>>>>
(defun summit (lst)
  (apply #'+ (remove nil lst)))

; (b)
(defun summit (lst)
  (let ((x (car lst)))
    (if (null x)
        (summit (cdr (lst))
        (+ x (summit (cdr lst)))))))
;>>>>>
(defun summit (lst)
     (if lst
        (+ (or (car lst) 0) (summit (cdr lst)))
       0))

doマクロの書式がどうも馴染めない。。繰り返しは再帰の方が楽な気がしました。
とりあえず、ハイライトしてくれるエディタを使っていれば、括弧の多さは思っているよりも気にならない事がわかった。