逆引きxyzzy lisp - circleratio/xyzzy GitHub Wiki

目次

Lisp の特徴、向き・不向き

  • 向き
    • 探検的プログラミング
    • すばやいプロトタイピング
    • 完成までの時間を最少にする
    • 単一のプログラマ(または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って何が違うんですか?

  • 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\")"

base64 エンコード/デコードする

(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
⚠️ **GitHub.com Fallback** ⚠️