Last modified: 2010/05/14 01:58:45
Common Lispはじめました。
環境
- Ubuntu Linux
- Emacs
- SBCL
- SLIME
個人設定
こんな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
その他取得に失敗したやつ
- infpre
- perfectstorm
- toolbox
- vektor
パス設定
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 "あ<い>う&え\"お'か") ->"あ<い>う&え"お'か"
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))
