コンテンツにスキップ

Common Lisp

インストール

dockerで使う

1
docker run --rm -it -v ${PWD}$:/home/cl clfoundation/cl-devel:latest sbcl

roswell

Chromebook

GitHub公式の情報にしたがってソースからインストールする. 作業時はcd /tmpしよう. 途中でエラーが出たらros install quicklispを実行. EmacsでSLIMEが実行できない場合はros install slime.

Mac

1
2
3
4
brew install roswell
ros install quicklisp
ros install sbcl
ros install slime

Windows

scoopでインストールする以外にバイナリのインストールもある. バイナリでインストールして /path/to/roswell.roswell/lisp/quicklisp/binにパスを通し, ros setup, ros install slime sbcl-binを実行してsbclをインストールする. roswell-slime連繋は~/.roswell/helper.elを読み込む. .roswell/init.lispでREPL初期読み込みライブラリを設定できる.

必要に応じて以下インストールする.

  • ros install rove

MIT-Scheme

1
docker run -it --rm --name ms -p 10000:8888 --mount type=bind,source=$(pwd),destination=/work kkwok/jupyter-mit-scheme jupyter notebook --ip=0.0.0.0 --allow-root --NotebookApp.token=''

参考

HyperSpec

HyperSpec は LispWorks が提供している ANSI Common Lisp の仕様書. 検索時も「clhs restart-case」が便利. EmacsのewwでCommonLispのHyperSpecをHack!の方法が便利.

エラー処理用メモ

1
ros setup > err.txt 2>&1

高速化

ライブラリの調べ方

Quickdocs と CLiki で Common Lisp 製のライブラリの情報を目的別に調べられる.

  • Quickdocs: Common Lisp 製ライブラリのドキュメントサイト
  • CLiki: Common Lisp Foundation が運営している Common Lisp の Wiki

学習資料

A Road to Common Lisp 翻訳にある勉強順

  • [元記事の翻訳]](https://gist.github.com/y2q-actionman/49d7587912b2786eb68643afde6ca192)にある勉強の順.
  • Common Lisp: A Gentle Introduction to Symbolic Computation
  • Practical Common Lisp
    • 邦訳は 実践Common Lisp
    • https://gigamonkeys.com/book/
  • 言語仕様
    • 単純に Google で "clhs なんとか" と検索してもいい
  • Sketch を使って Coding Math videos の何かを実装してみる
    • https://github.com/vydd/sketch
    • https://www.youtube.com/user/codingmath/videos
  • Paradigms of Artificial Intelligence Programming
    • 邦訳は「実用Common Lisp」
  • Common Lisp Recipes
  • Richard Gabriel の Patterns of Software: 著者のサイトでPDF形式で入手できる
  • On Lisp
  • Let Over Lambda
  • Object-Oriented Programming in COMMON LISP: A Programmer's Guide to CLOS
  • The Art of the Metaobject Protocol
  • Land of Lisp

雑多なメモ

TIPS

クォートつきリストを返すときの注意

On LispのP.28にある注意. 下手な書き方をすると関数内のクォートつきリストを書き換えてしまう.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
(ql:quickload :rove)
(use-package :rove)

(testing "P.28, place to quote 1"
  (flet ((exclaim1 (expression)
           (append expression '(oh my))))
    (ok '(exclaim1 '(lions and tigers and bears))
        '(LIONS AND TIGERS AND BEARS OH MY))
    (ok '(nconc * '(goodness))
        '(LIONS AND TIGERS AND BEARS OH MY GOODNESS))
    (ok '(exclaim1 '(fixnums and bignums and floats))
        '(FIXNUMS AND BIGNUMS AND FLOATS OH MY GOODNESS))))

(testing "P.28, place to quote 2"
  (flet ((exclaim2 (expression)
           (append expression (list 'oh 'my))))
    (ok '(exclaim2 '(lions and tigers and bears))
        '(LIONS AND TIGERS AND BEARS OH MY))
    (ok '(nconc * '(goodness))
        '(LIONS AND TIGERS AND BEARS OH MY GOODNESS))
    (ok '(exclaim2 '(fixnums and bignums and floats))
        '(FIXNUMS AND BIGNUMS AND FLOATS OH MY))))

apply, funcall

1
2
3
4
5
6
7
;; 以下は等価
(+ 1 2)
(apply #'+ '(1 2))
(apply (symbol-function '+) '(1 2))
(apply #'(lambda (x y) (+ x y)) '(1 2))
(apply #'+ 1 '(2))
(funcall #'+ 1 2)

do, dolist, dotimes

do

1
2
3
(do (variable-definition*)
    (end-test-form result-form*)
  statement*)

dolist

1
2
;;; (dotimes (var limit result) S式)
(dolist (x '(0 1 2 3 4)) (print x))

dotimes

1
2
;;; (dolist (var init-form result) S式)
(dotimes (x 5) (print x))

例: フィボナッチ

1
2
3
4
(do ((n 0 (1+ n))
     (cur 0 next)
     (next 1 (+ cur next)))
    ((= 10 n) cur))

次の二つは同じ.

1
2
3
4
(dotimes (i 4) (print i))
(do ((i 0 (1+ i)))
    ((>= i 4))
  (print i))

fold

Common Lisp では reduce.

1
(reduce #'max '(1 2 3 4))

format

基本

1
2
3
(format t "hello, world")
(format t "hello, ~a" '(1 2 3))
(format t "x: ~d y: ~d" 20.1 3)

注意

直接数は渡せないので "~a" の仲介が必要.

1
(format t "~a" 3)

値の文字列化

(format t "hello, world")tnil にすると数値を文字列化できる.

loop

いろいろな使い方ができる化け物マクロなので, 少しずつためていく.

整数からなるリストを作る

1
2
(loop :for i :below 5 :collect i)
(loop for i from 1 to 10 collecting i)

二重ループ

1
2
3
(loop for x downfrom 999 to 900
      append (loop for y downfrom 999 to 900
                   collect (format nil "~a" (* x y))))

while

1
2
3
4
5
6
7
8
9
(let ((i 0) (fibval 0) (fibs ())
      (xs ())
      (lastval 4000000))
  (loop while (< fibval lastval)
        do (progn
             (setq fibval (fib-memo i)
                        i (incf i))
             (when (< fibval lastval)
               (setq fibs (cons fibval fibs))))))))

MISC

1
2
3
4
5
6
7
8
(loop for x from 1 to 10 summing (expt x 2)) ;==> 385
(loop :for i :from 3 :upto 5 :do (print i))
(loop :repeat 5 :do (format t "~&five"))
(loop :for i :upto 5 :do (format t "~&~A" i))

;;; This counts the number of vowels in a string:
(loop for x across "the quick brown fox jumps over the lazy dog"
      counting (find x "aeiou"))

mapcar

詳しくは hyperspec 参照. map だと書式が違う.

1
(print (mapcar #'1+ '(1 2 3)))

remove-if-not: いわゆる filter

1
2
(remove-if-not #'(lambda (x) (if (oddp x) (1+ x)))
               '(1 2 3 4))

paredit

progn: 一箇所に処理をたくさん書きたいとき

loop while dodo のように, 1 つのフォームしか書けないところでたくさん処理を書きたいときは, progn でくくった中に書けばいい.

roswell

quicklispでのインストール先

  • .roswell/lisp/quicklisp/dists/quicklisp/software

SLIME

有用コマンド

コマンド 意味
C-c RET マクロ展開

関数のクォート

1
2
(defun sq (x) (* x x))
(mapcar #'sq '(1 2 3 4))

述語式

=, eq, equal, equalp

条件文

cond

1
2
3
4
(cond
    ((equal (mod x 3) 0) x)
    ((equal (mod x 5) 0) x))
    (t x))

if

1
2
3
(if (3or5p 4)
    1
    2)

when

1
2
(when (3or5p 4)
  1)

数学関係

累乗

1
(expt 2 3) ; 2^3

配列

1
2
3
4
5
(make-array 3)
(make-array 3 :element-type 'list :initial-element nil)
(make-array 3 :initial-contents '(1.0 2.0 3.0))
(let ((a #(1.0 2.0 3.0)))
  (format nil "~A" a))

ハッシュテーブル・連想リスト

Python でいう dictionary のこと.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
;;; ハッシュテーブル
(let ((hash (make-hash-table)))
  (setf (gethash 'color hash) 'red)
  (setf (gethash 'height hash) 185)
  (setf (gethash 'weight hash) 110)
  (setf (gethash 'name hash) "Mukku")
  (maphash #'(lambda (k v) (format t "~a => ~a~%" k v)) hash)
  (loop for key being each hash-key of hash
      using (hash-value value)
      do (format t "~A => ~A~%" key value))
  )
;;; 連想リスト
(let ((alst '()))
  (push '(:color :red) alst)
  (push '(height 185) alst)
  (push '(weight 110) alst)
  (format t "~&~a" (assoc :color alst))
  (format t "~&~a" (assoc 'height alst))
  (format t "~&~a" (assoc 'weight alst))
  (format t "~&~a" alst))

マクロ展開

Backquote Syntax Equivalent List-Building Code Result
`(a (+ 1 2) c) (list 'a '(+ 1 2) 'c) (a (+ 1 2) c)
`(a ,(+ 1 2) c) (list 'a (+ 1 2) 'c) (a 3 c)
`(a (list 1 2) c) (list 'a '(list 1 2) 'c) (a (list 1 2) c)
`(a ,(list 1 2) c) (list 'a (list 1 2) 'c) (a (1 2) c)
`(a ,@(list 1 2) c) (append (list 'a) (list 1 2) (list 'c)) (a 1 2 c)

メモ化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
;;; https://takeokunn.xyz/blog/post/common-lisp-fibonacci
;;; フィボナッチ数列
(defun memo (fn)
  (let ((table (make-hash-table :test 'equal)))
    #'(lambda (&rest rest)
        (multiple-value-bind (val found-p) (gethash rest table)
          (if found-p
              val
              (setf (gethash rest table) (apply fn rest)))))))
(defun memoize (fn-name)
  (setf (symbol-function fn-name)
        (memo (symbol-function fn-name))))
(defmacro defun-memo (fn args &body body)
  `(memoize (defun ,fn ,args . ,body)))
(defun fib (n)
  (if (<= n 1) 1
      (+ (fib (- n 1)) (fib (- n 2)))))
(defun-memo fib-memo (n)
  (if (<= n 1) 1
      (+ (fib-memo (- n 1)) (fib-memo (- n 2)))))

文字列

文字を数値化

1
(digit-char-p "test")

文字列を数値化

1
2
3
4
5
6
7
8
(parse-integer "1")
(parse-integer "11" :radix 2)
(parse-integer "11" :radix 8)
(parse-integer "11" :radix 10)
(parse-integer "11" :radix 16)
(read-from-string "1/10")
(read-from-string "1.2")
(read-from-string "#c(1 1)")

反転

1
(reverse "1234")

リストとその処理

最大値を取る

1
(reduce #'max '(1 2 3 4))