formatはCommon Lispの関数で、印刷書式文字列に似た書式文字列を使用して書式設定されたテキストを生成できます。 よりも多くの機能が提供され、数値をさまざまな形式 (16進数、2 進数、8 進数、ローマ数字、英語など) で出力したり、特定の条件でのみ特定の書式指定子を適用したり、データ構造を反復処理したり、データを表形式で出力したり、を再帰的に呼び出して、独自の書式文字列を含むデータ構造を内部的に処理したりすることもできます。この機能は、MITのLisp Machine Lisp [1]に由来しており、 Multicsに基づいています。
printformat
仕様
この関数は次の構文formatで指定される:[2]
フォーマット宛先 コントロール 文字列&rest formatArguments
制御文字列内のディレクティブはフォーマット引数を使用して補間され、そのように構築された文字シーケンスが宛先に書き込まれます。
行き先
出力先は、ストリーム、動的文字列、T定数のいずれかですNIL。定数は、新しい文字列オブジェクトを作成し、フォーマットして返すという特別なケースです。一方、 は標準出力Tを参照し、通常はコンソールと同等です。Common Lisp のストリームには、文字列出力やファイル ストリームなどが含まれます。したがって、この関数は、さまざまな出力先に書き込むことができるため、コンソール出力、文字列フォーマット、ファイル書き込み用のCなど、他のプログラミング言語の個別のコマンドに分散されている機能を統合します。
printfsprintffprintf
多数の宛先タイプを次に示します。
;; 標準出力に「1 + 2 = 3」を出力し、「NIL」を返します。
( format t "1 + 2 = ~D" 3 )
;; 「1 + 2 = 3」を含む新しい文字列を作成して返します。
( format nil "1 + 2 = ~D" 3 )
;; 「1 + 2 = 3」を含む新しい文字列を作成して返します。
( with-output-to-string ( output ) ( format output "1 + 2 = ~D" 3 ))
;; ファイル "outputFile.txt" に文字列 "1 + 2 = 3" を書き込みます。
( with-open-file ( output "outputFile.txt" :direction :output :if-does-not-exist :create :if-exists :append ) ( format output "1 + 2 = ~D" 3 ))
;; 動的文字列に文字列 "1 + 2 = 3" を追加します。
( let (( output-string ( make-array 0 :element-type 'character :adjustable t :fill-pointer 0 ))) ( declaration ( type string output-string )) ( format output-string "1 + 2 = ~D" 3 ) (文字列output-string ))
制御文字列とフォーマット引数
制御文字列には、リテラル文字だけでなく、フォーマット ディレクティブを区切るメタ文字 ~(チルダ)も含めることができます。入力内のリテラルはそのままエコーされますが、ディレクティブは特別な出力を生成し、多くの場合、1 つ以上のフォーマット引数を消費します。
指令
によって導入されるフォーマットディレクティブの後に、 0 個以上のプレフィックスパラメータ、0 個以上の修飾子、およびディレクティブ タイプ
~が続きます。したがって、ディレクティブ定義は次のパターンに準拠する必要があります。
~[プレフィックスパラメータ][修飾子]ディレクティブタイプ
ディレクティブ タイプは常に 1 文字で指定され、文字の場合は大文字と小文字が区別されません。フォーマット ディレクティブによって処理されるデータは、必要な場合はフォーマット引数と呼ばれ、互換性のある任意のタイプの 0 個以上のオブジェクトになります。このようなデータが受け入れられるかどうか、受け入れられる場合の量は、ディレクティブとそれに適用される潜在的な修飾子によって異なります。~%たとえば、ディレクティブ タイプ は、フォーマット引数を一切使用しませんが、 は~D正確に 1 つの整数が印刷されることを想定し、~@{アットマーク修飾子の影響を受けるディレクティブ は、残りのすべての引数を処理します。
次のディレクティブは、~Bフォーマット引数から 1 つの数値オブジェクトを期待し、その2 進数(基数2) に相当するものを標準出力に書き込みます。
(フォーマットt "~B" 5 )
構成が許可されている場合は、プレフィックス パラメータを指定できます。
プレフィックスパラメータ
プレフィックス パラメータを使用すると、関数にパラメータを指定した場合と同様に、操作するディレクティブに追加情報を注入できます。プレフィックスパラメータは常にオプションであり、指定する場合は、導入部と修飾子の間、または修飾子がない場合はディレクティブ タイプの間に配置する必要があります。値はコンマで区切られますが、どちらの側にも空白は許可されません。これらのパラメータの数とタイプは、ディレクティブと潜在的な修飾子の影響によって異なります。
~
2 つの特殊文字は、異なる解釈を持つプレフィックス パラメータ値として使用できます。vまたは は、Vフォーマット引数の整数または文字のプレースホルダとして機能し、その文字が消費されてその位置に配置されます。2 番目の特殊文字 は、#フォーマット引数の合計によって置き換えられますが、消費は維持されます。 と はどちらもV、#プレフィックス パラメータ リストに挿入された動的コンテンツによって定義される動作を有効にします。
パラメータ値は、一般的なプログラミングのコンテキストにおける変数Vと同等の機能を導入します。この単純なシナリオでは、整数のバイナリ表現の左側に少なくとも 8 桁のゼロを埋め込むためのリテラル ソリューションは次のようになります。
5
(フォーマットt "~8,'0b" 5 )
ただし、出力幅を制御する最初のプレフィックスパラメータは、文字で定義されV、パラメータ値の指定が次のフォーマット引数 (この場合は ) に委任される場合があります8。
(フォーマットt "~v,'0b" 8 5 )
この種のソリューションは、次のコードの場合のように、プレフィックス パラメータ リストの一部をリテラルではなく 変数または関数の引数で記述する場合に特に便利です。
( let ((桁数8 )) ( declare ( type (整数0 * )桁数)) ( format t "~V,'0b"桁数5 ))
外部入力を伴う状況では、関数の引数をフォーマット ディレクティブに渡すこともできます。
( defun print-as-hexadecimal ( number-to-format number-of-digits ) "NUMBER-TO-FORMAT を 16 進数 (基数 16) で出力し、 少なくとも NUMBER-OF-DIGITS になるまで左側にゼロを埋め込みます。" ( declaration ( type number number-to-format )) ( declaration ( type ( integer 0 * ) number-of-digits )) ( format t "~V,'0x" number-of-digits number-to-format ))
( 16進数で印刷12 2 )
#プレフィックス パラメータは、前のディレクティブによってまだ処理されていないフォーマット引数を集計しますが、このリストの引数は実際には使用されません。このように動的に挿入される値の有用性は、主に条件付き処理に関連する使用例に限定されます。引数番号は 0 以上の整数のみであるため、その意味は条件付き~[ディレクティブの句のインデックスの意味と一致します。
#特別なプレフィックス パラメータ値と条件付き選択ディレクティブの相互作用を~[次の例に示します。条件には、それぞれインデックス 0、1、2、3 を介してアクセスできる 4 つの節が指定されています。節インデックスの取得手段として、フォーマット引数の数が使用されます。これを行うには、#インデックスをプレフィックス パラメータにすることを許可する条件ディレクティブにを挿入します。#フォーマット引数の合計を計算し、この数を選択インデックスとして提案します。この動作によって消費されない引数は、選択された節のディレクティブで使用でき、処理されます。
;; 「選択なし」を出力します。
( format t "~#[選択なし~;1 つ選択: ~A~;2 つ選択: ~A と ~A~:;さらに選択: ~@{~A~^, ~}~]" )
;; 「1 つ選択: BUNNY」を出力します。
( format t "~#[none selected~;one selected: ~A~;two selected: ~A and ~A~:;more selected: ~@{~A~^, ~}~]" 'bunny )
;; 「2 つ選択済み: BUNNY と PIGEON」を出力します。
( format t "~#[選択なし~;1 つ選択済み: ~A~;2 つ選択済み: ~A と ~A~:;さらに選択済み: ~@{~A~^, ~}~]" 'bunny 'pigeon )
;; 「さらに選択: ウサギ、ハト、ネズミ」を出力します。
( format t "~#[選択なし~;1 つ選択: ~A~;2 つ選択: ~A と ~A~:;さらに選択: ~@{~A~^, ~}~]" 'ウサギ 'ハト'ネズミ)
修飾子
修飾子は、ディレクティブの動作に影響を与えるフラグとして機能します。 プレフィックスパラメータと同様に、動作変更の許容範囲、程度、および効果は、ディレクティブによって異なります。 深刻なケースでは、ディレクティブの構文が、特定のプレフィックス パラメータを無効にする程度まで変更されることがあります。 この機能は、修飾子をほとんどのパラメータと特に区別するものです。 有効な修飾子文字は 2 つあり、 @(アットマーク) と(コロン) です。または:の組み合わせで使用できます。
:@@:
次の例は、修飾子によってディレクティブに及ぼされる影響の比較的軽微な例を示しています@。これは、フォーマットされた数値のバイナリ表現の前に常に数値の符号が付くことを保証するだけです。
(フォーマットt "~@B" 5 )
フォーマットディレクティブ
完全な構文と修飾子の効果を含むフォーマットディレクティブの一覧を以下に示す。[3]
例
printf C呼び出しの例は次のとおりです。
printf ( "色 %s、数値1 %d、数値2 %05d、16進数 %x、浮動小数点数 %5.2f、符号なし値 %u。\n " 、
"赤" 、123456 、89 、255 、3.14 、250 );
Common Lisp を使用すると、これは次と同等になります。
( format t "Color ~A, number1 ~D, number2 ~5,'0D, hex ~X, float ~5,2F, unsigned value ~D.~%" "red" 123456 89 255 3.14 250 ) ;; 出力: Color red, number1 123456, number2 00089, hex FF, float 3.14, unsigned value 250.
もう一つの例として、リストの各要素をカンマで区切って印刷する方法があります。これは、 ~{、~^、~ }ディレクティブを使用して実行できます。[4]
( let (( groceries ' ( eggs bread butter carrots ))) ( format t "~{~A~^, ~}.~%" groceries ) ; 大文字で出力します( format t "~:(~{~A~^, ~}~).~%" groceries )) ; 出力を大文字にします;; 出力: EGGS, BREAD, BUTTER, CARROTS. ;; 出力: Eggs, Bread, Butter, Carrots.
値のリストが によって直接反復処理されるだけでなくformat、カンマが項目の後ではなく項目間に正しく印刷されることに注意してください。さらに複雑な例として、慣習的な英語のフレーズを使用してリストを印刷します。
( let (( template "幸運な勝者は次のとおりです:~#[ none~; ~S~; ~S and ~S~ ~:;~@{~#[~; and~] ~S~^,~}~]." )) ( format nil template ) ;; ⇒ "幸運な勝者は次のとおりです: none." ( format nil template 'foo ) ;; ⇒ "幸運な勝者は次のとおりです: FOO." ( format nil template 'foo 'bar ) ;; ⇒ "幸運な勝者は次のとおりです: FOO と BAR." ( format nil template 'foo 'bar 'baz ) ;; ⇒ "幸運な勝者は次のとおりです: FOO、BAR、および BAZ." ( format nil template 'foo 'bar 'baz 'quux ) ;; ⇒ "幸運な勝者は次のとおりです: FOO、BAR、BAZ、および QUUX." )
を通じて新しいディレクティブを定義する機能は、カスタマイズの手段を提供します。次の例では、入力文字列を小文字、大文字、または逆のスタイルで印刷する関数を実装し、繰り返し回数の構成も許可します。
~/functionName/
( defun mydirective ( destination format-argument colon-modifier-supplied-p at-sign-modifier-supplied-p &optional ( repetitions 1 )) "この関数は、``format'' 呼び出しのディレクティブとして適切なコールバックを表し 、文字列を FORMAT-ARGUMENT として期待し 、DESTINATION に REPETITIONS 回数印刷します。 --- COLON-MODIFIER-SUPPLIED-P フラグと AT-SIGN-MODIFIER-SUPPLIED-P フラグは、それぞれ ``:'' と ``@'' 修飾子 の代表である一般化されたブール値を必要とします。それらの影響は 次のように定義されます: - 修飾子が設定されていない場合、FORMAT-ARGUMENT は それ以上変更されずに印刷されます。 - コロン修飾子が設定されていて、アット サイン修飾子が設定されていない場合、 FORMAT-ARGUMENT は印刷前に小文字に変換されます。 - アット マーク修飾子が設定されていて、コロン修飾子が設定されていない場合、 FORMAT-ARGUMENT は、印刷前に大文字に変換されます。 - 両方の修飾子が設定されている場合、FORMAT-ARGUMENT は印刷前に逆順にされます 。 --- FORMAT-ARGUMENT 文字列が印刷される回数は 、プレフィックス パラメータ REPETITIONS によって決定されます。この パラメータは、負でない整数でなければならず、デフォルトは 1 です。" ( declare ( type ( or null ( eql t ) stream string ) destination )) ( declare ( type t format-argument )) ( declare ( type t colon-modifier-supplied-p )) ( declare ( type t at-sign-modifier-supplied-p )) ( declare ( type ( integer 0 * ) repetitions )) ( let (( string-to-print format-argument )) ( declare ( type string string-to-print )) ;; 修飾子に基づいて STRING-TO-PRINT を調整します。( cond (( andコロン修飾子指定pアットマーク修飾子指定p ) ( setf印刷する文字列(
逆順に文字列を印刷する))) (コロン修飾子で指定したp ( setf文字列を印刷する(文字列を小文字にする 文字列を印刷する))) (アットマーク修飾子で指定したp ( setf文字列を印刷する(文字列を大文字にする 文字列を印刷する))) ( t nil )) ( loop repeat繰り返しを実行する do (フォーマット先"~A"文字列を印刷する))))
;; 「Hello」を1回出力します。
( format t "~/mydirective/" "Hello" )
;; "Hello" を 3 回出力します。
( format t "~3/mydirective/" "Hello" )
;; 小文字の「Hello」(=「hello」)を3回出力します。
(format t "~3:/mydirective/" "Hello" )
;; 大文字の「Hello」(=「HELLO」)を3回印刷します。
(format t "~3@/mydirective/" "Hello" )
;; 逆順にした「Hello」(=「olleH」)を3回印刷します。
(format t "~3:@/mydirective/" "Hello" )
format不透明で読みにくい傾向があることでやや悪名高いが、特殊かつ一般的なニーズに対して、非常に簡潔でありながら強力な構文を提供する。[ 4]
Common Lisp FORMATの概要表が利用可能です。[5]
参照
参考文献
- ^ 「Lisp Machine Manual」(PDF) p. 85。2024年6月7日閲覧。
- ^ 「CLHS: 関数フォーマット」www.lispworks.com。
- ^ 「CLHS: セクション 22.3」www.lispworks.com。
- ^ ab 18.実用的な Common Lispからのいくつかの FORMAT レシピ
- ^ Common Lisp FORMAT の概要表
書籍
- Common Lisp HyperSpecセクション 22.3 フォーマットされた出力
- 実践的 Common Lisp第 18 章 FORMAT レシピ
