これってどう使うの?(2)
この記事は関西Lispユーザ会アドベントカレンダー4日目です。
「これってどう使うの?」の二回目は、format
の~^
指定子(Escape Upward)
の前置パラメータです。重箱の隅をつつくような話で恐縮です。
format
はC言語のprintf
みたいなものです。%
の代わりに~
を使います
(どうでもいいことですが、format
の中でついつい%
や\n
を書いてしまった
ことってありませんか)。
CL-USER> (setq x 5)
5
CL-USER> (format nil "The answer is ~d." x)
"The answer is 5."
format
はprintf
と比べてはるかに高機能です。全機能を解説したら、一冊の本が出来上る
くらいなのではと思えます。その中でお気に入りは~{~}
指定子(繰返し)です。リストを引数に
取り、リストの要素に対して繰返し出力をします。
CL-USER> (setq x '(1 2 3 4 5))
(1 2 3 4 5)
CL-USER> (format nil "~{~d,~}" x)
"1,2,3,4,5,"
最後のコンマが邪魔です。こういう場合のために(本当にそうか?)、~^
指定子
(Escape Upward)
が用意されています。(この例程度なら、perl等ではjoinが使えるぞ〜、という声が聞こえてきそうですが)
CL-USER> (format nil "~{~d~^,~}" x)
"1,2,3,4,5"
~^
(Escape Upward)は処理する引数がなくなると、その時点で処理を終了します。
~{~}
の中だけで使うという制約がある訳ではありません。わざとらしい例を書くと
(Common Lisp HeyperSpecの例 を少し手直し)、
CL-USER> (format nil "Done.~^ warning[~d].~^ error[~d].")
"Done."
CL-USER> (format nil "Done.~^ warning[~d].~^ error[~d]." 3)
"Done. warning[3]."
CL-USER> (format nil "Done.~^ warning[~d].~^ error[~d]." 3 5)
"Done. warning[3]. error[5]."
と、こんな具合に使えます。
format
の指定子では、指定子そのものの動きをコントロールするために前置パラメータ
というものが使えます。数字の出力では出力カラムを指定できます(もちろん他にもいろいろあります)。
CL-USER> (setq x 5)
5
CL-USER> (format nil "~3d" x)
" 5"
特殊な前置パラメータの指定方法として、~v
と~#
があります。~v
はformat
の引数を消費して前置パラメータの値とします。~#
はformat
の引数の残り数を
前置パラメータの値とします。
CL-USER> (format nil "~vd" 10 x) ; 出力カラム数として10を指定
" 5"
CL-USER> (format nil "~#d" x 10) ; 引数(xと10)の数である2が出力カラム数
" 5"
やっと本題です。~^
指定子も前置パラメータが使えます。書式としては
[x [,y [,z]]]
となっており、最大三つのパラメータが使えるということなります。
パラメータが一つの時は、パラメータ値が0の場合に処理を終了するという動きになります。
では、パラメータ値が0になるのはどんな時でしょうか?それは~v
や~#
を使った時です。
実際、~^~
は~#^
と同値です。~{~}
の中では、~#
は引数の残りではなく、処理している
引数(リスト)の未処理の要素の数となります。前置パラメータを使った例を~{~}
指定子で
無理やり作ると、
CL-USER> (setq x '(3 2 1 0 -1 -2))
(3 2 1 0 -1 -2)
CL-USER> (format nil "~{~d~v^,~}" x)
"3,1"
というところでしょうか。パラメータが二つ場合は両者が等しい場合に処理を終了します。 次の例ではリストの最後の二つを出力しません。
CL-USER> (setq x '(3 2 1 0 -1 -2))
(3 2 1 0 -1 -2)
CL-USER> (format nil "~{~d~2,#^,~}" x)
"3,2,1,0"
さて、パラメータが三つの場合(~x,y,z^
と指定したとする)ですが、
x <= y <= z
となる場合に処理を終了します。このパラメータ三つが、
「これってどう使うの?」というものです。~^
の前置パラメータは
~v
や~#
と組み合わせることになりますが、~#
は基本的に減少していくので、
上下限の設定は意味がなさそうに思えます。
(もしかすると、~{~}
の中で~*
を使うと~#
は増加することもあるのかな。でもどう使う?)
format
の外で計算した結果を~v
でformat
に渡すということはできるでしょうが、
どこかイケテない気がします。
パラメータ三つはどういうことを想定しているのでしょうね?
~{~}
では使い道はなそさう。~{~}
以外では案外自然な使い方があったりするのでしょうか?
余談: 上の例を動かしている時に間違えて次のようにしてしまいました。
CL-USER> (setq x '(3 2 1 0 -1 -2))
(3 2 1 0 -1 -2)
CL-USER> (format nil "~{~2,#^,~}" x)
結果は無限ループ。printf
みたいなもので無限ループしてしまうとは
「common lisp恐るべし」ですね。
明日はmyao_s_mokingさんの 「Common Lispのwrite関数のオプショナル引数について」 です。