CL-INTERPOL: 文字列表現の拡張

概要

このページではCommon Lispの文字列表現を拡張するCL-INTERPOLについて簡単に説明します。

正規表現ライブラリであるCL-PPCREと併用すると便利ですので、そちらも参照してください。

cl-interpolはリーダーマクロをフル活用したライブラリです。リーダーマクロに関する理解は必須ですので、リーダーマクロについて初耳の方は標準仕様入門の第2章「構文」を参照してください。

このページではASDFでcl-interpolをロードした後、enable-interpol-syntaxマクロで構文を有効化した状態で説明します。
(asdf:load-system :cl-interpol)
; => T

(interpol:enable-interpol-syntax)
; (返り値なし)

disable-interpol-syntaxマクロで構文を無効化することもできます。
(interpol:disable-interpol-syntax)
; (返り値なし)

文字列の表現(デリミタの切り替え)

ANSI Common Lispでは文字列は文字のベクトルであり、定数表現はダブルクォーテーションで囲みます。
"abc"
; => "abc"

これで通常は何の問題もありませんが、しばしば厄介な場合も出てきます。一つはダブルクォーテーション自体を使いたい場合です。
"a\"b\"c"
; => "a\"b\"c"

(length "a\"b\"c")
; => 5

もう一つの厄介な場合は、バックスラッシュを使用する場合です。バックスラッシュはANSI Common Lispで文字を表すリーダーマクロになっているため、文字列の中で1つで使うと、事実上消滅してしまします。
"a\bc"
; => "abc"

"a\\bc"
; => "a\\bc"

(length "a\\bc")
; => 4

cl-interpolは文字列定数表現をダブルクォーテーション以外にも拡張します。使用するにはシャープ・クエスチョン(#?)ディスパッチマクロを使います。
#?/abc/
; => "abc"

#?/a"b"c/
; => "a\"b\"c"

#?/a\bc/
; => "a\\bc"

ダブルクォーテーション以外に使用可能な区切り文字のリストは*outer-delimiters*スペシャル変数で確認できます。
interpol:*outer-delimiters*
; => ((#\( . #\))
;     (#\{ . #\}) 
;     (#\< . #\>)
;     (#\[ . #\])
;      #\/
;      #\|
;      #\"
;      #\'
;      #\#)

特殊文字の印字

cl-interpolのパワーは単にデリミタを切り替えられるだけではありません。バックスラッシュを用いて改行コードやタブ文字を入れることができます。
#?"abc\ndef"
; => "abc
;    def"

#?"abc\tdef"
; => "abc def"

簡易フォーマット

cl-interpolでは改行やタブだけでなく、文字列定数表現の中に式を埋め込むことができます。

ANSI Common Lispでは非常に高機能なformat関数が定められており、これを使うと文字列をプログラム的に生成することができます(第22章「出力(format関数)」を参照のこと)。ただ、高機能すぎて少し直感的ではなく、一般的に普及しているシェルスクリプトやPerlの流儀とは異なるため、最初は扱いづらいかもしれません。

cl-interpolではシェルスクリプトやPerlに近い形でフォーマットを提供してくれます。
;; 変数のフォーマット
(let ((foo 10)) #?"foo = ${foo}")
; => "foo = 10"

;; リストのフォーマット
(let ((foo '(1 2 3 4 5))) #?"foo = @{foo}")
; => "foo = 1 2 3 4 5"

;; リストのデリミタを切り替えて使う
(let ((foo '(1 2 3 4 5))
      (interpol:*list-delimiter* ", "))
  #?"foo = @{foo}")
; => "foo = 1, 2, 3, 4, 5"

;; 式のフォーマット
(let ((x 1)(y 2))
  #?"x = ${x}, y = ${y}\n(x + y) = ${(+ x y)}")
; => "x = 1, y = 2
;    (x + y) = 3"

一般の変数はドル記号を、リストはアットマークを使います。

もちろん、これらはANSI標準のformat関数でも実現できますが、少し冗長です。
(let ((foo 10)) (format nil "foo = ~a" foo))
; => "foo = 10"

(let ((foo '(1 2 3 4 5))) (format nil "foo = ~{~a~^ ~}" foo))
; => "foo = 1 2 3 4 5"

(let ((foo '(1 2 3 4 5))) (format nil "foo = ~{~a~^, ~}" foo))
; => "foo = 1, 2, 3, 4, 5"

(let ((x 1)(y 2))
  (format nil "x = ~a, y = ~a~%(x + y) = ~a" x y (+ x y)))
; => "x = 1, y = 2
;    (x + y) = 3"


一般に、リーダーマクロは影響力が大きいので慎重に使うべきとされています。ただ、cl-interpolは広く普及したライブラリとして定番となっており、使用を躊躇する必要はありません。特にCL-PPCREで正規表現を使う場合は強力な武器になりますので、積極的に活用してください。

0 件のコメント :

コメントを投稿