今日はEmacs LispのTipsです。
一言で言うと
おまいらformat関数をもっと使え
ってことです。
format 関数には3つのメリットがあります。
- printf 形式の書式文字列
- 読みやすい
- あらゆるオブジェクトを受け取れる
読みやすい
printf形式の書式文字列が使えるのは、言うまでもないので省きますが、
文字列を作る関数ではformat関数を使うのがすごい大事なのです。
たった1つの文字列を埋め込む場合でもformat関数を使うべきです。
(let ((x "helm")) (concat "http://rubikitch.com/tag/package:" x "/") ;; => "http://rubikitch.com/tag/package:helm/" (format "http://rubikitch.com/tag/package:%s/" x) ;; => "http://rubikitch.com/tag/package:helm/" )
この例を見てみればわかるように、format関数を使った方が、
URLのアウトラインがわかりやすくなりますよね。
文字列化
ですが、format関数のすごいところは、あらゆるオブジェクトを
文字列化してしまうところです。
Emacs Lispプログラマが陥るよくある落とし穴として、
バッファ名を代入する前提の「バッファを格納する変数」に
バッファオブジェクトそのものを代入してしまったため、
string-match 関数でエラーを起こしてしまうことです。
バッファオブジェクトでバッファ名に対してstring-matchするときは、
バッファ名を得る必要があります。
かといって、安易に buffer-name 関数を使うと、
今度はその変数にバッファ名が代入されているときにエラーになってしまいます。
(condition-case _ (buffer-name "*scratch*") (error _)) ;; => (wrong-type-argument bufferp "*scratch*") (let ((buf "*scratch*")) (if (bufferp buf) (buffer-name buf) buf)) ; => "*scratch*" (let ((buf (get-buffer "*scratch*"))) (if (bufferp buf) (buffer-name buf) buf)) ; => "*scratch*"
長くてうざったいです。
これを一気に解決してくれるのがformat関数なのです。
(let ((buf "*scratch*")) (string-match "^\\*" buf)) ; => 0 (let ((buf (get-buffer "*scratch*"))) (condition-case _ (string-match "^\\*" buf) (error _))) ;;; => (wrong-type-argument stringp #<buffer *scratch*>) (let ((buf (get-buffer "*scratch*"))) (string-match "^\\*" (format "%s" buf))) ; => 0
つーわけで (wrong-type-argument stringp 〜) というエラーに遭遇したら
format関数の出番なんですね。
concat + *-to-string が許されるのが小学生まで
で、すげー吐き気を催すコードが
concat と文字列化関数を合わせてる場合です。
Emacs Lispのソースコードに対して「concat.+-(name|to-string)」でgrepすると
出るわ出るわ、すごい汚いコードが…
(let ((x 512)) (concat "合計金額は" (number-to-string x) "円") ; => "合計金額は512円" (format "合計金額は%s円" x) ; => "合計金額は512円" ) (let ((c ?X)) (concat "文字[" (char-to-string c) "]") ; => "文字[X]" (format "文字[%c]" c) ; => "文字[X]" ) (let ((b (get-buffer "*scratch*"))) (concat "Buffer: " (buffer-name b)) ; => "Buffer: *scratch*" (format "Buffer: %s" b) ; => "Buffer: *scratch*" ) (let ((s 'sym)) (concat "Symbol: " (symbol-name s)) ; => "Symbol: sym" (format "Symbol: %s" s) ; => "Symbol: sym" ) (let ((s '(foo bar))) (concat "Sexp: " (prin1-to-string s)) ; => "Sexp: (foo bar)" (format "Sexp: %S" s) ; => "Sexp: (foo bar)" )
どっちが読みやすいかは火を見るより明らかですよね。
ただし、nilを渡すと"nil"が返るので注意です。
(format "%s" nil) ; => "nil"
Emacs Lispに限らず他言語を使う場合でも、同じ理由で
極力printf/format系の関数を使うことをおすすめします。
本日もお読みいただき、ありがとうございました。参考になれば嬉しいです。