この広告は、90日以上更新していないブログに表示しています。
基礎文法最速マスターブームにのっかってみる。Ruby基礎文法最速マスターをだいたいEmacs Lisp(elisp)に置き換えてみる。
Emacs LispはEmacsで使われているLisp方言のひとつだ。他の言語をある程度知っている人はこれを読めばEmacs Lispの基礎をマスターしてEmacs Lispを書くことができるようになるかもしれない。無保証ではあるが。
ある程度はCommon Lispにも応用できると思うよ。
Emacsを起動したときに生成されている、スクラッチバッファ(*scratch*)を使うと、Emacs Lispの式を簡単に評価することができる。M-Tabで補完できるぞ。
式を書いたらC-jを押すと、式の値が出てくるぞ。
(+ 1 3)4(format-time-string "%Y/%m/%d %H:%M:%S" (current-time))"2010/02/01 22:53:34"
あるいは M-x ielm を起動すると、シェルやirb(インタラクティブRuby)のようなインターフェースが使えるぞ。 Tabキーでの補完もきく。
*** Welcome to IELM *** Type (describe-mode) for help.ELISP> (* 5 4)20ELISP> (buffer-size)95ELISP>
xmpfilterが好きなRubyistはlispxmpをどうぞ→xmpfilter のような自動注釈を Emacs Lisp で実現する lispxmp.el をリリース - http://rubikitch.com/に移転しました
エコーエリアに表示するにはmessage関数を使う。message関数はformat関数の書式文字列が使える。format関数は、ぶっちゃけて言うとsprintfだ。
(message"ans = %d"(+34)); ans = 7と表示される
Emacs Lispは変数宣言をしなくても代入した時点ですぐに使える。が、Emacs Lispファイルを書くときはdefvarで変数宣言をしておくとバイトコンパイラを黙らせることができる。
(defvar foo1); 変数fooを1に初期化する(setq bar10); 変数barに10を代入する
ユーザカスタマイズを目的とする変数を宣言するにはdefcustomを使う。詳しくは C-h f defcustomをどうぞ。
ローカル変数を使うときは、letを使う。ここではローカル変数aとbを用意し、両者を足している。
(let((a1)(b2))(+ a b)); => 3
letにはlet*という亜種がある。let*は前に設定されたローカル変数の影響を受ける。let*を使うべきところでletを使ってしまうとハマってしまうので注意だ。
(setq x1);;; letは同時にバインドされるので外側のxを参照する(let((x(+ x3))(y(+ x2))); この時点でのxは1(+ x y)); => 7;;; let*は直前のローカル変数代入の影響を受ける(let*((x(+ x3))(y(+ x2))); この時点でのxは4(+ x y)); => 10
ただし、ローカル変数はダイナミックスコープなので、ローカル変数の値は呼び出した関数にも波及するので注意!!
(defun f() a); ローカル変数のaを参照してしまう(let((a12))(f)); => 12
コメントは「;」から行末まで。
コメントの用法はセミコロンの数によって使い分ける。
;;;; 主要な部分のヘッダ(左端に揃える);;; 関数定義の外側で使い、;;; プログラムの設計原理を説明する(左端に揃える)(+3;; このコメントは字下げに揃える(buffer-size)); バッファのサイズ
Emacs Lispファイルを「実行」するとは、ファイルをロードすることである。
たとえば、foo.elをロードするには
M-x load-file /path/to/foo.el
あるいは、
M-x load-library foo
を実行する。
整数や小数が使える。
11.234
(+11); => 2(-11); => 0(*12); => 2;; 整数どうしの割り算は整数になる(/52); => 2;; どちらかが小数なら結果も小数(/5.02); => 2.5;; 余りをとる(mod52); => 1
インクリメンタルはincfマクロを、デクリメントはdecfマクロを使う。
マクロなので、Emacs Lispファイルで使うときは (eval-when-compile (require 'cl)) にしたほうが無難。くわしくはこちら→Emacs Lispプログラマはガンガン(require 'cl)しろよ - http://rubikitch.com/に移転しました
(require'cl)(let((a0));; インクリメンタル(incf a); => 1(incf a); => 2;; デクリメント(decf a); => 1)
1だけ加減するにはそれぞれ1+関数と1-関数を使う。
(let((b3))(1+ b); => 4(1- b); => 2)
Emacs Lispの文字列はダブルクォートで囲む。\t(タブ)や\n(改行)などの特殊文字を使うこともできる。シングルクォート文字列は用意されていない。式展開も用意されていない。
"abc"; => "abc""abc\tdef\n"; abc タブ def 改行"\\"; \ 1文字"\"foo\""; "foo"
;;; 結合(concat"aaa""bbb"); => "aaabbb"(mapconcat'identity'("aaa""bbb""ccc")","); => "aaa,bbb,ccc";;; 分割(split-string"aaa,bbb,ccc"","); => ("aaa" "bbb" "ccc");;; 長さ(文字数)(length"abcdef"); => 6(length"あいうえお"); => 5;;; 長さ(文字幅)(string-width"あいうえお"); => 10;;; 切り出し(第3引数のインデックスは直前なので注意);; 0〜2番目の直前(substring"abcdef"02); => "ab";; 0〜最後から2番目の直前(substring"abcdef"0-2); => "abcd";; 0〜最後の直前(substring"abcdef"0-1); => "abcde";; 2番目から最後まで(substring"abcdef"2); => "cdef";;; 検索 (正規表現、文字列の順に指定する)(string-match"bc""abcd"); => 1
Lispにも配列はあるものの、リストの方が基本的なのでリストを。
リストはlist関数を使う。リテラルをリストするには「'(要素 ...)」でもよい。
(setq ary'(100 200 300)); => (100 200 300)(setq ary(list100200300)); => (100 200 300)
(setq ary(list100200300));; 最初の要素を参照するにはcarかfirst関数を使う。(setq a(car ary)); => 100(setq a(first ary)); => 100;; nthかelt関数を使うと任意の要素を取り出せる(setq a(nth0 ary)); => 100(setq a(elt ary0)); => 100(setq b(nth1 ary)); => 200;; リストを書き換えるにはsetfとnthを併用する(require'cl)(setf(nth0 ary)1); => 1(setf(nth1 ary)2); => 2;; 確かに書き変わっている。ary; => (1 2 300)
リストの要素にもlength関数が使える。
(length ary); => 3
(setq ary(list123)); => (1 2 3);; 先頭を取り出すにはpopマクロを使う(require'cl)(pop ary); => 1ary; => (2 3);; 先頭に追加するにはpushマクロを使う(push5 ary); => (5 2 3);; 末尾を取り出す(非破壊的);; データ構造上、リストでは末尾を取り出すことは稀。(last ary); => (3)(car(last ary)); => 3ary; => (5 2 3);; 末尾に追加するにはリスト化してappendする(setq ary(list52)); => (5 2)(append ary(list9)); => (5 2 9)
コンスセルとは、2つのデータのペアであり、リストの構成要素となっている。コンスセルの左側をcar、右側をcdrと言う。名前は歴史的理由からなので深く考えなくてよい。
(setq a1)(setq b2);; コンスセルはcons関数で作成する(setq c(cons a b)); => (1 . 2)(setq c'(1 . 2)); => (1 . 2)(car c); => 1(cdr c); => 2;; cdrにコンスセルを指定し、数珠繋ぎにするとリストになる。;; 最後のコンスセルはnilで終える。(setq l(cons1(cons2(cons3nil)))); => (1 2 3)(setq l(list123)); => (1 2 3);; よって、リストのcar/cdrを取るとこうなる。(car l); => 1(cdr l); => (2 3)
Emacs Lispにはハッシュリテラルは用意されていないのでmake-hash-table関数を使う必要がある。(めんどくせぇ)しかも表示形式が用意されていないので泣けてくる。
;; ハッシュの値を表示するために自分で関数を定義する必要がある…はぁ(defun print-hash(hash)(with-temp-buffer(loop initially(insert"{") for k beingthe hash-keys in hash using(hash-values v)do(insert" "(prin1-to-string k)" => "(prin1-to-string v)",") finally(delete-backward-char2)(insert" }"))(buffer-string)));; ハッシュの作成(テスト関数はequalにしておくのが無難)(setq hash(make-hash-table:test'equal)); => #<hash-table 'equal nil 3/65 0x11472f30>;; 要素の代入(puthash"a"1 hash); => 1(puthash"b"2 hash); => 2(print-hash hash); { "a" => 1, "b" => 2 }(puthash"c"5 hash); => 5(puthash"d"7 hash); => 7(print-hash hash); { "a" => 1, "b" => 2, "c" => 5, "d" => 7 };; 要素の参照(gethash"a" hash); => 1(gethash"b" hash); => 2;; キーの取得(loopマクロが必要)(require'cl)(loop for k beingthe hash-keys in hash collect k); => ("a" "b" "c" "d");; 値の取得(loop for v beingthe hash-values in hash collect v); => (1 2 5 7);; ペアを得る(長い!!!)(loop for k beingthe hash-keys in hash using(hash-values v) collect(cons k v)); => (("a" . 1) ("b" . 2) ("c" . 5) ("d" . 7));; キーの存在確認(gethashしかない?)(gethash"a" hash); => 1;; ハッシュのペアの削除(remhash"a" hash); => nil(print-hash hash); { "b" => 2, "c" => 5, "d" => 7 }
Emacs Lispでは、条件文で偽と見なされるのはnilのみで、それ以外のオブジェクトは全て真と見なされる(0や空文字列も真)。
;; if式のelse部分は複数の式を置くことができるのでちょっと不釣合い(setq a1)(if(equal a1)'ok'ng); => ok(if(equal a2)'ok1;複数の式が置ける'ng); => ng;; cond式(ifのthenも複数の式を書きたければcondを使うほうがいい)(cond((equal a1)'ok)(t'ng)); => ok(cond((equal a2)0'ok)(t1'ng)); => ng;; condならばelse ifがあっても大丈夫(setq a0)(setq b2)(cond((equal a1)"foo")((equal b2)"bar")(t"baz")); => "bar"
;; while式 (けっこう使われているが正直ダサい)(setq i0)(while(< i5) i; => 0, 1, 2, 3, 4(incf i));; リストを繰り返すにはdolistかmapcを使う(dolist(i'(1 2 3)) i; => 1, 2, 3)(mapc(lambda(i) i; => 1, 2, 3)'(1 2 3));; 回数繰り返し(dotimes(i5) i; => 0, 1, 2, 3, 4);; 無限ループは (while t 〜) で書ける
Rubyでいうイテレータ関数はclパッケージにごっそり入っているのだが、cl関数はEmacs Lisp的には使うなと言われている*1ので、loopマクロを推奨する。 loopマクロは (eval-when-compile (require 'cl)) と入れてればいつでも使える。 loopマクロの他の使用例→http://rubikitch.com/に移転しました
みんなもloopマクロ使おうぜ!
(require'cl);; 条件に合うものだけを選ぶ(remove-if-not'evenp'(1 2 3 4 5)); => (2 4)(loop for x in'(1 2 3 4 5)when(evenp x) collect x); => (2 4);; 条件に合うものを除く(remove-if'evenp'(1 2 3 4 5)); => (1 3 5)(loop for x in'(1 2 3 4 5)unless(evenp x) collect x); => (1 3 5);; 条件に合う最初のものを返す(find-if'evenp'(1 2 3 4 5)); => 2(loop for x in'(1 2 3 4 5)when(evenp x)return x); => 2;; 等しい値があるか調べる;; member関数は組み込み関数なので使える;; 等しい値がある場合はリストの残りを返す(member3'(1 2 3 4 5)); => (3 4 5)(member30'(1 2 3 4 5)); => nil;; findはcl関数(find3'(1 2 3 4 5)); => 3;; 条件に合うものがあるか調べる(loop for x in'(1 2 3 4 5) thereis(evenp x)); => t(loop for x in'(1) thereis(evenp x)); => nil;; 全ての要素が条件に合うかを調べる(loop for x in'(1 2 3 4 5) always(evenp x)); => nil(loop for x in'(2 4) always(evenp x)); => t;; 条件に合うものの個数を数える(count-if'evenp'(1 2 3 4 5)); => 2(loop for x in'(1 2 3 4 5)count(evenp x)); => 2;; 最大のものを返す(組み込み関数)(max12345); => 5;; 最小のものを返す(組み込み関数)(min12345); => 1;; 加工結果が最大のものを返す;; これだと加工後の値を返してしまう(loop for x in'(1 -2 3 4 -5) maximize(abs x)); => 5;; めんどい(^^;(let*((x'(1 -2 3 4 -5))(score(mapcar'abs x))(value(apply'max score))(i(position value score)))(nth i x)); => -5;; 加工結果が最小のものを返す;; これだと加工後の値を返してしまう(loop for x in'(1 -2 3 4 -5) minimize(abs x)); => 1;; めんどい(^^;(let*((x'(1 -2 3 4 -5))(score(mapcar'abs x))(value(apply'min score))(i(position value score)))(nth i x)); => 1;; 昇順にソートする(組み込み関数)(sort'(1 -2 3 4 -5)'<); => (-5 -2 1 3 4);; 加工結果で昇順にソートする(sort*'(1 -2 3 4 -5)'<:key'abs); => (1 -2 3 4 -5);; 加工結果を配列で返す(組み込み関数)(mapcar'abs'(1 -2 3 4 -5)); => (1 2 3 4 5)
(defun sum(x y)(+ x y))(sum12); => 3
Emacs Lispの場合、ファイルはバッファに読み込んでから加工する。
;; test-bufferという名前のバッファを作成し、いろいろな内容を書き出す(with-current-buffer(get-buffer-create"test-buffer");; バッファの内容を空にする(erase-buffer);; /tmp/foo.txtの内容を挿入する(insert-file-contents"/tmp/foo.txt");; 文字列を挿入する(insert"End\n");; バッファの内容をファイルに書き出す(write-region(point-min)(point-max)"/tmp/bar.txt"))
Emacs Lispは高階関数が使える。つまり、関数を引数にすることもできるし、関数を返り値にすることもできる。
関数を引数に渡すには、関数のシンボルを指定するか、無名関数(λ式)を指定する。
mapcar関数は、リスト加工結果を返す頻出関数だ。第1引数には要素を引数とする加工関数を指定するようになっている。
たとえば、リストの全要素を絶対値にしたもののリストを返すには、abs関数を加工関数とするため、次のように書ける。
(mapcar'abs'(1 -2 3 4 -5)); => (1 2 3 4 5)
これをλ式で書くと以下のようになる。
(mapcar(lambda(x)(abs x))'(1 -2 3 4 -5)); => (1 2 3 4 5)
(lambda (x) (abs x))がλ式である。λ式の文法は、上で述べた関数定義の「defun 関数名」を「lambda」に置き換えたものである。λ式は、名前がつけられていない関数をその場に埋め込むのに使う。Rubyのブロックみたいなもの。
Emacsにはビルトインでドキュメント機能がついている。つまり、EmacsにおいてわからないことはEmacsに聞けってことである。
関数の説明はC-h fで見られる。
変数の説明と現在の値はC-h vで見られる。
*1:くそったれが…
引用をストックしました
引用するにはまずログインしてください
引用をストックできませんでした。再度お試しください
限定公開記事のため引用できません。