逆引きxyzzy lisp - circleratio/xyzzy GitHub Wiki
- Lisp の特徴、向き・不向き
- 関数型言語の特徴
- 状態保存
- 比較
- ファイルのロードと評価
- base64 エンコード/デコードする
- ウィンドウのアイコン化と復元
- レジストリの操作
- 文字列を式と見なして評価する
- カーソル位置の式を評価する
- テーブルを使って、リストをビット列と対応させる
- エラーのハンドリング
- アニメーション処理
- ベルを鳴らす
- エディタ起動時に処理を行う
- 関数の意味を調べる
- 向き
- 探検的プログラミング
- すばやいプロトタイピング
- 完成までの時間を最少にする
- 単一のプログラマ(または10人未満のチーム)のプロジェクト
- ソースからソースへ、またはデータからデータへの変形
- コンパイラやその他のトランスレータ
- 問題特有の言語、メタプログラミング
- 動的なディスパッチや作成(実行時にコンパイラが利用可能)
- (Unixの文字パイプモデルとは対照的に)1つのイメージの中へのモジュールの緊密な統合
- 高度の対話性(read-eval-print, CLIM)
- ユーザが拡張できるアプリケーション(GNU Emacs)
- 不向き
- 持続性の記憶領域(データベース)
- 小さなマシン上での資源の使用を最大にする
- 数百名のプログラマを用いるプロジェクト
- 別言語のコードとの緊密な通信
- 小さなイメージのアプリケーションを配達する
- 実行時の制御(しかしGensymはそれを行なった)
- 経験不足のLispプログラマを用いるプロジェクト
- ある種の数値または文字計算(注意深く宣言を行なうことでうまく動作するが、Lispの効率のモデルは学習しづらい)
参照透明とは、変数の値を書き換えないこと。 すなわち、同じ関数の評価は必ず同じ結果となる。
-
現実的なメリット:
- デバッグがやりやすい。
- 並列計算と相性がよい。
- 遅延評価を利用できる。
-
参照透明でない例:
- 呼び出す度に値が変わる。副作用がある。
- 出力を伴う。
次の関数を例に、正格評価との比較で説明する。
(defun square (x) (* x x))
正格評価
(square (+ 1 2))
= (square 3)
= (* 3 3)
= 9
遅延評価
(square (+ 1 2))
= (* (+ 1 2) (+ 1 2))
= (* 3 3)
= 9
参照透明なら、いつ式を評価してもよい。
-
メリット
- 正格評価ではエラーとなるケースでも、遅延評価なら動く場合がある(=動作する範囲が広い)
- 正格評価では停止しないケースでも、遅延評価なら停止する場合がある
- 遅延評価だと、不要な計算が省かれ、速く計算できる場合がある(たらい回し関数など)
-
応用例
- 無限リスト→必要な部分だけ取り出せる。
Haskellはデフォルトで遅延評価。 Lispはデフォルトで正格評価。
純粋な関数型言語ではループなどは「ない」
- 関数を値として関数に渡せる(高階関数)。
- 関数の返り値として、関数を返せる
- 関数ポインタとの違い。
- 関数をどこでも定義できる。
- 無名関数を書ける。
クロージャとオブジェクトは本質的には等価(片方で、もう片方を書ける) ただし、記法(lambda)やレキシカルスコープなど、取り扱いではクロージャのほうが便利…なはず。
関数型言語ではデータ構造としてリストがよく使われる。 なぜなら、再帰的なデータ構造を持つため、何でも再帰でやる関数型言語との相性がよいため。
- save-excursion は、カレントバッファとポイントを保存する。ただし、抜けるときにバッファがなくなっていたらなにもしない(^^
- save-restriction は、narrow-to-region する前のバッファの範囲を保存する。ポイントやバッファは保存しない。これも抜けるときにバッファが削除されていればなにもしない。
eq, eql, equal, equalp の違いは次の通り.
- eq: 同一のオブジェクト (つまりアドレスが等しい) 場合に真.
- eql: eq を満たしている、あるいは、同じ型で同じ値の数値や、同じ値の文字であれば真.文字列は対象外.
- equal: eql を満たしている、あるいは、構造が同じリストや,内容が等しい文字列であれば真.
- equalp: equal と同様だが一部の差異を許容.型が違っても同じ値の数値ならば真.文字や文字列では英大小文字を区別しない.
等しいと判定される範囲の広さは eq < eql < equal < equalp となる.
- =: 引数がすべて等しければ t
- /=: 引数がすべて等しくなければ t
- <: 左から順に見て単調増加なら t
- >: 左から順に見て単調減少なら t
- <=: 左から順に見て減少することがなければ t
- >=: 左から順に見て増加することがなければ t
(/= 1 3 4 5)
=> t
(/= 1 3 3 4)
=> nil
(< 1 2 3 4 5)
=> t
(> 5 4 3 2 1)
=> t
(<= 1 1 1 2 3)
=> t
(>= 5 5 4 4 3 3)
=> t
関数 | 小さい | 等しい | 大きい | 大文字・小文字の区別 |
---|---|---|---|---|
string= | ○ | |||
string> | ○ | あり | ||
string< | ○ | あり | ||
string>= | ○ | ○ | あり | |
string<= | ○ | ○ | あり | |
string-lessp | ○ | なし | ||
string-not-lessp | ○ | ○ | なし | |
string-greaterp | ○ | なし | ||
string-not-greaterp | ○ | ○ | なし |
(string< "ABC" "BCD")
=> 0
(string< "abc" "BCD")
=> nil
(string-lessp "abc" "BCD")
=> 0
関数 | 昇順 | 等しい | 降順 | 大文字・小文字の区別 |
---|---|---|---|---|
char= | ○ | あり | ||
char/= | あり | |||
char< | ○ | あり | ||
char<= | ○ | ○ | あり | |
char> | ○ | あり | ||
char>= | ○ | ○ | あり | |
char-equal | ○ | なし | ||
char-not-equal | なし | |||
char-greaterp | ○ | なし | ||
char-lessp | ○ | なし | ||
char-not-greaterp | ○ | ○ | なし | |
char-not-lessp | ○ | ○ | なし |
(char-equal #\a #\a #\A)
=> t
(char= #\a #\a #\A)
=> nil
(char-greaterp #\c #\b #\a)
=> t
c:/temp/test.l
(msgbox "buf: ~A" (selected-buffer))
を予め用意した上で次の式を評価する。
(load-file "c:/temp/test.l")
=> t
ファイルを xyzzy 起動時に実行するには、次のようにする。
xyzzy -e "(load-file \"c:/temp/test.l\")"
(si:base64-encode "testテスト")
=> "dGVzdINlg1iDZw=="
(si:base64-decode "dGVzdINlg1iDZw==")
=> "testテスト"
(require "wip/winapi")
(defun iconize-and-restore ()
(winapi:ShowWindow (get-window-handle) 6) ;SW_MINIMIZE
(sleep-for 5)
(winapi:ShowWindow (get-window-handle) 9)) ;SW_MAXIMIZE
read/write-registry を使う。
(read-registry "SYSTEM\\CurrentControlSet\\Control\\Windows" "CSDBuildNumber" :local-machine)
=>16385
write-registry の利用例は危ないので省略。
(eval (read-from-string "(+ 1 2 3 4 5)"))
=> 15
<% (+ 1 2 3) %> のような形でテキスト中に書かれた式を評価し、値をポップアップ表示する。
(defun get-point ()
(interactive)
(let ((p (point))
(from)
(to))
(save-excursion
(if (scan-buffer "<%" :reverse t)
(and
(setq from (match-end 0))
(goto-char p)
(scan-buffer "%>" :reverse t)
(> (match-end 0) from)
(setq from nil)))
(goto-char p)
(if (scan-buffer "%>")
(and
(setq to (match-beginning 0))
(goto-char p)
(scan-buffer "<%")
(< (match-beginning 0) to)
(setq to nil))))
(if (and from to)
(buffer-substring from to))))
(defun eval-point ()
(interactive)
(let ((result (get-point)))
(if result
(popup-string (format nil "~A" (eval (read-from-string result))) (point)))))
(defun set-flag (ls tbl)
(apply #'+ (mapcar #'(lambda (x) (ash 1 (position x tbl))) ls)))
(defun parse-flag (flag tbl)
(do ((i 0 (1+ i))
(result))
((zerop flag) (reverse result))
(if (= (logand flag #b1) 1)
(setq result (cons (nth i tbl) result)))
(setq flag (ash flag -1))))
(set-flag '(a b e f) '(a b c d e f g))
=> 51
(parse-flag 51 '(a b c d e f g))
=> (a b e f)
(handler-case (/ 10 0)
(division-by-zero () nil))
=> nil
(handler-case (/ 10 0)
(error (c) c))
=> #S(division-by-zero operation / operands (10 0))
アニメーション処理のため「一定時間待った後に表示」を行う場合には,sleep-for でなく,sit-for を使う必要がある.
うまくいかない例.
(dotimes (i 50)
(sleep-for 1)
(insert "x"))
うまくいく例.
(dotimes (i 50)
(sit-for 1)
(insert "x"))
(ding)
~/.xyzzy に下記のように記述する. これにより,xyzzy 起動直後に scratch バッファに "abc" の文字列が挿入される.
(defun hook-test ()
(insert "abc"))
(add-hook '*post-startup-hook* #'hook-test)
M-x describe-function