4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

EmacsAdvent Calendar 2015

Day 11

連想リストの参照を少しでも楽にしたい

Last updated at Posted at 2015-12-10

連想リストとは

連想リスト (Association List / alist) は (KEY . DATA) なペアを要素とするリストのことを指します。Emacs Lisp では次のように書きます。

(setq editor '((name . "emacs") (version . 24) (options . ("-nw"))))

JSON データを解析するライブラリ json.el はデフォルトではオブジェクト要素 {"KEY": DATA} を連想リスト ((KEY . DATA)) に変換するため、Web API を叩くコードを書いていると連想リストのデータをよく見かけます。

例えば、Qiita の新着投稿 API から JSON データを取得してバッファにタイトルと URL を列挙するコードはこんな感じで書けます。

(defun json-load (src)
  "ファイルまたはURLからJSONデータを読み込む."
  (with-temp-buffer
    (if (string-match "^https?://" src)
        (url-insert-file-contents src)
      (insert-file-contents src))
    (json-read)))

(defun print-items (items)
  "ITEMSを整形して出力する."
  (cl-loop for item across items
           do (let ((title (cdr (assq 'title item)))
                    (url (cdr (assq 'url item))))
                (princ (format "Title: %s\n" title))
                (princ (format "URL  : %s\n" url))
                (terpri))))

(with-output-to-temp-buffer "*Qiita*"
  (print-items (json-load "https://qiita.com/api/v1/items?per_page=50")))
;;-> *Qiita* バッファに結果が出力される

問題点

上記のコードのように連想リストを参照する方法は assoc (あるいは assq) 関数と car/cdr を利用するのが一般的ですが、(cdr (assoc 'title item)) というコードは正直あまり書きやすいとは言えません。他のプログラミング言語 によくある item.title といったドット記法でさくっと書けたら楽なのに…と思うことがよくあります。

この辺りの話題は去年の今頃にもスタック・オーバーフローでも質問を投げてみたのですが、残念ながらあまり有効な回答は得られませんでした。 1

ただ、最近になってこの問題を解消してくれるライブラリをいくつか見つけた&思いの外使い勝手が良かったのでメモがてらに書き残しておきます。

解決策その1 let-alist.el

syntax: (let-alist ALIST &rest BODY)

let-alist.el は引数に与えられた連想リストを .KEY のようなドットで始まるシンボルで参照できるようになるマクロを提供します。

これを利用すると、先ほどのコードの一部は次のように書き直すことができます。

(require 'let-alist)

(defun print-items (items)
  "ITEMSを整形して出力する."
  (cl-loop for item across items
           do (let-alist item 
                (princ (format "Title: %s\n" .title))
                (princ (format "URL  : %s\n" .url))
                (terpri))))

少しだけですが見やすくなりました。
let-alist は参照するキーの数が多ければ多いほど、面倒なタイプ数を減らすことのできるライブラリともいえます。

特徴は以下のとおり

  • ○ elpa (GNU Emacs の標準パッケージリポジトリ) で配布
  • ○ Emacs25 からは標準で利用できる(予定らしい)
  • ○ リストのネストした参照も可能 (.foo.bar)
  • ✗ 扱えるキーはシンボルのみ
  • ✗ setf による再代入はできない

こんな具合にいくつかの制限はありますが、ほぼ標準なライブラリを使って直感的にコードが書けるという点はその不満を上回るくらいには便利かなと思います。

参考リンク

解決策その2 dash.el

syntax: (-let (((&alist key0 a0 ... keyN aN)) &rest BODY) 2

Emacs Lisp の定番ライブラリ dash.el には -let という let の拡張版マクロが含まれていて、パターンマッチ近いシンボルの束縛ができるようになります。また、連想リストだけでなくハッシュやプロパティリスト(plist)でも利用可能なため、こちらの方が汎用性は高いです。

これを利用すると、先ほどのコードの一部は次のように書き直すことができます。

(require 'dash)

(defun print-items (items)
  "ITEMSを整形して出力する."
  (cl-loop for item across items
           do (-let (((&alist 'title title 'url url) item))
                (princ (format "Title: %s\n" title))
                (princ (format "URL  : %s\n" url))
                (terpri))))

let-alist よりは若干タイプ数が多くなりますが、こちらも直感的に書けます。

  1. http://ja.stackoverflow.com/q/2741/2391

  2. https://github.com/magnars/dash.el#-let-varlist-rest-body

4
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?