読者です 読者をやめる 読者になる 読者になる

よくある処理を Emacs Lispで書く場合

emacs

Emacsで何かやりたいとき, 何をすればいいかはわかっていても, Emacs Lisp
それをどうやって表現したらいいかわからないということがあるかと思います.
すべてではありませんが, よく使いそうな処理を他の言語と合わせて掲載します.


詳しいことは以下にあるので, そちらを見ていただければと思います。
Emacs Lisp基礎文法最速マスター - http://rubikitch.com/に移転しました

リストを加工する(写像, map)

  (mapcar (lambda (elm) (1+ elm)) '(1 2 3 4 5))
  ;; => (2 3 4 5 6)

  ;; 複数のリストを関数に渡す場合
  (require 'cl)
  (mapcar* (lambda (a b) (+ a b)) '(1 2 3) '(10 20 30))
  ;; => (11 22 33)

  ;; loopを使う
  (require 'cl)
  (loop for elm in '(1 2 3 4 5)
        collect (1+ elm))
Perl
  my @nums = 1..5;
  map { $_ + 1 } @nums;
Ruby
  a = [1,2,3,4,5]
  a.collect {|e| e + 1}
Python
  a = [1,2,3,4,5]
  mapped = [x + 1 for x in a]

リストから条件にマッチする要素を抽出する(filter)

(require 'cl)
(remove-if-not (lambda (elm) (evenp elm)) '(1 2 3 4 5))

リストから条件にマッチする要素を抽出する場合, remove-if-notが
使えます. ただし remove-if-notは cl.elで定義される関数(not マクロ)なので,
バイトコンパイル時にエラーが出てしまいます(evenpもそうです).


別にそんなの気にしないということでもいいんですけど(今後のことを考えると
そっちの方がいいと思いますが), 回避するためには以下のように loopマクロ
を使うとよいでしょう. これに関わらず loopマクロは汎用性が高いので, 知って
おいて損はないでしょう.

(loop for elm in '(1 2 3 4 5)
      when (mod elm 2)
      collect elm)
Perl
my @list = (1..5);
my @evens = grep { $_ % 2 == 0 } @list;
Ruby
a = [1,2,3,4,5]
a.select {|e| e.even? }
Python
a = range(1, 6)
evens = [e for e in range(1,6) if e % 2 == 0]


逆に条件に一致するものを除去する場合は, 条件文を逆にすれば
よいでしょう. Emacs Lispだと remove-ifを使うことも可能です.

文字列を結合する

文字列をそのまま連結する場合は, concat, 加工する場合やセパレータを
挟んで連結したい場合は mapconcatを使うとよいです.

文字列を連結する
;; そのまま結合
(concat "apple" "orange" "melon")

;; 各要素を加工して結合
(mapconcat #'capitalize '("apple" "orange" "melon") " ")

mapconcatで文字列をそのまま使いたい場合は, 第一引数の関数に
"identity"を指定してください.

Perl
my @words = qw/apple orange melon/;
my $joined = join " ", @words;
Ruby
words = %w(apple melon orange)
puts words.join(' ')
Python
words = ["apple", "melon", "orange"]
" ".join(words)

正規表現マッチ

string-match関数で正規表現マッチを行えます. 補足した部分を取得する場合は
match-string関数を使います. このとき第二引数にマッチした文字列を指定する
ことを忘れないでください. これは抜かすとバッファにある文字列を返して
しまうので, 正しく動作しません.

(let ((str "apple 12345"))
  (when (string-match "\\([0-9]+\\)" str)
    (match-string 1 str)))
Perl
my $str = "apple 12345";
if ($str =~ m/([0-9]+)/) {
    print $1, "\n"
}
Ruby
str = "apple 12345"
if /(\d+)/ =~ str
  puts $1
end
Python
import re

s = "apple 12345"
m = re.search(r'(\d+)', s)

if m:
    print(m.group(1))

外部コマンドの実行

外部コマンドの実行結果を取得するには, shell-command-to-string関数が
使えます. 単にコマンドだけ実行したい場合は shell-commandが使えます.

(shell-command-to-string "ls -l /tmp")
Perl
my $ls_output = `ls -l /tmp`;
Ruby
ls_output = `ls -l /tmp`
Python
from subprocess import Popen, PIPE

result = Popen(["ls", "-l", "/tmp"], stdout=PIPE).stdout.read()
print result
外部コマンドの出力に対して, 細かい操作を行う場合

出力結果をそのまま使う場合は, shell-command-to-stringでいいのですが,
部分的に使いたい場合は不向きです. 得られた文字列を加工しまくることでも
なんとかならないことはないですが, Emacsはバッファにある文字の操作の
方が得意なので, こちらの方法の方が, 手軽にかつ高速に処理を行えます.


このとき使う関数が call-processです. call-processは外部コマンドの
出力結果を指定したバッファに出力できます. だいたいは
with-temp-bufferと使うことが多いと思います. 引数が多い場合は,
call-processより call-process-shell-commandの方が楽です.


(例) "ls -l"の結果から, ファイルの属性とファイル名を取得

(with-temp-buffer
  (let ((ret (call-process-shell-command "ls -l /tmp" nil t)))
    (unless (zerop ret)
      (error "Failed ls -l /tmp"))
    (goto-char (point-min))
    (let ((permissions nil))
      (while (not (eobp))
        (let ((line (buffer-substring-no-properties
                     (line-beginning-position) (line-end-position))))
          (let ((parts (split-string line)))
            (push (cons (car parts) (car (last parts))) permissions)))
        (forward-line 1))
      permissions)))