Last modified: 2010/05/14 01:58:45

Common Lispはじめました。

環境
個人設定
よく使うキーバインド
Emacs
SLIME
ParEdit
Redshank
導入
設定
感想
clbuild
メリット
導入
全パッケージ取得
パス設定
メモ
文字列中の文字の置換
1文字取得
文字列の切り出し
ソート
文字列->数値
数値->文字列
文字列 trim
cl-ppcre (正規表現)
split-sequence (文字列の分割)
PNG (PNG画像)
UNZIP
drakma (HTTPクライアント)
html-encode
cxml (XML)
clsql-postgresql
Common LispでSDL

環境

個人設定

こんなelisp書いて M-x cl から環境を整えてます。

(defvar *my-cl-home* "~/program/common-lisp/source/")

(defun open-cl-project (project)
  (let* ((project-path (concat *my-cl-home* project))
         (main-file (concat project ".lisp")))
    (if (file-exists-p project-path)
        (progn
          (big-frame)
          (cd project-path)
          (slime)
          ;(split-window)
          (if (file-exists-p main-file)
              (find-file main-file)))
        (message "project not found."))))

(defun cl ()
  (interactive)
  (let ((project (completing-read "Project: " (directory-files *my-cl-home*))))
    (unless (string= project "")
      (open-cl-project project))))

よく使うキーバインド

Emacs

キー 機能
C-M-a カーソルを関数の先頭に移動
C-M-u 1つ外側のS式に移動
C-M-Space カーソル位置直後のS式をマーク

C-M-uで移動後、C-M-SpaceでS式まるまるマーキング。

SLIME

キー 機能
C-j 改行+インデント
C-c C-i シンボル補完
C-c C-k カレントバッファをコンパイルしてロード
C-x C-e カーソル位置直前の式を評価
C-c C-p カーソル位置直前の式を評価して結果を別バッファに表示

関数定義とかはC-x C-eでロード確認すると良さげ。

dbって打ってC-c C-iすると候補にdestructuring-bindとか出てくるので[TAB]で確定する。

ParEdit

Lispで一番打つのが多そうなカッコや、'[' '"'を補完してくれるやつ。

Emacs関連の方にまとめます。

Redshank

導入

cd ~/.emacs.d/lib/
sudo aptitude install dacs
darcs get http://www.foldr.org/~michaelw/projects/redshank

設定

(require 'redshank-loader
         "~/.emacs.d/lib/redshank/redshank-loader")
(eval-after-load "redshank-loader"
  `(redshank-setup '(lisp-mode-hook
                     slime-repl-mode-hook) t))

感想

C-x C-r C でdefclassの雛形作るんだけど、ミニバッファで補完が効かないのでスーパークラス補完とか涙目。

Slotのaccessor名に勝手に何か付くのがイヤゲ。

簡易でこんなん作ったしRedshankはもういいや。ParEditだけでお腹いっぱいです^q^

(defun cl-insert-class-slot ()
  "defclass用簡易フィールド追加"
  (interactive)
  ;; カーソルを行末にする為saveしない
  ;;(save-excursion
  (let ((field (read-string "Slot name=>")))
    (unless (string= field "")
      (insert (format "(%s :initarg :%s :initform nil :accessor %s)" field field field)))))

(define-key slime-mode-map "\C-ci" 'cl-insert-class-slot)

clbuild

clbuild Common Lisp用パッケージ管理システム

メリット

clbuildの導入まではroot権限が要るが、その後のパッケージ管理は一般ユーザでできる。

aptに縛られない(今回導入まではaptitudeでやってるけど)

導入

sudo aptitude install darcs
darcs get http://common-lisp.net/project/clbuild/clbuild
cd clbuild
chmod +x clbuild

clbuildの実行に必要なものを導入

sudo aptitude install cvs subversion mercurial curl git-core

./clbuild check がなんとなく通ったらOK。

全パッケージ取得

./clbuild update --all-projects
気持ちイイー

と思いきや、取得に失敗するやつがいる

例えば cl-cairo に失敗したら下記のようにディレクトリを作成してresumeする。

mkdir source/cl-cairo
./clbuild update --resume

ディレクトリだけではダメなやつもあった。

mkdir -p source/clpython/CVS/
touch source/clpython/CVS/Root
./clbuild update --resume

その他取得に失敗したやつ

パス設定

clbuildからSBCLを動かす気はないので、~/.sbclrcにclbuild/systemsのパスを追加

(push "/home/kazamai/program/common-lisp/clbuild/systems/"
       asdf:*central-registry*)

メモ

文字列中の文字の置換

(substitute #\A #\a "aiueokakikukeko")
"AiueokAkikukeko"

1文字取得

(char "aiueo" 0)
#\a

文字列の切り出し

(subseq "aiueo" 1 (1- (length "aiueo")))
"iue"

ソート

(sort "aiueo" #'char<) ;楽だなぁ
"aeiou"

文字列->数値

(parse-integer "9999")
9999

数値->文字列

(princ-to-string 1234)
"9999"

文字列 trim

(string-trim '(#\Space #\Tab #\Newline) "  aiueo  ")
-> "aiueo"

cl-ppcre (正規表現)

(asdf:operate 'asdf:load-op 'cl-ppcre)

(cl-ppcre:all-matches-as-strings "^[a-zA-Z]{5}$" "aiue")
-> NIL

(cl-ppcre:all-matches-as-strings "^[a-zA-Z]{5}$" "aiueo")
-> ("aiueo")

;; 文字列による文字列分割
(cl-ppcre:split "deli" "123deli456deli789deli")
("123" "456" "789")

split-sequence (文字列の分割)

(asdf:operate 'asdf:load-op 'split-sequence)

;; スペースで分割
(split-sequence:split-sequence #\Space "a b c")
-> ("a" "b" "c")

;; カンマで分割
(split-sequence:split-sequence #\, "a,b,,,c,")
-> ("a" "b" "" "" "c" "")

;; 空の値を無視する
(split-sequence:split-sequence #\, "a,b,,c," :remove-empty-subseqs t)
-> ("a" "b" "c")

PNG (PNG画像)

ん?

zpngの方が良いのかな?clbuildにはzpngしかなさげ。

(asdf:operate 'asdf:load-op 'png)
;; 画像サイズ取得(多値: 横幅 縦幅)
(png:image-size "./hoge.png")
->26
->240

UNZIP

(asdf:operate 'asdf:load-op 'zip)
;; ZIPファイル解凍
(zip:unzip "hoge.zip" "./hoge/")
;; ZIPファイル解凍(解凍先にファイルが存在していたら上書き)
(zip:unzip "hoge.zip" "./hoge/" :if-exists :supersede)

drakma (HTTPクライアント)

(asdf:operate 'asdf:load-op 'drakma)
;; 文字コード指定
(setq drakma:*drakma-default-external-format* :utf-8)
;; 取得
(multiple-value-bind (body-or-stream status-code headers uri stream must-close reashon-phase)
    (drakma:http-request "http://www.strnet.com/")
  (format t "Status: ~a~%~a~%" status-code body-or-stream))

html-encode

(asdf:operate 'asdf:load-op 'html-encode)
;; 4つの記号が対象。
(html-encode:encode-for-argument "あ<い>う&え\"お'か")
->"あ&lt;い&gt;う&amp;え&quot;お'か"

cxml (XML)

すごく適当なXMLファイル出力

(asdf:operate 'asdf:load-op 'cxml)

(with-open-file (out "hoge.xml"
                     :direction :output
                     :element-type '(unsigned-byte 8)
                     :if-exists :supersede)

  (cxml:with-xml-output (cxml:make-octet-stream-sink out
                                           :canonical nil
                                           :indentation 2
                                           :omit-xml-declaration-p t)
    (cxml:with-element "parent"
      (dotimes (i 10)
        (cxml:with-element "child"
          (cxml:attribute "id" i)))
(cxml:text "hoge!"))))

結果

<parent>
  <child id="0"/>
  <child id="1"/>
  <child id="2"/>
  <child id="3"/>
  <child id="4"/>
  <child id="5"/>
  <child id="6"/>
  <child id="7"/>
  <child id="8"/>
  <child id="9"/>
  hoge!
</parent>

すごく適当なXMLファイル入力(DOM)

上記の

(defmethod print-test ((ele dom:element))
  (format t "~a: ~a~%" (dom:tag-name ele) (dom:get-attribute ele "id")))

(defmethod print-test ((ele dom:text))
  (let ((trimed-str (string-trim '(#\Space #\Tab #\Newline) (dom:node-value ele))))
    (if (> (length trimed-str) 0)
        (format t "~a~%" trimed-str))))

(let* ((root (cxml:parse-file "hoge.xml" (cxml-dom:make-dom-builder)))
       (parent (dom:get-elements-by-tag-name root "parent")))
  (loop for ele across (dom:child-nodes (aref parent 0))
       do (print-test ele)))

->child: 0
child: 1
child: 2
child: 3
child: 4
child: 5
child: 6
child: 7
child: 8
child: 9
hoge!

clsql-postgresql

だいたいこんな感じ

(asdf:operate 'asdf:load-op 'clsql-postgresql)

(clsql-sys:with-database (con '("localhost" "dbname" "user" "pass")
                              :database-type :postgresql)
  (clsql-sys:query "SET client_encoding TO 'UTF-8'" :database con) ; サーバ側とクライアントで文字コードが違う場合指定
  (clsql-sys:select 'column-name 'column-name2
                  :from 'table-name
                  :where (clsql-sys:sql-and (clsql-sys:sql-= 'delete_flag "0")
                                            (clsql-sys:sql-= 'open_flag "1"))
                  :order-by '((column-hoge :desc))
                  :limit 8
                  :flatp t
                  :database con))

Common LispでSDL

こちら