Commits

camlspotter committed e000d35 Merge

merge

Comments (0)

Files changed (9)

+>>
+´ŘϢĽęĽóĽŻ:
+<a href="http://d.hatena.ne.jp/camlspotter/20091013/1255443545
+">OCaml ɸ˝ŕĽéĽ¤ĽÖĽéĽęþˏ Âč0˛ó</a>
+¤˝¤Îž¤Î˛ó¤ĎÂč0˛ó¤ÎĽČĽéĽĂĽŻĽĐĽĂĽŻ¤č¤ę¤´Í÷¤Ż¤Ŕ¤ľ¤¤ĄŁ
+<<

log/ocaml-lib1.txt

 
 ** prerr_endline
 
-文字をプリントする関数は標準出力へ出力する print_* (print_endline, print_string, print_int ...)系 と、
+文字をプリントする関数は、
+
+- 標準出力へ出力する print_* (print_endline, print_string, print_int ...)系
+- エラー出力へ出力する prerr_* (prerr_endline, prerr_string, prerr_int ...)系
+- output channel へ出力する output_* 系
+
+と三つ用意されています。多分一番使うのが prerr_endline。デバッグ用です。(標準出力の print_endline でも良いですが、エラー出力を使った方がよいでしょう。) 
+prerr_endline "hogehoge" と prerr_string "hogehoge\n" は似ていますが、違うことに注意してください。前č€
+
+>>
+OCaml には二段階のバッファリングがあることに注意してください。まず文字列は OCaml ランタイムが持つバッファに送られます。(byterun/io.h) ここにある程度溜まると、Unix のファイルハンドルのバッファにまとめて送られます。
+OCaml ランタイムにはバッファリングされていても、Unix のファイルハンドルへのバッファに移されていないデータは、プログラムが異常終了すると、失われてしまいます。デバッグメッセージはĺż
+<<
 
 ** format, format4, format6
 
 
 ** at_exit
 
+at_exit で登録されたコールバック関数は、プログラムが終了する際に呼び出されます。プログラム終了処理に便利です。
+
 ** モジュール名 "Pervasives" を敢えて使う
 
+Pervasives はデフォルトで open されたモジュールなので、その中で定義された値や型はわざわざ Pervasives.prerr_endline などとモジュール名つきで使用するĺż
+ただ、敢えて Pervasives を使う場合もあります。例えば、Pervasives で定義されている値と同名の値を定義した環境で、Pervasives の値を使いたい場合です:
+>|ocaml|
+let (+) = Int32.add
+let (-) = Int32.sub
+...
+let v = Pervasives.(+) e1 e2
+||<
+あるモジュールが int32 なおぎ int 以外の数値型を主に使う場合、関連モジュール (ここでは Int32) が提供する四則演算(Int32.add, Int32.sub)などの長い名前をいちいち書くのは面倒ですから、(+) や (-) などの数値演算子として定義し直すことがよくあります。もしそのような環境でĺ
+残念ながら、predefined type や predefined exception の名前やコンストラクタが再定義された場合、ĺ
+>|ocaml|
+type t = Some
+(* 以降、もう Some を 'a option のコンストラクタとして使うことは出来ません *)
+let some_one = Pervasives.Some 1 (* エラー。無駄です。Some は Pervasives で定義されている訳ではありません. *)
+||<
+
 ** 次回は List です。
 
 モジュール探訪第一回は基本中の基本、 Pervasives でした。基本の割には避けて通れない重要なポイントがいくつもあり、初回から異様に長くなってしまいましたが、次からはもっと短くなるはずです。よろしくお願いします。

log/ocaml-lib2.txt

+*[OCaml][Tutorial] OCaml 標準ライブラリ探訪 #2 List : 計算量に注意
+
+>>
+関連リンク:
+<a href="http://d.hatena.ne.jp/camlspotter/20091013/1255443545
+">探訪 #0</a>
+その他の回は第0回のトラックバックよりご覧ください。
+<<
+ 
+標準ライブラリ探訪第二回は List です。このモジュールは関数型言語で最も基本的な再帰データ型である list 型のデータを扱う関数群からなっています。
+
+OCaml では list は predefined な型で、その定義は標準ライブラリにはなく、コンパイラにとって既知になっています。(詳しいĺ†
+>|ocaml|
+type 'a list = 
+  | []                  (* nil *)
+  | :: of 'a * 'a list  (* cons *)
+||<
+もちろん上の式は [] という OCaml では variant constructor として許されない変な記号を使っているのでエラーになります。
+
+>>
+>>
+づなみき [] はダメですが、なぜか :: はコンストラクタの名前として正しく認識されます。OCaml 3.11.1 でコンストラクタに使える変な名前は ::, true, false, () の四つ。使うと難読化になる、かもしれません、けど、一度定義すると以降の名前空間では二度とĺ
+>|ocaml|
+ type t = :: | true of int | false of unit | () of t * t
+let _ = true 1 :: false (prerr_endline "howdy") :: ();;
+||<
+<<
+<<
+
+list 型のĺ
+
+** 末尾再帰: non tail recursive v.s. tail recursive 
+
+List で一番注意しなければいけないこと、それは、一部の関数が tail recursive で無いことです。Non tail recursive な関数では関数の戻りアドレスをスタックに積むため、あまり多くの回数再帰するとスタックを使いきいて Stack_overflow 例外を発生します:
+>|ocaml|
+let cntr = ref 0
+
+let rec f () =
+  incr cntr;
+  f (); ()  (* 末尾再帰ではない *)
+;;
+
+let _ = try f () with _ -> Printf.eprintf "cntr=%d\n" !cntr;;
+||<
+上の例では f () の再帰呼び出しの後に敢えて ; () を置いて non tail rec にしています。このプログラムを動かすと、スタックを使い切ってしまってエラーがでた際の呼び出しの深さを表示します。私の ocaml toplevel では 262045 でした。
+
+この f (); () を f () にしてやると tail rec になります。Tail recursive だと戻りアドレスを覚えるĺż
+
+え、tail recursion (末尾再帰) って何だって? それは多分検索すると山の様に出てきますから、そっちで勉強してくださいね。
+
+>>
+>>
+OCaml は上の例でもわかるように non tail rec を tail rec に自動的に最適化するという気の利いたことをしてくれません。f (); () も f () に自動的に簡約してくれたら良さそうですが、それもしてくれません。とても実直なコンパイラですね。
+<<
+<<
+
+ともかく、OCaml では再帰関数を tail rec で書くことが時に重要です。特にライブラリ関数を書いている時。ライブラリを使っている人がどんな深さの再帰をしてくるか想定できませんからね。
+
+** List モジュールには non tail rec 関数がたくさんある!!
+
+さて、tail rec が重要、ということを頭にĺ
+
+あれれれ、ĺ
+
+例えば、List.map を見てみましょう:
+
+>|ocaml|
+let rec map f = function
+    [] -> []
+  | a::l -> let r = f a in r :: map f l
+||<
+
+最後が r :: map f l になっている。Tail rec ではありません。ということは、、、
+>|ocaml|
+# let rec list st  = function 0 -> st | n -> list (n :: st) (n-1);;
+# let _ = List.map (fun x -> x) (list [] 300000);; (* 上の環境と同じなので、この回数でスタックを使い果たす *)
+Stack overflow during evaluation (looping recursion?)
+||<
+案の定 Stack overflow してしまいます。
+
+List.map をはじめとして、List モジュールの多くの関数は tail rec ではありません。そのため、与えるリストの深さが深すぎて再帰の回数を多くなりすぎると Stack overflow します。
+
+これはライブラリの欠陥ではなく、敢えて選んだ物だと OCaml コンパイラ開発プロジェクトの Xavier Leroy が次の様に説明しています:
+
+>http://groups.google.com/group/fa.caml/msg/01e80aadf16837d6?pli=1>
+Let me put this another way.  There are some library functions for
+which no "one size fits all" implementation exist: i.e. the
+implementation favors one style of use over others.  For List.map or
+list concatenation, for instance, one has to choose between running at
+full speed on small to medium-sized lists, or being tail-recursive and
+handling arbitrarily large lists at a significant cost in speed and
+clarity of implementation.  I chose the former approach.  So, if you
+really need to handle large lists, you may have to write your own
+(tail-rec and slow) map function.  It's not really code duplication,
+in that your map function will have quite different running behavior
+than the one in the standard library.   
+<<
+Tail rec ぎ map は stack overflow はしない代わりに、 non tail rec バージョンの map が使う二倍の量をヒープで使用するため、効率が悪い。そのため、 non tail rec のわかりやすく効率の良い実čŁ
+
+この話は List モジュールの他の non tail rec な関数にも当てはまります。Non tail rec はわかりやすい定義で、効率が良いが、 Stack_overflow する可能性がある。なんとかとハサミは使い様。この辺を理解して使ってください。
+
+>>
+>>
+では、小中のリストでは non tail rec で、大きいリストでは tail rec を使って Stack_overflow しない map の実čŁ
+<<
+<<
+
+** 関数紹介
+
+** length
+実直にパターンマッチを使って長さを数えるので深さに比例した時間がかかります。それが嫌な場合は他のデータ型を使うか、自分で長さを別ćƒ
+
+** hd, tl
+もちろん、head, tail の略です。[] が与えられると Failure 例外を発生しますので、明らかに引数が非 null リストであるとわかっている時くらいにしか使いません。普段はパターンマッチを使って null の場合も押さえた書き方をした方がよいでしょう。
+
+** rev
+
+さすがに、 rev は tail rec で書いてあります。ナイーブに書くと Stack_overflow 以前に計算量が爆発しますから。(Tail rec バージョンの map の非効率性とは比べ物にならないほど爆発する。)
+>|ocaml|
+let rec dame_rev = function
+  | [] -> []
+  | x::xs -> dame_rev xs @ [x]   (* アッー何てことだ *)
+||<
+どうダメかわからない方は是非自分で実験される事をおすすめします。わからずに放っておかれる方は関数型言語は使わないでé
+
+** rev_append, rev_map
+
+rev と同じく tail rec です。append や map と比べると効率がよいですが、結果の要素の順序が引数の物と逆になります。(これを逆にしないでやると tail rec バージョンの map の様に効率に問題が出てきます。)
+
+- 順序が逆でも構わないとき
+- 順序が逆だと却ってうれしいとき
+- 順序が逆になる関数を二回使うので、結果はĺ
+
+に使うと効果的です。
+
+他の tail rec な関数、例えば、List.fold_left もĺ†
+
+** fold_left, fold_right
+
+fold (折りたたみ)は accumulator とリスト要素を受け取って計算を行い、新しい accumulator の値を得て、次のリスト要素へと計算を進めていく関数です。fold の使い方はこれまたいろんなĺ
+
+しかし、fold_left と fold_right の違いや型、リスト要素が関数に適用されていく順序が覚えにくいですね。良い覚えかたがあります。
+>>
+fold_left/right では、 Accumulator はリスト要素の 左/右 に来る。リストの要素は 左/右 から関数に適用されていく。
+<<
+>|ocaml|
+val fold_left : ('acc -> 'l -> 'acc) -> 'acc -> 'l list -> 'acc
+(* Tail rec。 fold_left は accumulator が left。リスト要素は left から関数に与えられていく。 *)
+
+val fold_right : ('l -> 'acc -> 'acc) -> 'l list -> 'acc -> 'acc
+(* Non tail rec。 fold_right は accumulator が right。リスト要素は right から関数に与えられていく。*)
+||<
+リスト要素の適用順は関数中で副作用が起こるとき重要です。
+
+>>
+>>
+Haskell の同等の関数 foldl, foldr は引数順が OCaml ぎ List.fold_left, List.fold_right とは違います。このため両č€
+<<
+<<
+
+初心č€
+
+*** fold で書けるなら fold を絶対使おう
+
+fold きć
+>>
+再帰関数を定義したら、それが fold で書き直せるかどうか検討せよ
+<<
+ちょっと誇張がĺ
+
+*** fold_left をなるべく使おう
+
+fold_right は non tail rec なので。left で簡単に書けるものを right で書いてはいけません。政治信条はともかく、List.fold は left でお願います。
+
+** mem, assoc
+
+リストが小さければ大丈夫ですが、あまりに大きくなるなら、Set や Hashtbl を使うことを検討しましょう。
+
+** stable_sort, sort, fast_sort
+
+なんだか .mli に色ă€
+
+** 終わり。次回は Printf
+
+教科書ではないのでわかりにくかった所もあると思いますが、OCaml の List で注意するべき点は大体挙げることが出来たのではないかと思います。
+
+次回は Printf です。

log/printf-euc.txt

+printf ¤Ă¤Ć OCaml ¤Ç¤âĘŘÍř¤Ç¤š¤č¤ÍĄŁC ¤Ť¤éϢĚʤČÂł¤¤¤Ć¤¤¤ëČž¤Đžďźą¤Î % Ľ¤ĽóĽżĄźĽŐĽ§ĄźĽš¤Ë˛Ă¤¨Ą˘¤Á¤ç¤Ă¤ČÉԝ׾ĤʡżżäĎŔ¤Î¤Ş¤Ť¤˛¤Çˇż°ÂÁ´Ŕ­¤âĘÝžÚ¤ľ¤ě¤Ć¤Ţ¤šĄŁprintf ťČ¤Ă¤Ć¤ĆˇżĽ¨ĽéĄź¤Ź¸Ť¤Ä¤Ť¤ë¤ż¤Ó¤ËĄ˘¤˘Ąź C ¤Ŕ¤Ă¤ż¤é seg fault ¤ˇ¤Ć¤ż¤Ť¤â¤ˇ¤ě¤ó¤ĘĄ˘Ą˘Ą˘ÎɤŤ¤Ă¤żÎɤŤ¤Ă¤żĄ˘¤Čť×¤¤¤Ţ¤šĄŁşŁĆü¤Ď¤˝¤ó¤Ę printf ˇĎ¤Î´Řżô¤ňÄ󜥤š¤ë Printf ĽâĽ¸ĽĺĄźĽë¤Î¤ŞĎĂĄŁ
+
+** Printf ¤ÎĆĂźě¤ĘˇżÉŐ¤ą
+
+printf ˇĎ¤Î´Řżô¤Ďžőśˇ¤Ë¤č¤Ă¤Ć°Ű¤Ę¤ëżô¤Î°úżô (variable length arguments (˛ÄĘŃÄš°úżô)) ¤ňźč¤ë¤ł¤Č¤Ź˝ĐÍč¤Ţ¤šĄŁÎ㤨¤ĐĄ˘
+>|ocaml|
+open Printf
+let name = "hogera";;
+let value = 42;;
+let () = printf "hello world\n";;
+let () = printf "your name is %s\n" name;;
+let () = printf "%s : %d\n" name value;;
+||<
+name ¤ä value ¤ÎĹŹÍѤň¤ť¤şĄ˘toplevel ¤ÇźÂšÔ¤š¤ë¤Čˇż¤Ź˝Đ¤Ć¤­¤Ć˛ň¤ę°×¤¤:
+>|ocaml|
+open Printf;;
+# printf "hello world\n";;
+hello world
+- : unit = ()
+# printf "your name is %s\n";;
+- : string -> unit = <fun>
+# printf "%s : %d\n";;
+- : string -> int -> unit = <fun>
+||<
+ML ¤ÎÉáÄ̤ΡżÉŐ¤ą¤Ç¤Ď¤ł¤ó¤Ęťö¤Ď˝ĐÍč¤Ţ¤ť¤óĄŁ¤˘¤ë´Řżô¤ËʸťúÎó¤ňÍż¤¨¤ĆĘ֤äƤ­¤żĂͤΡż¤Ź unit ¤Ŕ¤Ă¤ż¤ę string -> unit ¤Č¤¤¤Ś´Řżô¤Ŕ¤Ă¤ż¤ę¤š¤ë¤Î¤ĎĚŔ¤é¤Ť¤ËÉÔźŤÁł¤Ç¤šĄŁ˛ż¤ŤĆĂźě¤Ęťö¤ň¤ä¤Ă¤Ć¤¤¤ë¤Ë°ă¤¤¤˘¤ę¤Ţ¤ť¤óĄŁ${libdir}/printf.mli ¤Î¤Ç printf ¤Îˇż¤ň¸Ť¤Ć¤ß¤Ţ¤ˇ¤ç¤Ś:
+>|ocaml|
+val printf : ('a, out_channel, unit) format -> 'a
+||<
+˛ż¤Ŕ¤ł¤ě¤ĎĄŠ´Řżô¤ß¤ż¤¤¤Ç¤š¤ą¤ÉĄ˘Âč°ě°úżô¤Ď string ¤¸¤ă¤Ę¤Ż¤Ă¤Ć ('a, out_channel, unit) format ¤Č¤Ť¤¤¤ŚĽÇĄźĽżˇż¤Ç¤šĄŁ¤˝¤ˇ¤ĆĘÖ¤ęĂͤΡż¤ŹˇżĘŃżô 'aĄŁ¤ł¤ě¤Ď°ěÂΤɤŚ¤Ę¤Ă¤Ć¤¤¤ë¤Î¤Ç¤ˇ¤ç¤Ś¤ŤĄŁ
+
+*** źÂ¤ĎĆĂźě¤Ę¤Î¤ĎʸťúÎóÄężô¤ÎˇżÉŐ¤ą
+
+¤ł¤ł¤ŹşŁ˛ó°ěČÖĆń¤ˇ¤¤˝ęĄŁˇż¤Č¤Ťś˝ĚŁ¤Ę¤¤¤ČŔľÄžżÉ¤¤¤Ť¤éĄ˘śěźę¤ĘżÍ¤ĎźĄŔá¤ÎĄÖĆ°ĹŞ¤ĘĽŐĽŠĄźĽŢĽĂĽČʸťúÎóĄ×¤Ţ¤ÇČô¤Đ¤ˇ¤Ć¤Ż¤Ŕ¤ľ¤¤ĄŁ¤Ň¤Č¤Ţ¤şĄ˘¤ľ¤č¤Ś¤Ę¤éĄŁ
+
+printf ¤ÎÂč°ě°úżô¤Ď ('a, out_channel, unit) format ¤Č¤¤¤Śˇż¤Ç¤š¤ŹĄ˘¤˝¤ł¤ËʸťúÎó¤ňĹŹÍѤˇ¤Ć¤â ocaml ¤Ď˛ż¤Ë¤âʸśç¤ň¸Ŕ¤ď¤Ę¤¤ĄŁ¤ł¤ě¤Ď°ěÂΤɤŚ¤¤¤Ś¤ł¤Č¤Ç¤ˇ¤ç¤Ś¤ŤĄŠ
+>|ocaml|
+# "hello world\n";;
+- : string = "hello world\n"
+# "your name is %s\n";;
+- : string = "your name is %s\n"
+# "%s : %d\n";;
+- : string = "%s : %d\n"
+||<
+¤Ş¤Ť¤ˇ¤¤Ą˘¤ł¤ě¤Ď¤ß¤ó¤ĘʸťúÎó¤Î¤Ď¤şĄ˘¤Ę¤Î¤ËĄ˘Ą˘Ą˘format ¤Ę¤ó¤Ćˇż¤¸¤ă¤Ę¤¤¤č!!
+
+¤Ç¤ĎĄ˘´ş¤¨¤ĆĄ˘format ˇż¤Č¤ˇ¤Ć ocaml ¤ËÍż¤¨¤ë¤Č¤É¤Ś¤Ę¤ë¤Ç¤ˇ¤ç¤Ś¤ŤĄŁ(ʸťúÎó : (_,_,_) format) ¤ÇźÂšÔ¤ˇ¤Ć¤ß¤Ţ¤šĄŁ( _ ¤ĎˇżĘŃżô 'a ¤ČĆą¤¸¤Ç¤š¤ą¤ÉĄ˘¤ŢĄź¸ĺ¤ÇťČ¤ď¤Ę¤¤¤ˇĄ˘ĚžÁ°¤Ę¤ó¤Ť¤ÉĄź¤Ç¤â¤¤¤¤¤č¤Ă¤Ćťţ¤ËťČ¤¤¤Ţ¤š):
+>|ocaml|
+# ("hello world" : (_,_,_) format);;
+- : ('a, 'b, 'a) format = <abstr>
+# ("your name is %s\n" : (_,_,_) format);;
+- : (string -> 'a, 'b, 'a) format = <abstr>
+# ("%s : %d\n" : (_,_,_) format);;
+- : (string -> int -> 'a, 'b, 'a) format = <abstr>
+||<
+¤š¤ë¤ČʸťúÎó¤ÎĘĘ¤Ë format ˇż¤Č¤ˇ¤ĆˇżÉŐ¤ą¤ľ¤ě¤Ţ¤ˇ¤żĄŁ¤˝¤ÎžĺĄ˘ˇż¤ŹĄ˘format ¤ÎÂč°ě°úżôÉôĘŹ¤ŹĄ˘¤˝¤ě¤ž¤ě°ă¤¤¤Ţ¤šĄŁ'a, string -> 'a, string -> int -> 'aĄÄ şÇ¸ĺ¤ÎˇżĘŃżô 'a ¤ň˝ü¤ą¤ĐĽŐĽŠĄźĽŢĽĂĽČʸťúÎ󤏟ő¤ą¤Č¤ë¤Ů¤­ %s ¤ä %d ¤ËÂĐąţ¤ˇ¤ż°úżôˇż¤Ź˝Đ¤Ć¤­¤Ţ¤ˇ¤żĄŁ(ťĎ¤á¤Î "hello world" ¤Ď˛ż¤â°úżô¤ňÉŹÍפȤˇ¤Ę¤¤¤Î¤Ç¤ż¤ŔĂą¤Ë 'a) 
+
+ʸťúÎó¤Ę¤Î¤ËĄ˘string ¤Î¤Ď¤ş¤Ę¤Î¤ËĄ˘format ¤Č¤ˇ¤Ć˛ňźá¤ľ¤ť¤ë¤ČĄ˘¤˝¤ÎĆâÍƤˤč¤Ă¤Ćˇż¤ŹĘѤď¤ëĄ˘¤ł¤ě¤Ď¤Ę¤ó¤Ŕ¤ŤÁ´Áł¤ď¤Ť¤é¤Ę¤¤ĄŁ
+
+¤ď¤Ť¤é¤Ę¤¤¤Î¤âĹöÁł¤Ç¤šĄŁźÂ¤Ď printf ¤Î¤ż¤á¤ËĄ˘ocaml ¤Ç¤ĎʸťúÎóÄężô¤ÎˇżÉŐ¤ą¤ËĆĂźě¤ĘĽëĄźĽë¤Ź¤˘¤ë¤Î¤Ç¤š:
+
+- ÉáĂĘ¤Ď "ʸťúÎó" ¤Îˇż¤Ď string
+- ¤Ç¤âĄ˘¤â¤ˇ format ¤Č¤¤¤Śˇż¤ň´üÂÔ¤ľ¤ě¤ż¤é "ʸťúÎó" ¤ÎĆâÍƤň printf ¤ÎĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤Č¤ˇ¤Ć˛ňźá¤ˇ¤ĆĄ˘%d, %s Ĺů¤ËÂĐąţ¤š¤ë°úżô¤Îˇż¤ňťý¤Ä (°úżô -> ... -> °úżô -> 'a, 'b, 'a) format ¤Č¤¤¤Śˇż¤Ë¤Ę¤ë
+°ěĘýĄ˘printf ˇĎ´Řżô¤Źťý¤Ă¤Ć¤¤¤ëˇżźŤÂΤϤł¤Î format ¤Č¤¤¤ŚĽÇĄźĽżˇż¤ňťČ¤Ă¤Ć¤¤¤ë°Őł°Ą˘ĆĂźě¤Ę˝ę¤Ď¤˘¤ę¤Ţ¤ť¤óĄŁ(¤ż¤ŔĆâÉô¤Ç¤Ďż§Ąšąř¤¤¤ł¤Č¤ň¤ä¤Ă¤Ć¤¤¤Ţ¤š¤Ź(¸ĺ˝Ň))
+
+ĄÖformat ¤Č¤¤¤Śˇż¤ň´üÂÔ¤ľ¤ě¤ż¤éĄ×¤ČČůĚŻ¤ĘĆüËܸě¤Ç˝ń¤­¤Ţ¤ˇ¤żĄŁ¤ł¤ě¤ĎŔľ¤ËČůĚŻ¤Ę˝ę¤Ç¤ˇ¤ĆĄ˘ĽŤĽóĽżĽó¤Ë¤ĎĄ˘
+ĄÖprintf ˇĎ´Řżô¤ÎÍÍ¤Ë format ¤ň°úżô¤Č¤ˇ¤Ćťý¤Ä´Řżô¤ËÄžŔÜʸťúÎó¤ňÍż¤¨¤żžěšç+ŚÁĄ×
+¤ČÍý˛ň¤ˇ¤Ć¤¤¤ż¤Ŕ¤ą¤ě¤ĐÎɤ¤¤Čť×¤¤¤Ţ¤šĄŁ
+
+>>
+>>
+¤Ű¤ó¤Č¤ËĄ˘ĆüËܸě¤Ç KY ¤Ă¤Ć¸Ŕ¤Ă¤Ć¤â¤˝¤Îż´¤ĎČůĚŻ¤Ę¤Î¤ČĆą¤¸¤°¤é¤¤ČůĚŻ:
+>|ocaml|
+(* śőľ¤¤ňĆɤá¤ëÎă *)
+# (fun x -> (x : (_,_,_) format)) "hello world";;
+- : ('_a, '_b, '_a) format = <abstr>
+
+(* ¤â¤Ă¤ČĆɤá¤ë¤č!! *)
+# [ (fun x -> (x : (_,_,_) format)) "hello world"; "bye world" ];;
+- : ('_a, '_b, '_a) format list = [<abstr>; <abstr>]
+
+(* ¤Ç¤âľŐ¤Ë¤š¤ë¤ČĆɤá¤Ę¤¤¤ó¤Ŕ!! *)
+# [ "bye world"; (fun x -> (x : (_,_,_) format)) "hello world";  ];;
+Characters 15-60:
+  [ "bye world"; (fun x -> (x : (_,_,_) format)) "hello world";  ];;
+                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Error: This expression has type
+         ('a, 'b, 'a) format = ('a, 'b, 'a, 'a, 'a, 'a) format6
+       but an expression was expected of type string
+
+(* let ¤Ź¤˘¤ë¤Č¤â¤ŚĆɤá¤Ę¤¤ *)
+# let str = "hello world" in (str : (_,_,_) format);; 
+Characters 28-31:
+  let str = "hello world" in (str : (_,_,_) format);; 
+                              ^^^
+Error: This expression has type string but an expression was expected of type
+         ('a, 'b, 'c) format = ('a, 'b, 'c, 'c, 'c, 'c) format6
+||<
+¤ł¤ó¤Ę´ś¤¸¤Ç¤šĄŁML ¤ÎˇżżäĎŔ¤ň¤´Â¸ĂΤοͤʤ饢ˇżżäĎŔĽ˘ĽëĽ´ĽęĽşĽŕĆâ¤Ç¤ÎżäĎŔ˝çČ֤ˤš¤´¤Ż°Í¸¤ˇ¤Ć¤¤¤ë¤Î¤Ź¤ď¤Ť¤ë¤Ç¤ˇ¤ç¤ŚĄŁ
+
+printf ¤ÎĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤ň¤â¤Ă¤ČĺşÎď¤ĘˇżÂΡϤÎĂć¤Ç°ˇ¤Ă¤Ć¤ä¤í¤Ś¤Č¤š¤ë¤ČĄ˘Î㤨¤Đ°Í¸ˇż¤Č¤ŤťČ¤¨¤ĐÎɤ¤¤Čť×¤¤¤Ţ¤š¤ˇĄ˘¤˝¤Ś¤¤¤Ś¸Śľć¤â¤˘¤Ă¤ż¤Ť¤ČĄŁ
+ocaml ¤Ç¤ĎĄ˘ĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤Î¤ż¤á¤Ë°Í¸ˇż¤Ę¤ó¤ŤĆţ¤ě¤Ć¤â overkill ¤Ç¤ˇ¤Ť¤Ę¤¤¤Î¤ÇşÎÍѤˇ¤Ć¤¤¤Ţ¤ť¤óĄŁ¤ż¤Ŕ¤˝¤ÎÂĺ¤ď¤ę¸Ť¤Ć¤­¤ż¤č¤Ś¤Ë°ěÉôÉÔĘؤǤϤ˘¤ę¤Ţ¤šĄŁ
+<<
+<<
+
+printf "%s : %d" ¤ÎÎă¤ň¤â¤Ś°ěĹٸŤ¤Ć¤ß¤Ţ¤ˇ¤ç¤ŚĄŁ
+- printf ¤Îˇż¤Ď ('a, out_channel, unit) format -> 'a ¤Ę¤Î¤ÇĄ˘Âč°ě°úżô¤ËÍż¤¨¤é¤ě¤żĘ¸ťúÎó "%s : %d" ¤Ď string ¤Č¤¤¤Śˇż¤Ç¤Ď¤Ę¤ŻĄ˘format ¤Č¤ˇ¤Ć˛ňźá¤ľ¤ě¤Ţ¤šĄŁ 
+- %s ¤Č %d ¤Ź¤˘¤ë¤Î¤ÇĄ˘žĺ¤Ç¤â¸Ť¤ż¤č¤Ś¤Ë (string -> int -> 'b, 'c, 'b) format (printf ¤Îˇż¤ÎˇżĘŃżô¤ČşŽÍ𤡤ʤ¤¤č¤Ś¤ËˇżĘŃżôĚž¤ňĘѤ¨¤Ć¤¤¤Ţ¤š)
+- ¤ł¤ě¤Ź printf ¤Îˇż¤ÎÂč°ě°úżôÉôĘŹ¤Č unify (Ăą°ě˛˝) ¤ľ¤ě¤ë¤ČĄ˘(string -> int -> unit, out_channel, unit) format ¤Č¤¤¤Śˇż¤ËĄŁ
+- ¤ł¤Î unification ¤ÇˇżĘŃżô 'a ¤Ź string -> int -> unit ¤Ë¤Ę¤ę¤Ţ¤š¤Ť¤éĄ˘ printf ¤Îˇż¤Ď (string -> int -> unit, out_channel, unit) format -> string -> int -> unit
+- printf "%s : %d" ¤Č¤¤¤Śź°Á´ÂΤΡż¤Ď string -> int -> unit ¤Ë¤Ę¤ę¤Ţ¤šĄŁ¤Ŕ¤Ť¤é printf "%s : %d" "hoge" 42 ¤ĎŔľ¤ˇ¤ŻˇżÉŐ¤ą¤ľ¤ě¤żź°ĄŁ
+¤É¤Ś¤Ç¤ˇ¤ç¤ŚĄ˘¤Ä¤¤¤Ć¤ł¤ě¤Ţ¤ˇ¤ż¤ŤĄŠ
+
+format ˇż¤Ďť°¤Ä°úżô¤Ź¤˘¤ę¤Ţ¤šĄŁ('a, 'b, 'c) formatĄŁÂč°ě°úżô 'a ¤ĎşŁ¤Ţ¤Ç¤â¸Ť¤Ć¤­¤ż¤Č¤Ş¤ęĄ˘ĽŐĽŠĄźĽŢĽĂĽČʸťúÎ󤏤ɤó¤Ę°úżô¤ň´üÂÔ¤š¤ë¤Ť¤ÎžđĘó¤ňťý¤Á¤Ţ¤šĄŁ"%s : %d" ¤Ę¤é (string -> int -> 'c, 'b, 'c) formatĄŁ
+ĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤ňˇżÉŐ¤ą¤š¤ë¤Č format Âč°ě°úżô¤ÎĚá¤ęˇż¤ŹÂčť°°úżô¤ČĆą¤¸ˇżĘŃżô(žĺ¤Ç¤Ď 'c)¤Ë¤Ę¤Ă¤Ć¤¤¤ë¤ł¤Č¤ËĂí°Ő¤ˇ¤Ć¤Ż¤Ŕ¤ľ¤¤ĄŁ¤ł¤ÎÂčť°°úżô¤ĎĄ˘¤ł¤ÎĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤ňĹŹÍѤˇ¤ż¤éşÇ˝ŞĹޤˤɤó¤Ęˇë˛Ě¤Ë¤Ę¤ë¤Î¤Ť¤ÎžđĘó¤ÇĄ˘printf ¤Ę¤é¤ĐĄ˘(_, _, unit) format -> _ ¤Č¤¤¤ŚˇÁ¤ň¤ˇ¤Ć¤¤¤ĆĄ˘ÉŹÍפʤŔ¤ą°úżô¤ňźő¤ąźč¤Ă¤ż¤éşÇ˝ŞĹŞ¤Ë¤Ď unit ¤ňĘÖ¤ˇ¤Ţ¤š¤čĄ˘¤Č¤¤¤Ś°ŐĚŁ¤ňÉ˝¤ˇ¤Ţ¤šĄŁsprintf ¤Ę¤é¤ĐşÇ˝Şˇë˛Ě¤Ď string ¤Ę¤Î¤Ç:
+>|ocaml|
+val sprintf : ('a, unit, string) format -> 'a   (* format ¤ÎÂčť°°úżô¤ËĂíĚܤˇ¤Ć¤Ż¤Ŕ¤ľ¤¤ *) 
+||<
+¤Ë¤Ę¤ę¤Ţ¤šĄŁ¤ł¤ě¤ĎĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤ňťČ¤Ś printf ˇĎ´ŘżôÂŚ¤Îˇżžĺ¤ÎÍ×ŔÁ¤ňÉ˝¤š¤ż¤á¤ÎĘŞ¤Ç¤šĄŁ
+format ¤ÎÂčĆó°úżô¤ĎĄ˘%t ¤ä %a ¤Ę¤ÉĄ˘ocaml ĆČźŤ¤ÎšâłŹĽ×ĽęĽóĽż¤ÎˇżÉŐ¤ą¤ËÉŹÍפǥ˘ĽŐĽŠĄźĽŢĽĂĽČ¤ˇ¤żˇë˛Ě¤ÎʸťúÎó¤Ď¤É¤ł¤ËÁ÷¤é¤ě¤ë¤Ů¤­¤ŤĄ˘˝ĐÎĎĽÁĽăĽóĽÍĽë¤Ę¤Î¤ŤĄ˘ĽĐĽĂĽŐĽĄ¤Ę¤Î¤ŤĄ˘¤˝¤ě¤Č¤â sprintf ¤ÎÍͤËĘÖ¤ęĂͤËĚᤝ¤Đ¤¤¤¤¤Î¤ÇĆäˤ¤¤é¤Ę¤¤¤Î¤Ť¤ÎžđĘó¤Ç¤šĄŁ(¤ł¤ě¤Ë¤č¤Ă¤Ć %t ¤ä %a ¤Ç´üÂÔ¤ľ¤ě¤ë´Řżô¤Îˇż¤ŹĘѤď¤Ă¤Ć¤Ż¤ë):
+>|ocaml|
+val fprintf : out_channel -> ('a, out_channel, unit) format -> 'a  (* out_channel ¤Ë˝ń¤­˝Đ¤ˇ¤Ţ¤šĄŁfprintf ¤ÎÂč°ě°úżô¤Îˇż¤ČĆą¤¸ĄŁ *)
+val printf : ('a, out_channel, unit) format -> 'a                  (* fprintf ¤ÎĆĂźěÎă *)
+val sprintf : ('a, unit, string) format -> 'a                      (* Ćäˤ¤¤é¤Ę¤¤¤Î¤Ç unit *)
+val bprintf : Buffer.t -> ('a, Buffer.t, unit) format -> 'a        (* ĽĐĽĂĽŐĽĄ¤Ë˝ń¤­˝Đ¤ˇ¤Ţ¤šĄŁbprintf ¤ÎÂč°ě°úżô¤Îˇż¤ČĆą¤¸ĄŁ *)
+
+# ("%t %a" : (_,_,_) format);;
+- : (('a -> 'b) -> ('a -> 'c -> 'b) -> 'c -> 'b, 'a, 'b) format = <abstr>
+(* format ÂčĆó°úżô¤ÎˇżĘŃżô 'a ¤Ź %t ¤Č %a ¤ËÂĐąţ¤š¤ë´Řżô°úżô¤ÎÂč°ě°úżôˇż¤ËÂĐąţ¤ˇ¤Ć¤¤¤ë¤ł¤Č¤ËĂí°Ő *)
+||<
+
+** Ć°ĹŞ¤ĘĽŐĽŠĄźĽŢĽĂĽČʸťúÎ󥢤ϥ˘˝ĐÍč¤Ę¤¤ĄŠ
+
+ĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤ÎĆĂźě¤ĘˇżÉŐ¤ą¤ň¸Ť¤Ć¤­¤Ţ¤ˇ¤żĄŁĘ¸ťúÎóÄężô¤Ź format ¤Ë˛˝¤ą¤ëĄ˘¤ł¤ÎĆĂźě¤ĘˇżÉŐ¤ą¤Ď*Äężô*¤Ë¤Î¤ßČŻŔ¸¤ˇ¤Ţ¤šĄŁ¤Ç¤ĎĄ˘Ć°ĹŞ¤ËĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤ňŔ¸ŔŽ¤š¤ëĄ˘Î㤨¤ĐĄ˘ĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤ňĽŐĽĄĽ¤Ľë¤Ť¤éĆɤߚţ¤ó¤Ç¤Ż¤ëĄ˘¤č¤Ś¤Ę¤ł¤Č¤Ď˝ĐÍč¤Ę¤¤¤Î¤Ç¤ˇ¤ç¤Ś¤ŤĄŁšńşÝ˛˝¤ľ¤ě¤żĽáĽĂĽťĄźĽ¸ĽŐĽĄĽ¤Ľë¤ňşî¤Ă¤Ć¤˝¤ł¤Ť¤éłĆšń¸ě¤ÎĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤ňźč¤Ă¤Ć¤Ż¤ëĄ˘¤Ę¤ÉĄ˘¤˘¤ę¤˝¤Ś¤Ç¤šĄŁ
+
+¤Á¤ç¤Ă¤ČźŤżŽ¤Ź¤˘¤ę¤Ţ¤ť¤ó¤ŹĄ˘¤ł¤ě¤ĎÉáÄ̤ˤϽĐÍ褽¤Ś¤Ë¤˘¤ę¤Ţ¤ť¤óĄŁÉ¸˝ŕĽéĽ¤ĽÖĽéĽę¤Ë¤Ď string ¤ň format ¤ËĘѤ¨¤Ć¤Ż¤ě¤ë´Řżô¤Ź¤Ę¤¤¤Î¤Ç¤šĄŁ
+
+*** ¤¤¤äĄ˘˝ĐÍ褽¤Ś¤ŔĄ˘ %{ %} ¤ňťČ¤¨¤ĐĄ˘Ą˘Ą˘
+
+Ć°ĹŞˇżÉŐ¤ą¤Ď˝ĐÍ褽¤Ś¤Ę¤â¤Î¤Ç¤š¤ŹĄ˘Ą˘Ą˘¤ČšÍ¤¨¤Ć¤¤¤ż¤éĄ˘˝ĐÍč¤Ţ¤ˇ¤żĄŁ¤ł¤ł¤ÇĄ˘Ć°ĹŞˇżÉŐ¤ąĄ˘¤Č¤¤¤Ś¤Î¤ĎĄ˘
+>|ocaml|
+let print_hoge_42 fmt = 
+  Printf.printf <fmt_check fmt> "hoge" 42
+;;
+||<
+¤Č˝ń¤¤¤ż¤Č¤­¤Ë fmt ¤Ź "%s : %d" ¤ä "name %s, age %d" ¤Î¤č¤Ś¤ĘÂč°ě°úżô¤Č¤ˇ¤Ć string ¤ňĄ˘ÂčĆó°úżô¤Č¤ˇ¤Ć int ¤ňźč¤ë¤č¤Ś¤ĘĽŐĽŠĄźĽŢĽĂĽČʸťúÎ󤍤ɤŚ¤Ť¤ňźÂšÔťţ¸Ąşş¤ˇ¤ĆĄ˘¤â¤ˇ¤˝¤Ś¤Ç¤Ę¤Ť¤Ă¤żžěšç¤ĎÎăł°¤ňČŻŔ¸¤ľ¤ť¤ëľĄš˝¤Ç¤šĄŁ( <fmt_check fmt> ¤ĎŔľ¤ˇ¤¤Ę¸ËĄ¤Ç¤Ď¤˘¤ę¤Ţ¤ť¤óĄŁÂżĘŹ¤ł¤ó¤Ęź°¤Ź¤˘¤Ă¤Ć¤ł¤ó¤Ę´ś¤¸¤Ę¤é¤¤¤¤¤Ę¤Ą¤Č¤¤¤Ś pseudo syntax ¤Ŕ¤Čť×¤Ă¤Ć¤Ż¤Ŕ¤ľ¤¤ĄŁ) Î㤨¤ĐĄ˘
+>|ocaml|
+print_hoge_42 "%d : %d" 
+||<
+¤ĎźÂšÔťţĽ¨ĽéĄź(Îăł°)¤Ë¤Ę¤ë¤ď¤ą¤Ç¤šĄŁźÂšÔťţ¤Ëźő¤ąźč¤Ă¤żĘ¸ťúÎóĂć¤Î %¤Ę¤ó¤ż¤é ¤ňĽšĽ­ĽăĽó¤ˇ¤ĆÄ´¤ŮĄ˘ŔĹĹŞˇżÉŐ¤ąĂĘłŹ¤Ç¤ď¤Ť¤Ă¤Ć¤¤¤ë printf ¤ËÍż¤¨¤é¤ě¤ë°úżô¤ÎˇżžđĘó¤ČČć¤Ů¤ě¤Đ¤Ę¤ó¤Č¤Ť¤Ę¤ę¤˝¤Ś¤Ç¤šĄŁ°úżô¤Îżô¤äˇż¤Ďʸ̎¤Ť¤éŔĹĹŞ¤Ë¤Ď¤Ă¤­¤ęˇč¤Ţ¤Ă¤Ć¤¤¤Ć¤â¤é¤ď¤Ę¤ą¤ě¤Đ¤¤¤ą¤Ţ¤ť¤óĄŁÂżÁę´Řżô¤Ë¤Ę¤Ă¤Ć¤¤¤Ćˇč¤Ţ¤Ă¤Ć¤¤¤Ę¤¤žěšç°ěČ̤ň°ˇ¤Ś¤Î¤Ď¤š¤´¤ŻĆń¤ˇ¤¤¤Ç¤ˇ¤ç¤ŚĄŁÎ㤨¤ĐĄ˘
+>|ocaml|
+let print_something_and_42 fmt something =
+  Printf.printf <fmt_check fmt> something 42
+in
+print_something_and_42 "%s : %d" "hoge";
+print_something_and_42 "%d : %d" 42
+;;
+||<
+¤ł¤ó¤Ęžěšç¤Ď fmt ¤Ë´üÂÔ¤ľ¤ě¤ëˇż¤Ź print_something_and_42 ¤Î¸Ć¤Ó˝Đ¤ˇĘý¤Ë¤č¤Ă¤ĆĘѤď¤Ă¤Ć¤­¤Ţ¤šĄŁĽÁĽ§ĽĂĽŻ¤š¤ë¤Ů¤­ˇż¤ŹŔĹĹŞ¤ËˇčÄę¤Ç¤­¤Ţ¤ť¤óĄŁ
+
+ĆťśńΊ¤Ć¤Ď°ěąţ¡¤Ă¤Ć¤¤¤ż¤Î¤Çşî¤Ă¤Ć¤ß¤Ţ¤ˇ¤żĄŁ
+>|ocaml|
+val check : ('a, 'b, 'c, 'd, 'e, 'f) format6 -> string -> ('a, 'b, 'c, 'd, 'e, 'f) format6
+
+let fmt_check (fmt_type : ('a,'b,'c,'d,'e,'f) format6) (fmt_string : string) : ('a,'b,'c,'d,'e,'f) format6 =
+  let string_fmt_type = Printf.sprintf "%{%}" (Obj.magic fmt_type) in
+  let fmt_string = Obj.magic fmt_string in
+  if string_fmt_type = Printf.sprintf "%{%}" fmt_string then fmt_string
+  else invalid_arg ("check " ^ string_fmt_type)
+;;
+||<
+format6 ¤Îťö¤Ď¸ĺ˝Ň¤ˇ¤Ţ¤š¤ŹĄ˘¤ł¤ł¤Ç¤Ď format ¤Î¤č¤ęĘŁť¨¤ĘĽĐĄźĽ¸ĽçĽó¤Čť×¤Ă¤Ć¤Ż¤Ŕ¤ľ¤¤ĄŁ
+Obj.magic ¤ňťČ¤ś¤ë¤ňĆŔ¤Ţ¤ť¤ó¤Ç¤ˇ¤ż¤Î¤ÇĄ˘ŔľÄž´í¸ą¤ĘĽłĄźĽÉ¤Ç¤šĄŁ¤â¤ˇ¤Ť¤š¤ë¤ČźÂ¤Ď´Ö°ă¤Ă¤Ć¤¤¤ĆĄ˘¤ł¤Î´Řżô¤ňťČ¤Ă¤Ć¤¤¤ë¤ČĽŻĽéĽĂĽˇĽĺ¤š¤ë¤Ť¤â¤ˇ¤ě¤Ţ¤ť¤ó¤Î¤ÇĂí°Ő¤ň¤Ş´ę¤¤¤ˇ¤Ţ¤šĄŁ
+fmt_check fmt str ¤Ď fmt ¤Ë str ¤Ź¤˘¤ë¤Ů¤­ˇż¤ËÁęĹö¤š¤ëĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤ňťŘÄꤡĄ˘źÂšÔťţ¤Ë str ¤ŹĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤Č¤ˇ¤ĆĆą¤¸ˇż¤ňťý¤Ă¤Ć¤¤¤ë¤Ť¤É¤Ś¤Ť¤ňĽÁĽ§ĽĂĽŻĄ˘Ćą¤¸ˇż¤Ę¤éĚľÍý¤ä¤ęĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤Ëˇż¤ňĘŃ´š¤ˇ¤Ţ¤šĄŁĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤ň normalize ¤š¤ë¤ż¤á¤ËĄ˘ %{%} ¤ňťČ¤Ă¤Ć¤¤¤Ţ¤šĄ˘¤ŹĄ˘¤ł¤ě¤âĄ˘ťĹÍÍÄ̤ę¤ÎťČ¤¤Ęý¤¸¤ă¤Ę¤¤¤Î¤ÇĄ˘śöÁłžĺźę¤ŻšÔ¤Ă¤Ć¤¤¤ë¤Ŕ¤ą¤Ť¤â¤ˇ¤ě¤Ţ¤ť¤óĄŁ¤ż¤ŔĄ˘ %{fmt%} ¤Î°ŐĚŁ¤Î¤˘¤ëťČ¤¤Ęý¤Ă¤ĆŔľÄžť×¤¤¤Ä¤Ť¤Ę¤¤¤Î¤ÇĄ˘ fmt_check ¤ß¤ż¤¤¤Ęťö¤ň¤ä¤ę¤ż¤¤¤Čť×¤Ă¤ĆĂćĹÓȞߤˤʤäƤ¤¤ë˛ÄÇ˝Ŕ­¤â¤˘¤ę¤Ţ¤šĄŁ(printf.ml ¤ň¤č¤ŻĆɤá¤ĐĄ˘°ěÉôźÂÁőĂć¤Ç¤Ű¤Ă¤ż¤é¤Ť¤ˇ¤Ë¤Ę¤Ă¤Ć¤¤¤ëľĄÇ˝¤Ź¸Ť¤Ä¤Ť¤ę¤Ţ¤šĄŁÍę¤ŕ¤č Pierre((˛ś¤ÎťŐž˘))ĄŁ)
+¤Ţ¤˘Ą˘¤ľ¤ĆĄ˘¤ł¤Î fmt_check ¤ňťČ¤Ś¤ČĄ˘źĄ¤Î¤č¤Ś¤Ęź°¤Ź˝ń¤ą¤Ţ¤š:
+>|ocaml|
+let f () = 
+  Printf.printf (fmt_check "%s%d" (read_line ())) "hello" 42
+||<
+ɸ˝ŕĆţÎϤŤ¤éʸťúÎó¤ňĆɤߚţ¤ßĄ˘¤˝¤ě¤Ź "%s%d" ¤ČĆą¤¸ format ˇż¤ňťý¤ÄĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤Č¤ˇ¤Ć˛ňźá¤Ç¤­¤ë¤Ę¤é¤Đ Printf.printf ¤ňźÂšÔ¤ˇ¤Ţ¤š:
+>|ocaml|
+# f ();;
+hello world    (* "hello world" ¤Ď "%s%d" ¤Č¤ĎČó¸ß´š *)
+Exception: Invalid_argument "check %s%i".
+# f ();;
+name=%s; value=%d;   (* ¤ł¤ě¤Ę¤é¸ß´š! *)
+name=hoge; value=42;- : unit = ()
+||<
+
+** Printf ¤Ď¤Ţ¤ŔÂł¤­¤Ţ¤šĄ˘Ą˘Ą˘
+
+Printf ¤Ë¤ĎĄ˘¤˘¤ČĆó¤Ä¤Ű¤Éż¨¤ě¤Ć¤Ş¤Ż¤Ů¤­ĆâÍƤŹ¤˘¤ë¤Î¤Ç¤š¤ŹĄ˘¤ł¤ě¤Ţ¤żÄš¤Ż¤Ę¤ę¤˝¤Ś¤Ç¤šĄŁźč¤ęšç¤¨¤ş¤ł¤ł¤ÇĂŚšĆ¤ˇ¤ĆÁ°ČžÉô¤Č¤ľ¤ť¤Ć¤Ż¤Ŕ¤ľ¤¤ĄŁ
+
+

log/printf-euc11.txt

+*1257099984*[OCaml][Tutorial] OCaml ɸ˝ŕĽéĽ¤ĽÖĽéĽęþˏ #3.0: Printf: ĘŘÍř¤Ŕ¤ą¤É¤¤¤í¤¤¤íĆ椏
+
+printf ¤Ă¤Ć OCaml ¤Ç¤âĘŘÍř¤Ç¤š¤č¤ÍĄŁC ¤Ť¤éϢĚʤČÂł¤¤¤Ć¤¤¤ëČž¤Đžďźą¤Î % Ľ¤ĽóĽżĄźĽŐĽ§ĄźĽš¤Ë˛Ă¤¨Ą˘¤Á¤ç¤Ă¤ČÉԝ׾ĤʡżżäĎŔ¤Î¤Ş¤Ť¤˛¤Çˇż°ÂÁ´Ŕ­¤âĘÝžÚ¤ľ¤ě¤Ć¤Ţ¤šĄŁprintf ťČ¤Ă¤Ć¤ĆˇżĽ¨ĽéĄź¤Ź¸Ť¤Ä¤Ť¤ë¤ż¤Ó¤ËĄ˘¤˘Ąź C ¤Ŕ¤Ă¤ż¤é seg fault ¤ˇ¤Ć¤ż¤Ť¤â¤ˇ¤ě¤ó¤ĘĄ˘Ą˘Ą˘ÎɤŤ¤Ă¤żÎɤŤ¤Ă¤żĄ˘¤Čť×¤¤¤Ţ¤šĄŁşŁĆü¤Ď¤˝¤ó¤Ę printf ˇĎ¤Î´Řżô¤ňÄ󜥤š¤ë Printf ĽâĽ¸ĽĺĄźĽë¤Î¤ŞĎĂĄŁ
+
+** ¤ŞÉĘ˝ń¤­
+
+- Printf ¤ÎĆĂźě¤ĘˇżÉŐ¤ą¤Ë¤Ä¤¤¤Ć / format ˇż¤Îťö
+- źÂšÔťţ¤Ë format ¤ňˇżÉŐ¤ą¤š¤ëĽĆĽŻĽËĽĂĽŻ 
+- źŤşî printf ˇĎ´Řżô¤Îşî¤ęĘý / kprintf ¤Îťö
+- Printf Ă٤¤¤č / printf ¤ÎźÂÁő
+
+** Printf ¤ÎĆĂźě¤ĘˇżÉŐ¤ą
+
+printf ˇĎ¤Î´Řżô¤Ďžőśˇ¤Ë¤č¤Ă¤Ć°Ű¤Ę¤ëżô¤Î°úżô (variable length arguments (˛ÄĘŃÄš°úżô)) ¤ňźč¤ë¤ł¤Č¤Ź˝ĐÍč¤Ţ¤šĄŁÎ㤨¤ĐĄ˘
+>|ocaml|
+open Printf
+let name = "hogera";;
+let value = 42;;
+let () = printf "hello world\n";;
+let () = printf "your name is %s\n" name;;
+let () = printf "%s : %d\n" name value;;
+||<
+name ¤ä value ¤ÎĹŹÍѤň¤ť¤şĄ˘toplevel ¤ÇźÂšÔ¤š¤ë¤Čˇż¤Ź˝Đ¤Ć¤­¤Ć˛ň¤ę°×¤¤:
+>|ocaml|
+open Printf;;
+# printf "hello world\n";;
+hello world
+- : unit = ()
+# printf "your name is %s\n";;
+- : string -> unit = <fun>
+# printf "%s : %d\n";;
+- : string -> int -> unit = <fun>
+||<
+Ćą¤¸´Řżô¤ËʸťúÎó¤ňÍż¤¨¤ĆĘ֤äƤ­¤żĂͤΡż¤Źžěšç¤Ë¤č¤Ă¤Ć°Ű¤Ę¤Ă¤Ć¤¤¤ëĄŁML ¤ÎÉáÄ̤ΡżÉŐ¤ą¤Ç¤Ď¤ł¤ó¤Ęťö¤Ď˝ĐÍč¤Ţ¤ť¤óĄŁĚŔ¤é¤Ť¤ËÉÔźŤÁł¤Ç¤šĄŁ˛ż¤ŤĆĂźě¤Ęťö¤ň¤ä¤Ă¤Ć¤¤¤ë¤Ë°ă¤¤¤˘¤ę¤Ţ¤ť¤óĄŁ¤ł¤Ś¤¤¤Śťţ¤Ď ${libdir}/printf.mli ¤Î¤Ç printf ¤Îˇż¤ň¸Ť¤Ć¤ß¤Ţ¤ˇ¤ç¤Ś:
+>|ocaml|
+val printf : ('a, out_channel, unit) format -> 'a
+||<
+Âč°ě°úżô¤Ď string ¤¸¤ă¤Ę¤Ż¤Ă¤Ć ('a, out_channel, unit) format ¤Č¤Ť¤¤¤ŚĽÇĄźĽżˇż¤Ç¤šĄŁ¤˝¤ˇ¤ĆĘÖ¤ęĂͤΡż¤ŹˇżĘŃżô 'aĄŁ¤ł¤ě¤Ď°ěÂΤɤŚ¤Ę¤Ă¤Ć¤¤¤ë¤Î¤Ç¤ˇ¤ç¤Ś¤ŤĄŁ
+
+*** źÂ¤ĎĆĂźě¤Ę¤Î¤ĎʸťúÎóÄężô¤ÎˇżÉŐ¤ą
+
+¤ł¤ł¤ŹşŁ˛ó°ěČÖĆń¤ˇ¤¤˝ęĄŁˇż¤Č¤Ťś˝ĚŁ¤Ę¤¤¤ČŔľÄžżÉ¤¤¤Ť¤éĄ˘śěźę¤ĘżÍ¤ĎźĄŔá¤ÎĄÖĆ°ĹŞ¤ĘĽŐĽŠĄźĽŢĽĂĽČʸťúÎóĄ×¤Ţ¤ÇČô¤Đ¤ˇ¤Ć¤Ż¤Ŕ¤ľ¤¤ĄŁ¤Ň¤Č¤Ţ¤şĄ˘¤ľ¤č¤Ś¤Ę¤éĄŁ
+
+printf ¤ÎÂč°ě°úżô¤Ď ('a, out_channel, unit) format ¤Č¤¤¤Śˇż¤Ç¤š¤ŹĄ˘¤˝¤ł¤ËʸťúÎó¤ňĹŹÍѤˇ¤Ć¤â ocaml ¤Ď˛ż¤Ë¤âʸśç¤ň¸Ŕ¤ď¤Ę¤¤ĄŁ¤ł¤ě¤Ď°ěÂΤɤŚ¤¤¤Ś¤ł¤Č¤Ç¤ˇ¤ç¤Ś¤ŤĄŠ
+>|ocaml|
+# "hello world\n";;
+- : string = "hello world\n"
+# "your name is %s\n";;
+- : string = "your name is %s\n"
+# "%s : %d\n";;
+- : string = "%s : %d\n"
+||<
+¤Ş¤Ť¤ˇ¤¤Ą˘¤ł¤ě¤Ď¤ß¤ó¤ĘʸťúÎó¤Î¤Ď¤şĄ˘¤Ę¤Î¤ËĄ˘Ą˘Ą˘format ¤Ę¤ó¤Ćˇż¤¸¤ă¤Ę¤¤¤č!!
+
+¤Ç¤ĎĄ˘´ş¤¨¤ĆĄ˘format ˇż¤Č¤ˇ¤Ć ocaml ¤ËÍż¤¨¤ë¤Č¤É¤Ś¤Ę¤ë¤Ç¤ˇ¤ç¤Ś¤ŤĄŁ(ʸťúÎó : (_,_,_) format) ¤ČĽČĽĂĽ×ĽěĽŮĽë¤ÇźÂšÔ¤ˇ¤Ć¤ß¤Ţ¤šĄŁ( _ ¤ĎˇżĘŃżô 'a ¤ČĆą¤¸¤Ç¤š¤ą¤ÉĄ˘¤ŢĄź¸ĺ¤ÇťČ¤ď¤Ę¤¤¤ˇĄ˘ĚžÁ°¤Ę¤ó¤Ť¤ÉĄź¤Ç¤â¤¤¤¤¤č¤Ă¤Ćťţ¤ËťČ¤¤¤Ţ¤š):
+>|ocaml|
+# ("hello world" : (_,_,_) format);;
+- : ('a, 'b, 'a) format = <abstr>
+# ("your name is %s\n" : (_,_,_) format);;
+- : (string -> 'a, 'b, 'a) format = <abstr>
+# ("%s : %d\n" : (_,_,_) format);;
+- : (string -> int -> 'a, 'b, 'a) format = <abstr>
+||<
+¤š¤ë¤ČʸťúÎó¤ÎĘĘ¤Ë format ˇż¤Č¤ˇ¤ĆˇżÉŐ¤ą¤ľ¤ě¤Ţ¤ˇ¤żĄŁ¤˝¤ÎžĺĄ˘ˇż¤ŹĄ˘format ¤ÎÂč°ě°úżôÉôĘŹ¤ŹĄ˘¤˝¤ě¤ž¤ě°ă¤¤¤Ţ¤šĄŁ'a, string -> 'a, string -> int -> 'aĄÄ şÇ¸ĺ¤ÎˇżĘŃżô 'a ¤ň˝ü¤ą¤ĐĽŐĽŠĄźĽŢĽĂĽČʸťúÎ󤏟ő¤ą¤Č¤ë¤Ů¤­ %s ¤ä %d ¤ËÂĐąţ¤ˇ¤ż°úżôˇż¤Ź˝Đ¤Ć¤­¤Ţ¤ˇ¤żĄŁ(ťĎ¤á¤Î "hello world" ¤Ď˛ż¤â°úżô¤ňÉŹÍפȤˇ¤Ę¤¤¤Î¤Ç¤ż¤ŔĂą¤Ë 'a) 
+
+ʸťúÎó¤Ę¤Î¤ËĄ˘string ¤Î¤Ď¤ş¤Ę¤Î¤ËĄ˘format ¤Č¤ˇ¤Ć˛ňźá¤ľ¤ť¤ë¤ČĄ˘¤˝¤ÎĆâÍƤˤč¤Ă¤Ćˇż¤ŹĘѤď¤ëĄ˘¤ł¤ě¤Ď°ěÂβż¤ŔĄŠÁ´Áł¤ď¤Ť¤é¤Ę¤¤ĄŁ
+¤ď¤Ť¤é¤Ę¤¤¤Î¤âĹöÁłĄŁźÂ¤Ď printf ¤Î¤ż¤á¤ËĄ˘ocaml ¤Ç¤ĎʸťúÎóÄężô¤ÎˇżÉŐ¤ą¤ËĆĂźě¤ĘĽëĄźĽë¤Ź¤˘¤ë¤Î¤Ç¤š:
+
+- ÉáĂĘ¤Ď "ʸťúÎó" ¤Îˇż¤Ď string
+- ¤â¤ˇ format ¤Č¤¤¤Śˇż¤ň´üÂÔ¤ľ¤ě¤ż¤é "ʸťúÎó" ¤ÎĆâÍƤň printf ¤ÎĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤Č¤ˇ¤Ć˛ňźá¤ˇ¤ĆĄ˘%d, %s Ĺů¤ËÂĐąţ¤š¤ë°úżô¤Îˇż¤ňťý¤Ä (°úżô -> ... -> °úżô -> 'a, 'b, 'a) format ¤Č¤¤¤Śˇż¤Ë¤Ę¤ë
+°ěĘýĄ˘printf ˇĎ´Řżô¤ÎˇżÉŐ¤ąźŤÂÎ¤Ď format ¤Č¤¤¤ŚĽÇĄźĽżˇż¤ňťČ¤Ă¤Ć¤¤¤ë°Őł°Ą˘ĆĂźě¤Ę˝ę¤Ď¤˘¤ę¤Ţ¤ť¤óĄŁ(¤ż¤ŔĆâÉô¤Ç¤Ďż§Ąšąř¤¤¤ł¤Č¤ň¤ä¤Ă¤Ć¤¤¤Ţ¤š¤Ź(¸ĺ˝Ň))
+
+ĄÖformat ¤Č¤¤¤Śˇż¤ň´üÂÔ¤ľ¤ě¤ż¤éĄ×¤ČČůĚŻ¤ĘĆüËܸě¤Ç˝ń¤­¤Ţ¤ˇ¤żĄŁ¤ł¤ě¤ĎŔľ¤ËČůĚŻ¤Ę˝ę¤Ç¤ˇ¤ĆĄ˘ĽŤĽóĽżĽó¤Ë¤ĎĄ˘
+>>
+ĄÖprintf ˇĎ´Řżô¤ÎÍÍ¤Ë format ¤ň°úżô¤Č¤ˇ¤Ćťý¤Ä´Řżô¤ËÄžŔÜʸťúÎó¤ňÍż¤¨¤żžěšç+ŚÁĄ×
+<<
+¤ČÍý˛ň¤ˇ¤Ć¤Ş¤ą¤ĐĚäÂę¤Ę¤¤¤Ç¤ˇ¤ç¤ŚĄŁÍפϥ˘
+- printf ˇĎ´Řżô¤ĎĄ˘Ăą˝ă¤Ę´Řżôˇż¸Ŕ¸ě¤ÎˇżĽˇĽšĽĆĽŕ¤Ç¤Ď¤Ś¤Ţ¤Żˇż¤ňÍż¤¨¤ë¤ł¤Č¤Ź˝ĐÍč¤Ę¤¤
+- ¤Ç¤âĘŘÍř¤Ę¤Î¤Ç ocaml ¤Ç¤âťČ¤¤¤ż¤¤
+- ¤Ę¤Î¤ÇĄ˘ printf ˇĎ´Řżô¤Ź˛ż¤Č¤Ę¤Żžĺźę¤ŻˇżÉŐ¤ą¤Ç¤­¤ë¤č¤Ś¤ËˇżÉŐ¤ąĽëĄźĽë¤ň¤Á¤ç¤Ă¤Č hack ¤ˇ¤żĄŁ¤Ę¤Î¤ÇĄ˘¤č¤ŻťČ¤ď¤ě¤ëĽˇĽÁĽĺĽ¨ĄźĽˇĽçĽó°Ęł°¤Ç¤Ďžĺźę¤Ż¤¤¤Ť¤Ę¤¤
+¤Č¤¤¤Ś¤ł¤Č¤Ç¤šĄŁ
+
+>>
+>>
+ĄÖformat ¤Č¤¤¤Śˇż¤ň´üÂÔ¤ľ¤ě¤ż¤éĄŁĄ×¤ł¤ě¤ĎĄ˘ĆüËܸě¤Ç KY ¤Ă¤Ć¸Ŕ¤Ă¤Ć¤â¤˝¤Îż´¤ĎČůĚŻ¤Ę¤Î¤ČĆą¤¸¤°¤é¤¤ČůĚŻ:
+>|ocaml|
+(* śőľ¤¤ňĆɤá¤ëÎă *)
+# (fun x -> (x : (_,_,_) format)) "hello world";;
+- : ('_a, '_b, '_a) format = <abstr>
+
+(* ¤â¤Ă¤ČĆɤá¤ë¤č!! *)
+# [ (fun x -> (x : (_,_,_) format)) "hello world"; "bye world" ];;
+- : ('_a, '_b, '_a) format list = [<abstr>; <abstr>]
+
+(* ¤Ç¤âľŐ¤Ë¤š¤ë¤ČĆɤá¤Ę¤¤¤ó¤Ŕ!! *)
+# [ "bye world"; (fun x -> (x : (_,_,_) format)) "hello world";  ];;
+Characters 15-60:
+  [ "bye world"; (fun x -> (x : (_,_,_) format)) "hello world";  ];;
+                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Error: This expression has type
+         ('a, 'b, 'a) format = ('a, 'b, 'a, 'a, 'a, 'a) format6
+       but an expression was expected of type string
+
+(* let ¤Ź¤˘¤ë¤Č¤â¤ŚĆɤá¤Ę¤¤ *)
+# let str = "hello world" in (str : (_,_,_) format);; 
+Characters 28-31:
+  let str = "hello world" in (str : (_,_,_) format);; 
+                              ^^^
+Error: This expression has type string but an expression was expected of type
+         ('a, 'b, 'c) format = ('a, 'b, 'c, 'c, 'c, 'c) format6
+||<
+¤ł¤ó¤Ę´ś¤¸¤Ç¤šĄŁML ¤ÎˇżżäĎŔ¤ň¤´Â¸ĂΤοͤʤ饢ˇżżäĎŔĽ˘ĽëĽ´ĽęĽşĽŕĆâ¤Ç¤ÎżäĎŔ˝çČ֤ˤš¤´¤Ż°Í¸¤ˇ¤Ć¤¤¤ë¤Î¤Ź¤ď¤Ť¤ë¤Ç¤ˇ¤ç¤ŚĄŁ
+
+printf ¤ÎĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤ň¤â¤Ă¤ČĺşÎď¤ĘˇżÂΡϤÎĂć¤Ç°ˇ¤Ă¤Ć¤ä¤í¤Ś¤Č¤š¤ë¤ČĄ˘Î㤨¤Đ°Í¸ˇż¤Č¤ŤťČ¤¨¤˝¤Ś¤Ęľ¤¤Ź¤ˇ¤Ţ¤š¤ˇĄ˘¤˝¤Ś¤¤¤Ś¸Śľć¤â¤˘¤Ă¤ż¤Ť¤ČĄŁ
+ocaml ¤Ç¤ĎĄ˘ĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤Î¤ż¤á¤Ë°Í¸ˇż¤Ę¤ó¤ŤĆţ¤ě¤Ć¤â overkill ¤Ç¤ˇ¤Ť¤Ę¤¤¤Î¤ÇşÎÍѤˇ¤Ć¤¤¤Ţ¤ť¤óĄŁ¤ż¤Ŕ¤˝¤ÎÂĺ¤ď¤ę¸Ť¤Ć¤­¤ż¤č¤Ś¤Ë°ěÉôÉÔĘؤǤϤ˘¤ę¤Ţ¤šĄŁ
+<<
+<<
+
+*** ('a,'b,'c) format ¤Î 'a, 'b, 'c ¤Î¤Ď¤ż¤é¤­
+
+¤Ç¤ĎźÂşÝ¤Ë printf "ĽŐĽŠĄźĽŢĽĂĽČʸťúÎó" ¤ŹˇżÉŐ¤ą¤ľ¤ě¤ë¤Č¤ł¤í¤ň printf "%s : %d" ¤ÎÎă¤Ç¸Ť¤Ć¤ß¤Ţ¤ˇ¤ç¤Ś:
+- printf ¤Îˇż¤Ď ('a, out_channel, unit) format -> 'a ¤Ę¤Î¤ÇĄ˘Âč°ě°úżô¤ËÍż¤¨¤é¤ě¤żĘ¸ťúÎó "%s : %d" ¤Ď string ¤Č¤¤¤Śˇż¤Ç¤Ď¤Ę¤ŻĄ˘format ¤Č¤ˇ¤Ć˛ňźá¤ľ¤ě¤Ţ¤šĄŁ 
+- %s ¤Č %d ¤Ź¤˘¤ë¤Î¤ÇĄ˘žĺ¤Ç¤â¸Ť¤ż¤č¤Ś¤Ë (string -> int -> 'b, 'c, 'b) format ¤Č¤¤¤Śˇż¤Ë¤Ę¤ę¤Ţ¤šĄŁ(printf ¤Îˇż¤ÎˇżĘŃżô¤ČşŽÍ𤡤ʤ¤¤č¤Ś¤ËˇżĘŃżôĚž¤ňĘѤ¨¤Ć¤¤¤Ţ¤š)
+- ¤ł¤ě¤Ź printf ¤Îˇż¤ÎÂč°ě°úżôÉôĘŹ¤Č unify (Ăą°ě˛˝) ¤ľ¤ě¤ë¤ČĄ˘(string -> int -> unit, out_channel, unit) format ¤Č¤¤¤Śˇż¤ËĄŁ
+- ¤ł¤Î unification ¤ÇˇżĘŃżô 'a ¤Ź string -> int -> unit ¤Ë¤Ę¤ę¤Ţ¤š¤Ť¤éĄ˘ printf ¤Îˇż¤Ď (string -> int -> unit, out_channel, unit) format -> string -> int -> unit
+- printf "%s : %d" ¤Č¤¤¤Śź°Á´ÂΤΡż¤Ď string -> int -> unit ¤Ë¤Ę¤ę¤Ţ¤šĄŁ¤Ŕ¤Ť¤é printf "%s : %d" "hoge" 42 ¤ĎŔľ¤ˇ¤ŻˇżÉŐ¤ą¤ľ¤ě¤żź°ĄŁ
+¤É¤Ś¤Ç¤ˇ¤ç¤ŚĄ˘¤Ä¤¤¤Ć¤ł¤ě¤Ţ¤ˇ¤ż¤ŤĄŠ
+
+format ˇż¤Ďť°¤Ä°úżô¤Ź¤˘¤ę¤Ţ¤šĄŁ('a, 'b, 'c) formatĄŁÂč°ě°úżô 'a ¤ĎşŁ¤Ţ¤Ç¤â¸Ť¤Ć¤­¤ż¤Č¤Ş¤ęĄ˘ĽŐĽŠĄźĽŢĽĂĽČʸťúÎ󤏤ɤó¤Ę°úżô¤ň´üÂÔ¤š¤ë¤Ť¤ÎžđĘó¤ňťý¤Á¤Ţ¤šĄŁ"%s : %d" ¤Ę¤é (string -> int -> 'c, 'b, 'c) formatĄŁ
+
+ĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤ňˇżÉŐ¤ą¤š¤ë¤Č format Âč°ě°úżô¤ÎĚá¤ęˇż¤ŹÂčť°°úżô¤ČĆą¤¸ˇżĘŃżô(žĺ¤Ç¤Ď 'c)¤Ë¤Ę¤Ă¤Ć¤¤¤ë¤ł¤Č¤ËĂí°Ő¤ˇ¤Ć¤Ż¤Ŕ¤ľ¤¤ĄŁ¤ł¤ÎÂčť°°úżô¤ĎĄ˘printf ˇĎ´ŘżôÂŚ¤Îˇżžĺ¤ÎÍ×ŔÁĄ˘¤Ä¤Ţ¤ęĄ˘printf ˇĎ´Řżô¤ËĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤ČÉŹÍפʰúżô¤ňÁ´¤ĆĹŹÍѤˇ¤ż¤éşÇ˝ŞĹޤˤɤó¤Ęˇë˛Ě¤Ë¤Ę¤ë¤Ť¤Ă¤Ć¤ł¤ČĄŁprintf ¤Ę¤é¤ĐĄ˘(_, _, unit) format -> _ ¤Č¤¤¤ŚˇÁ¤ÇĄ˘şÇ˝ŞĹŞ¤Ë¤Ď unit ¤ňĘÖ¤ˇ¤Ţ¤š¤čĄ˘¤Č¤¤¤Ś°ŐĚŁ¤Ç¤šĄŁsprintf ¤Ę¤é¤ĐşÇ˝Şˇë˛Ě¤Ď string ¤ň¤â¤é¤¤¤ż¤¤¤Î¤Ç:
+>|ocaml|
+val sprintf : ('a, unit, string) format -> 'a   (* format ¤ÎÂčť°°úżô¤ËĂíĚܤˇ¤Ć¤Ż¤Ŕ¤ľ¤¤ *) 
+||<
+¤Ë¤Ę¤Ă¤Ć¤¤¤Ţ¤šĄŁ
+
+format ¤ÎÂčĆó°úżô¤ĎĄ˘%t ¤ä %a ¤Ę¤ÉĄ˘ocaml ĆČźŤ¤ÎšâłŹĽ×ĽęĽóĽż¤ÎˇżÉŐ¤ą¤ËÉŹÍפǥ˘ĽŐĽŠĄźĽŢĽĂĽČ¤ˇ¤żˇë˛Ě¤ÎʸťúÎó¤Ď¤É¤ł¤ËÁ÷¤é¤ě¤ë¤Ů¤­¤ŤĄ˘˝ĐÎĎĽÁĽăĽóĽÍĽë¤Ę¤Î¤ŤĄ˘ĽĐĽĂĽŐĽĄ¤Ę¤Î¤ŤĄ˘¤˝¤ě¤Č¤â sprintf ¤ÎÍͤËĘÖ¤ęĂͤËĚᤝ¤Đ¤¤¤¤¤Î¤ÇĆäˤ¤¤é¤Ę¤¤¤Î¤Ť¤ÎžđĘó¤Ç¤šĄŁ(¤ł¤ě¤Ë¤č¤Ă¤Ć %t ¤ä %a ¤Ç´üÂÔ¤ľ¤ě¤ë´Řżô¤Îˇż¤ŹĘѤď¤Ă¤Ć¤Ż¤ë):
+>|ocaml|
+val fprintf : out_channel -> ('a, out_channel, unit) format -> 'a  (* out_channel ¤Ë˝ń¤­˝Đ¤ˇ¤Ţ¤šĄŁfprintf ¤ÎÂč°ě°úżô¤Îˇż¤ČĆą¤¸ĄŁ *)
+val printf : ('a, out_channel, unit) format -> 'a                  (* fprintf ¤ÎĆĂźěÎă *)
+val sprintf : ('a, unit, string) format -> 'a                      (* Ćäˤ¤¤é¤Ę¤¤¤Î¤Ç unit *)
+val bprintf : Buffer.t -> ('a, Buffer.t, unit) format -> 'a        (* ĽĐĽĂĽŐĽĄ¤Ë˝ń¤­˝Đ¤ˇ¤Ţ¤šĄŁbprintf ¤ÎÂč°ě°úżô¤Îˇż¤ČĆą¤¸ĄŁ *)
+
+# ("%t %a" : (_,_,_) format);;
+- : (('a -> 'b) -> ('a -> 'c -> 'b) -> 'c -> 'b, 'a, 'b) format = <abstr>
+(* format ÂčĆó°úżô¤ÎˇżĘŃżô 'a ¤Ź %t ¤Č %a ¤ËÂĐąţ¤š¤ë´Řżô°úżô¤ÎÂč°ě°úżôˇż¤ËÂĐąţ¤ˇ¤Ć¤¤¤ë¤ł¤Č¤ËĂí°Ő *)
+||<
+
+*** format4, format6
+
+źÂ¤Ďť°°úżô¤ÎĽÇĄźĽżˇż format ¤ĎźÂ¤ĎĘ̤ΝͰúżô¤Îˇż format4 ¤ÎĽ¨Ľ¤ĽęĽ˘ĽšĄ˘¤˝¤ˇ¤Ć format4 ¤ĎĎť°úżô¤Î format6 ¤ÎĽ¨Ľ¤ĽęĽ˘Ľš¤Ç¤š (${libdir}/pervasives.mli ť˛žČ):
+>|ocaml|
+type ('a, 'b, 'c, 'd) format4 = ('a, 'b, 'c, 'c, 'c, 'd) format6
+
+type ('a, 'b, 'c) format = ('a, 'b, 'c, 'c) format4
+||<
+¤â¤Ś˛ż¤Ź¤Ę¤ó¤Ŕ¤Ť¤ľ¤Ă¤Ń¤ę¤ď¤Ť¤ę¤Ţ¤ť¤óĄŁpervasives.mli ¤âśŚÄ̤š¤ëťĎ¤á¤Îť°¤Ä¤ÎĽŃĽéĽáĄźĽż¤Ë¤Ä¤¤¤Ć¤Ď˛ňŔ⤡¤Ć¤¤¤Ţ¤š¤ŹĄ˘ťÄ¤ę¤ĎĘüĂ֤ǤšĄŁ
+ÎňťËĹŞ¤Ë¤ĎÄš¤¤´Ö format ¤ˇ¤Ť¤Ę¤Ť¤Ă¤ż¤Î¤Ç¤š¤ŹĄ˘ĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤ÎÁŕşî¤ň¤â¤ŚžŻ¤ˇťČ¤¨¤ë¤č¤Ś¤Ë˛ţ¤¤ˇ¤Ć¤¤¤Ă¤ż¤éĄ˘¤â¤Ś°ě¤ÄĽŃĽéĽáĄźĽż¤ŹÍߤˇ¤Ż¤Ę¤Ă¤ż¤Î¤Ź format4ĄŁ¤˝¤ˇ¤Ć¤č¤ęťČ¤¨¤ë¤č¤Ś¤Ë˛ţ¤¤ˇ¤ż¤é¤ľ¤é¤ËĆó¤ÄÍߤˇ¤Ż¤Ę¤Ă¤Á¤ă¤Ă¤ż¤č¤Ă¤Ć¤Î¤Ź format6:
+
+- ÂčťÍ°úżô¤Ď¸ĺ˝Ň¤š¤ë kprintf ¤Î¤ż¤á¤ÎˇŃÂł¤Ë´Ř¤š¤ëˇż
+- Âč¸ŢĄ˘ÂčĎť¤Ď¤č¤Ż¤ď¤Ť¤ó¤Ę¤¤¤ą¤ÉĄ˘ĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤ňϢˇë¤š¤ë¤ż¤á¤ËťČ¤Ă¤Ć¤¤¤ë¤ß¤ż¤¤ĄŁ ( Pervasives.(^^) ) 
+
+˝˝ÇŻ¸ĺ¤°¤é¤¤¤Ë¤Ď format10 ¤Č¤Ť¤Ë¤Ę¤Ă¤Ć¤¤¤ë¤Ť¤â¤ˇ¤ě¤Ţ¤ť¤óĄŁ
+
+** Ć°ĹŞ¤ĘĽŐĽŠĄźĽŢĽĂĽČʸťúÎ󥢤ϥ˘˝ĐÍč¤Ę¤¤ĄŠ
+
+ĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤ÎĆĂźě¤ĘˇżÉŐ¤ą¤ň¸Ť¤Ć¤­¤Ţ¤ˇ¤żĄŁĘ¸ťúÎóÄężô¤Ź format ¤Ë˛˝¤ą¤ëĄ˘¤ł¤ÎĆĂźě¤ĘˇżÉŐ¤ą¤Ď*Äężô*¤Ë¤Î¤ßČŻŔ¸¤ˇ¤Ţ¤šĄŁ¤Ç¤ĎĄ˘Ę¸ťúÎóÄężô°Ęł°¤Ť¤éĄ˘Ć°ĹŞ¤ËĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤ňŔ¸ŔŽ¤š¤ë¤ł¤Č¤Ď¤Ç¤­¤Ę¤¤¤Î¤Ç¤ˇ¤ç¤Ś¤ŤĄŁÎ㤨¤ĐĄ˘ĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤ňĽŐĽĄĽ¤Ľë¤Ť¤éĆɤߚţ¤ó¤Ç¤Ż¤ëĄ˘¤Č¤Ť¤Ç¤šĄŁšńşÝ˛˝¤ľ¤ě¤żĽáĽĂĽťĄźĽ¸ĽŐĽĄĽ¤Ľë¤ňşî¤Ă¤Ć¤˝¤ł¤Ť¤éłĆšń¸ě¤ÎĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤ňźč¤Ă¤Ć¤Ż¤ëĄ˘¤Ę¤ÉĄ˘źÂşÝ¤Ë¤˝¤Ś¤¤¤ŚÎă¤Ď¤˘¤ę¤˝¤Ś¤Ç¤šĄŁ
+
+¤Á¤ç¤Ă¤ČźŤżŽ¤Ź¤˘¤ę¤Ţ¤ť¤ó¤ŹĄ˘¤ł¤ě¤ĎÉáÄ̤ˤϽĐÍč¤Ţ¤ť¤óĄŁÉ¸˝ŕĽéĽ¤ĽÖĽéĽę¤Ë¤Ď string ¤ň format ¤ËĘѤ¨¤Ć¤Ż¤ě¤ë´Řżô¤Ź¤Ę¤¤¤Î¤Ç¤šĄŁ
+
+*** ¤¤¤äĄ˘˝ĐÍ褽¤Ś¤Ŕ! Ć°ĹŞ¤ĘĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤ÎˇżĽÁĽ§ĽĂĽŻ
+
+žŻ¤ˇšÍ¤¨¤Ţ¤ˇ¤ż¤ŹĄ˘¤Á¤ç¤Ă¤ČšŠÉפš¤ě¤Đ˝ĐÍč¤ëťö¤Ź¤ď¤Ť¤ę¤Ţ¤ˇ¤żĄŁ
+
+>|ocaml|
+val Printf.CamlinternalPr.Tformat.summarize_format_type :  ('a, 'b, 'c, 'd, 'e, 'f) format6 -> string
+||<
+¤Č¤¤¤Ś˛ř¤ˇ¤˛¤Ę´Řżô¤Ź¤˘¤ę¤Ţ¤šĄŁ¤ł¤ě¤ĎÍż¤¨¤é¤ě¤żĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤Î % ÉôĘŹ¤Ŕ¤ą¤ňČ´¤­˝Đ¤ˇ¤ĆĄ˘normalize ¤š¤ë´Řżô¤Ç¤šĄŁÎ㤨¤ĐĄ˘
+>|ocaml|
+# Printf.CamlinternalPr.Tformat.summarize_format_type "name: %s, age: %d";;
+- : string = "%s%i"
+||<
+°ěąţĄ˘ĆâÉôťČÍѤ˸¤륢¤ČĽłĽáĽóĽČ¤ľ¤ě¤Ć¤¤¤Ţ¤šÂž¤Ë¤âĄ˘%{fmt%} ¤Č¤¤¤ŚĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤ňźő¤ąźč¤ëĽŐĽŠĄźĽŢĽĂĽż¤Ź¤˘¤ęĄ˘¤ł¤ě¤âĆą¤¸¤č¤Ś¤Ęťö¤Ź˝ĐÍč¤Ţ¤š(¤ż¤Ŕ¤ˇĄ˘Obj.magic ¤ňťČ¤Ă¤Ć¤¤¤ë¤Ś¤¨Ą˘ËÜÍč¤ÎťČ¤¤Ęý¤Ç¤Ď¤Ę¤¤¤Čť×¤ď¤ě¤Ţ¤šĄŁ¤ż¤ŔĄ˘ËÜÍč¤ÎťČ¤¤Ęý¤Ă¤Ć¤˘¤ë¤Î¤Ă¤ĆľĄÇ˝¤Ç¤š¤ŹĄ˘Ą˘Ą˘):
+>|ocaml|
+# Printf.sprintf "%{%}" (Obj.magic "name : %s, age : %d");;
+- : string = "%s%i"
+||<
+
+¤ł¤ě¤ňťČ¤¨¤ĐĄ˘źÂšÔťţ¤Ë string ¤Ť¤éĄ˘¤˝¤ě¤ň format ¤Č¤ˇ¤Ć˛ňźá¤ˇ¤żžěšç¤Îˇż¤ňʸťúÎó¤ÎˇÁ¤ÇČ´¤­˝Đ¤š¤ł¤Č¤Ź¤Ç¤­¤Ţ¤šĄŁ¤ł¤ÎʸťúÎó¤ňČćłÓ¤š¤ě¤ĐˇżĽÁĽ§ĽĂĽŻ¤Ź˛ÄÇ˝¤Ç¤šĄŁ
+
+>|ocaml|
+val fmt_check : ('a, 'b, 'c, 'd, 'e, 'f) format6 -> string -> ('a, 'b, 'c, 'd, 'e, 'f) format6
+
+let fmt_check (fmt_type : ('a,'b,'c,'d,'e,'f) format6) (fmt_string : string) : ('a,'b,'c,'d,'e,'f) format6 =
+  let string_fmt_type = Printf.sprintf "%{%}" (Obj.magic fmt_type) in
+  let fmt_string = Obj.magic fmt_string in
+  if string_fmt_type = Printf.sprintf "%{%}" fmt_string then fmt_string
+  else invalid_arg ("check " ^ string_fmt_type)
+;;
+||<
+Obj.magic ¤ňťČ¤ś¤ë¤ňĆŔ¤Ţ¤ť¤ó¤Ç¤ˇ¤ż¤Î¤ÇĄ˘ŔľÄž´í¸ą¤ĘĽłĄźĽÉ¤Ç¤šĄŁ¤â¤ˇ¤Ť¤š¤ë¤ČźÂ¤Ď´Ö°ă¤Ă¤Ć¤¤¤ĆĄ˘¤ł¤Î´Řżô¤ňťČ¤Ă¤Ć¤¤¤ë¤ČĽŻĽéĽĂĽˇĽĺ¤š¤ë¤Ť¤â¤ˇ¤ě¤Ţ¤ť¤ó¤Î¤ÇĂí°Ő¤ň¤Ş´ę¤¤¤ˇ¤Ţ¤šĄŁ
+fmt_check fmt str ¤Ďˇż¤ňÍż¤¨¤ë¤ż¤á¤ÎĽŐĽŠĄźĽŢĽĂĽČʸťúÎó fmt ¤ňťŘÄꤡĄ˘źÂšÔťţ¤Ë str ¤ŹĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤Č¤ˇ¤ĆĆą¤¸ˇż¤ňťý¤Ă¤Ć¤¤¤ë¤Ť¤É¤Ś¤Ť¤ňĽÁĽ§ĽĂĽŻĄ˘Ćą¤¸ˇż¤Ę¤éĚľÍý¤ä¤ęĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤Ëˇż¤ňĘŃ´š¤ˇ¤Ć¤ˇ¤Ţ¤¤¤Ţ¤šĄŁ¤ľ¤ĆĄ˘¤ł¤Î fmt_check ¤ňťČ¤Ś¤ČĄ˘źĄ¤Î¤č¤Ś¤Ęź°¤ň˝ń¤Ż¤ł¤Č¤Ź¤Ç¤­¤Ţ¤š:
+>|ocaml|
+let f () = 
+  Printf.printf (fmt_check "%s%d" (read_line ())) "hello" 42
+||<
+ɸ˝ŕĆţÎϤŤ¤éʸťúÎó¤ňĆɤߚţ¤ßĄ˘¤˝¤ě¤Ź "%s%d" ¤ČĆą¤¸ format ˇż¤ňťý¤ÄĽŐĽŠĄźĽŢĽĂĽČʸťúÎó¤Č¤ˇ¤Ć˛ňźá¤Ç¤­¤ë¤Ę¤é¤Đ Printf.printf ¤ňźÂšÔ¤ˇ¤Ţ¤š:
+>|ocaml|
+# f ();;
+hello world    (* "hello world" ¤Ď "%s%d" ¤Č¤ĎČó¸ß´š *)
+Exception: Invalid_argument "check %s%i".
+# f ();;
+name=%s; value=%d;   (* ¤ł¤ě¤Ę¤é¸ß´š! *)
+name=hoge; value=42;- : unit = ()
+||<
+
+** Printf ¤Ď¤Ţ¤ŔÂł¤­¤Ţ¤šĄ˘Ą˘Ą˘
+
+Printf ¤Ë¤ĎĄ˘¤˘¤ČĆó¤Ä¤Ű¤Éż¨¤ě¤Ć¤Ş¤Ż¤Ů¤­ĆâÍƤŹ¤˘¤ë¤Î¤Ç¤š¤ŹĄ˘¤ł¤ě¤Ţ¤żÄš¤Ż¤Ę¤ę¤˝¤Ś¤Ç¤šĄŁźč¤ęšç¤¨¤ş¤ł¤ł¤ÇĂŚšĆ¤ˇ¤ĆÁ°ČžÉô¤Č¤ľ¤ť¤Ć¤Ż¤Ŕ¤ľ¤¤ĄŁ°ěĂśťĹŔÚ¤ę¤Ę¤Ş¤ľ¤Ę¤¤¤ČČ˝¤ę¤Ë¤Ż¤¤˝ę¤ňÄž¤š¤Î¤â˛Żšĺ¤Ę¤Î¤Ç¤šĄŁ

log/printf-euc2.txt

+*[OCaml][Tutorial] OCaml標準ライブラリ探訪 #3.1: Printf: 便利だけどいろいろ謎のある奴
+
+>>
+関連リンク:
+<a href="http://d.hatena.ne.jp/camlspotter/20091013/1255443545
+">OCaml 標準ライブラリ探訪 第0回</a>
+その他の回は第0回のトラックバックよりご覧ください。
+<<
+
+Printf の続きです。前回は型の話をせざるを得なかったので解りにくくならざるを得ませんでしたが、今回はもうちょっと楽だと思います。
+
+** 自前の printf 系関数を作る: ksprintf / kfprintf / kbprintf
+
+printf 系関数が提供するフォーマットのインターフェースは色ă€
+
+例えば、sprintf と failwith を組み合わせて、 printf っぽく、フォーマット文字列とそのフォーマット文字列がĺż
+>|ocaml|
+# let failwithf fmt = failwith (Printf.sprintf fmt);;
+val failwithf : (string, unit, string) format -> 'a = <fun>
+# failwithf "you failed!";;
+Exception: Failure "you failed!".
+||<
+上手くいってる?いきません。何か引数を取るフォーマッタをĺ
+>|ocaml|
+# failwithf "your error is %s" "memory over";;
+Characters 10-28:
+  failwithf "your error is %s" "memory over";;
+            ^^^^^^^^^^^^^^^^^^
+Error: This expression has type (string -> 'a, 'b, 'c, 'd, 'd, 'a) format6
+       but an expression was expected of type
+         (string, unit, string) Pervasives.format =
+           (string, unit, string, string, string, string) format6
+||<
+何だか文句を言われました。エラーに関しては前回(#3.0)で説明した format の型を理解してないと読みづらいですが、failwithf が期ĺž
+
+引数が増えたのが原因かな?じゃあ、failwithf でも引数を増やしてあげましょう: 
+>|ocaml
+# let failwithf fmt v = failwith (Printf.sprintf fmt v);;
+val failwithf : ('a -> string, unit, string) format -> 'a -> 'b = <fun>
+# failwithf "your error is %s" "memory over";;
+Exception: Failure "your error is memory over".
+||<
+上手くいきました?いえ、いきません。始めの failwithf "you failed!" が型エラーになります:
+>|ocaml|
+# failwithf "you failed!";;
+Characters 10-23:
+  failwithf "you failed!";;
+            ^^^^^^^^^^^^^
+Error: This expression has type
+         ('a -> string, unit, string, string, string, 'a -> string) format6
+       but an expression was expected of type
+         ('a -> string, unit, string) Pervasives.format) =
+           ('a -> string, unit, string, string, string, string) format6
+||<
+
+これは、与えるフォーマット文字列によって、sprintf が受け取る引数の数が変わってくるのが原因です。出来れば、
+>|ocaml|
+let failwithf fmt <引数1> ... <引数n> (* n は fmt がĺż
+  failwith (sprintf fmt <引数1> ... <引数n>)
+;;
+||<
+と書けて、fmt によって n が動的に変われば嬉しいのですが、静的型付言語ではそんな芸当は出来ません。
+>>
+>>
+C では可変長引数関数の定義は varargs とか使って引数リストを引数é
+<<
+<<
+
+じゃあ、自作 printf は出来ないのかというと、format 型しかなかった昔は出来ませんでした。format 型が format4 に拡張された現在は、 ksprintf を使えば出来ます!!
+
+*** ksprintf を使った自作 printf 系関数の実čŁ
+
+ksprintf は sprintf に似ていますが、フォーマット文字列の引数の前に一つ関数 k を引数に取ります:
+>|ocaml|
+val sprintf : ('a, unit, string, string) format4 -> 'a
+val ksprintf : (string -> 'b) -> ('a, unit, string, 'b) format4 -> 'a;;
+               ^^^^^^^^^^^^^^
+||<
+ksprintf k fmt 引数1 ... 引数n は与えられたフォーマット文字列 fmt と fmt きĺż
+
+>|ocaml|
+# let failwithf fmt = Printf.ksprintf failwith fmt;;
+val failwithf : ('a, unit, string, 'b) format4 -> 'a = <fun>
+# failwithf "you failed!!";;
+Exception: Failure "you failed!!".
+# failwithf "your error is %s" "memory over";;
+Exception: Failure "your error is memory over".
+||<
+Value polymorphism restriction の影響で、 let failwithf = Printf.ksprintf failwith ではなく、fmt の引数がわざわざ足されている事に注意してください。これがないと、failwithf の型が多相型にならないのです。副作用のある関数型言語の型システムの限界を乗り越えるためのテクニックです。
+
+瑣末な点になりますけれども、 failwithf はこのままの定義でもちろん良いのですが、間違ってフォーマット文字列よりも多い引数を与えてもエラーとして弾いてくれないという実用上の問題があります。(例えば failwithf "hello world" 1 2 3 4 5 が通る) この問題を取り合えず修正した次の定義の方をお勧めします:
+>|ocaml|
+# let failwithf fmt = Printf.ksprintf (fun s () -> failwith s) fmt;;
+val failwithf : ('a, unit, string, unit -> 'b) format4 -> 'a = <fun>
+# failwithf "you failed!!" ();;
+Exception: Failure "you failed!!".
+# failwithf "your error is %s" "memory over" ();;
+Exception: Failure "your error is memory over".
+
+(* 多すぎると型エラーになる *)
+# failwithf "hello world" 1 2 3 4 5;;
+Characters 24-25:
+  failwithf "hello world" 1 2 3 4 5;;
+                          ^
+Error: This expression has type int but an expression was expected of type
+         unit
+||<
+これだとĺż
+
+*** CPS変換
+
+ksprintf ぎ様なĺ
+>|ocaml|
+let sprintf fmt = ksprintf (fun s -> s) fmt;;
+||<
+
+>>
+>>
+CPS は、コンパイラの実čŁ
+<<
+<<
+
+*** ksprintf の親戚
+
+ksprintf は sprintf の CPS 版ですが、これに対応して、fprintf (ファイル書き込み) と bprintf (バッファ書き込み) の CPS 版として kfprintf, kbprintf があります。使いかは ksprintf と同じです。
+
+** Printf はé
+
+*** (^) か Printf.sprintf かはたまた Buffer.add_string か
+
+さて、便利かつ安ĺ
+
+論より証拠、簡単なベンチを取ってみた。"hello" と "world" を concat するのを (^) と Printf.sprintf "%s%s", Buffer でやって比べてみます。Buffer は sprintf の実čŁ
 (${libdir}/printf.ml 参ç
+
+>|ocaml|
+let num = 10000000
+
+for i = 0 to num do "hello" ^ "world" done
+
+for i = 0 to num do sprintf "%s%s" "hello" "world" done
+
+for i = 0 to num do 
+  let b = Buffer.create 0 in
+  Buffer.add_string b "hello";
+  Buffer.add_string b "world";
+  Buffer.contents b
+done
+||<
+
+結果:
+>||
+        : 時間
+(^)     : 1.547 : ##
+sprintf : 6.880 : ########
+Buffer  : 4.426 : #####
+||<
+うわっé
+
+実を言うとこれは (^) に非常に有利なベンチで、 Buffer が (^) に負けるのは当たり前。"hello" ^ "world" は一回しかアロケーションを行いませんが、対応する Buffer 版は(多分)三回も行います。
+>>
+>>
+Buffer が強いのは多数の文字列をつなげて一つにする場合です。n 個の文字列を連結するのに (^) は n-1 回のメモリアロケーションがĺż
+<<
+<<
+
+もちろん printf の方が高機能ですからある程度オーバーヘッドがあってもいいのですが、ĺ†
+
+これだと (^) ぎ 3.13 倍位の時間。ちょっとまし。なぜ早くなるかというと、sprintf にフォーマットを与えた時点で、printf.ml ĺ†
+
+>|ocaml|
+(* sprintf 改 *)
+let f = Printf.sprintf "%s%s" in
+for i = 0 to num do f "hello" "world" done
+||<
+
+>||
+          : 時間
+(^)       : 1.547 : ##
+sprintf   : 6.880 : ########
+Buffer    : 4.426 : #####
+sprintf改 : 4.660 : ######
+||<
+
+
+
+---
+
+
+で、終わりかとういと、まだ終わりません。なぜかというと、折角部分適用してクロージャ生成の回数を減らしても、まだ printf はé
+
+じゃあ、この型検査時のスキャンćƒ

log/printf-utf.txt

+*1257099984*[OCaml][Tutorial] OCaml 標準ライブラリ探訪 #3.0: Printf: 便利だけどいろいろ謎が
+printf っが OCaml でも便利ですよね。C から連綿と続いている半ば常識の % インターフェースに加え、ちょっと不思議な型推論のおかげで型安ĺ
+
+** Printf の特殊な型付け
+
+printf 系の関数は状況によって異なる数の引数 (variable length arguments (可変長引数)) を取ることが出来ます。例えば、
+>|ocaml|
+open Printf
+let name = "hogera";;
+let value = 42;;
+let () = printf "hello world\n";;
+let () = printf "your name is %s\n" name;;
+let () = printf "%s : %d\n" name value;;
+||<
+name や value の適用をせず、toplevel で実行すると型が出てきて解り易い:
+>|ocaml|
+open Printf;;
+# printf "hello world\n";;
+hello world
+- : unit = ()
+# printf "your name is %s\n";;
+- : string -> unit = <fun>
+# printf "%s : %d\n";;
+- : string -> int -> unit = <fun>
+||<
+ML の普通の型付けではこんな事は出来ません。ある関数に文字列を与えて返ってきた値の型が unit だったり string -> unit という関数だったりするのは明らかに不自然です。何か特殊な事をやっているに違いありません。${libdir}/printf.mli ので printf の型を見てみましょう:
+>|ocaml|
+val printf : ('a, out_channel, unit) format -> 'a
+||<
+何だこれは?関数みたいですけど、第一引数は string じゃなくって ('a, out_channel, unit) format とかいうデータ型です。そして返り値の型が型変数 'a。これは一体どうなっているのでしょうか。
+
+*** 実は特殊なのは文字列定数の型付け
+
+ここが今回一番難しい所。型とか興味ないと正直辛いから、苦手な人は次節の「動的なフォーマット文字列」まで飛ばしてください。ひとまず、さようなら。
+
+printf の第一引数は ('a, out_channel, unit) format という型ですが、そこに文字列を適用しても ocaml は何にも文句を言わない。これは一体どういうことでしょうか?
+>|ocaml|
+# "hello world\n";;
+- : string = "hello world\n"
+# "your name is %s\n";;
+- : string = "your name is %s\n"
+# "%s : %d\n";;
+- : string = "%s : %d\n"
+||<
+おかしい、これはみんな文字列のはず、なのに、、、format なんて型じゃないよ!!
+
+では、敢えて、format 型として ocaml に与えるとどうなるでしょうか。(文字列 : (_,_,_) format) で実行してみます。( _ は型変数 'a と同じですけど、まー後で使わないし、名前なんかどーでもいいよって時に使います):
+>|ocaml|
+# ("hello world" : (_,_,_) format);;
+- : ('a, 'b, 'a) format = <abstr>
+# ("your name is %s\n" : (_,_,_) format);;
+- : (string -> 'a, 'b, 'a) format = <abstr>
+# ("%s : %d\n" : (_,_,_) format);;
+- : (string -> int -> 'a, 'b, 'a) format = <abstr>
+||<
+すると文字列の癖に format 型として型付けされました。その上、型が、format の第一引数部分が、それぞれ違います。'a, string -> 'a, string -> int -> 'a… 最後の型変数 'a を除けばフォーマット文字列が受けとるべき %s や %d に対応した引数型が出てきました。(始めの "hello world" は何も引数をĺż
+
+文字列なのに、string のはずなのに、format として解釈させると、そのĺ†
+
+わからないのも当然です。実は printf のために、ocaml では文字列定数の型付けに特殊なルールがあるのです:
+
+- 普段は "文字列" の型は string
+- でも、もし format という型を期ĺž
+一方、printf 系関数が持っている型自体はこの format というデータ型を使っている意外、特殊な所はありません。(ただĺ†
+
+「format という型を期ĺž
+「printf 系関数の様に format を引数として持つ関数に直接文字列を与えた場合+α」
+と理解していただければ良いと思います。
+
+>>
+>>
+ほんとに、日本語で KY って言ってもその心は微妙なのと同じぐらい微妙:
+>|ocaml|
+(* 空気を読める例 *)
+# (fun x -> (x : (_,_,_) format)) "hello world";;
+- : ('_a, '_b, '_a) format = <abstr>
+
+(* もっと読めるよ!! *)
+# [ (fun x -> (x : (_,_,_) format)) "hello world"; "bye world" ];;
+- : ('_a, '_b, '_a) format list = [<abstr>; <abstr>]
+
+(* でも逆にすると読めないんだ!! *)
+# [ "bye world"; (fun x -> (x : (_,_,_) format)) "hello world";  ];;
+Characters 15-60:
+  [ "bye world"; (fun x -> (x : (_,_,_) format)) "hello world";  ];;
+                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Error: This expression has type
+         ('a, 'b, 'a) format = ('a, 'b, 'a, 'a, 'a, 'a) format6
+       but an expression was expected of type string
+
+(* let があるともう読めない *)
+# let str = "hello world" in (str : (_,_,_) format);; 
+Characters 28-31:
+  let str = "hello world" in (str : (_,_,_) format);; 
+                              ^^^
+Error: This expression has type string but an expression was expected of type
+         ('a, 'b, 'c) format = ('a, 'b, 'c, 'c, 'c, 'c) format6
+||<
+こんな感じです。ML の型推論をご存知の人なら、型推論アルゴリズムĺ†
+
+printf のフォーマット文字列をもっと綺麗な型体系の中で扱ってやろうとすると、例えば依存型とか使えば良いと思いますし、そういう研究もあったかと。
+ocaml では、フォーマット文字列のために依存型なんかĺ
+<<
+<<
+
+printf "%s : %d" の例をもう一度見てみましょう。
+- printf の型は ('a, out_channel, unit) format -> 'a なので、第一引数に与えられた文字列 "%s : %d" は string という型ではなく、format として解釈されます。 
+- %s と %d があるので、上でも見たように (string -> int -> 'b, 'c, 'b) format (printf の型の型変数と混乱しないように型変数名を変えています)
+- これが printf の型の第一引数部分と unify (単一化) されると、(string -> int -> unit, out_channel, unit) format という型に。
+- この unification で型変数 'a が string -> int -> unit になりますから、 printf の型は (string -> int -> unit, out_channel, unit) format -> string -> int -> unit
+- printf "%s : %d" という式ĺ
+どうでしょう、ついてこれましたか?
+
+format 型は三つ引数があります。('a, 'b, 'c) format。第一引数 'a は今までも見てきたとおり、フォーマット文字列がどんな引数を期ĺž
+フォーマット文字列を型付けすると format 第一引数の戻り型が第三引数と同じ型変数(上では 'c)になっていることに注意してください。この第三引数は、このフォーマット文字列を適用したら最終的にどんな結果になるのかのćƒ
+>|ocaml|
+val sprintf : ('a, unit, string) format -> 'a   (* format の第三引数に注目してください *) 
+||<
+になります。これはフォーマット文字列を使う printf 系関数側の型上の要請を表すための物です。
+format の第二引数は、%t や %a など、ocaml 独自の高階プリンタの型付けにĺż
+>|ocaml|
+val fprintf : out_channel -> ('a, out_channel, unit) format -> 'a  (* out_channel に書き出します。fprintf の第一引数の型と同じ。 *)
+val printf : ('a, out_channel, unit) format -> 'a                  (* fprintf の特殊例 *)
+val sprintf : ('a, unit, string) format -> 'a                      (* 特にいらないので unit *)
+val bprintf : Buffer.t -> ('a, Buffer.t, unit) format -> 'a        (* バッファに書き出します。bprintf の第一引数の型と同じ。 *)
+
+# ("%t %a" : (_,_,_) format);;
+- : (('a -> 'b) -> ('a -> 'c -> 'b) -> 'c -> 'b, 'a, 'b) format = <abstr>
+(* format 第二引数の型変数 'a が %t と %a に対応する関数引数の第一引数型に対応していることに注意 *)
+||<
+
+** 動的なフォーマット文字列、は、出来ない?
+
+フォーマット文字列の特殊な型付けを見てきました。文字列定数が format に化ける、この特殊な型付けは*定数*にのみ発生します。では、動的にフォーマット文字列を生成する、例えば、フォーマット文字列をファイルから読み込んでくる、ようなことは出来ないのでしょうか。国際化されたメッセージファイルを作ってそこから各国語のフォーマット文字列を取ってくる、など、ありそうです。
+
+ちょっと自信がありませんが、これは普通には出来そうにありません。標準ライブラリには string を format に変えてくれる関数がないのです。
+
+*** いや、出来そうだ、 %{ %} を使えば、、、
+
+動的型付けは出来そうなものですが、、、と考えていたら、出来ました。ここで、動的型付け、というのは、
+>|ocaml|
+let print_hoge_42 str = 
+  Printf.printf <fmt_check str> "hoge" 42
+;;
+||<
+と書いたときに str が "%s : %d" や "name %s, age %d" のような第一引数として string を、第二引数として int を取るようなフォーマット文字列かどうかを実行時検査して、もしそうでなかった場合は例外を発生させる機構です。( <fmt_check str> は正しい文法ではありません。多分こんな式があってこんな感じならいいなぁという pseudo syntax だと思ってください。) もしこれが上手くいけば、
+>|ocaml|
+print_hoge_42 "%d : %d" (* 一つ目の % は文字列を取る %s でなければいけない *)
+||<
+は実行時エラー(例外)になるわけです。実行時に受け取った文字列中の %なんたら をスキャンして調べ、静的型付け段階でわかっている printf に与えられる引数の型ćƒ
+>|ocaml|
+let print_something_and_42 str something =
+  Printf.printf <fmt_check str> something 42
+in
+print_something_and_42 "%s : %d" "hoge";
+print_something_and_42 "%d : %d" 42
+;;
+||<
+こんな場合は str に期ĺž
+
+道ĺ
+>|ocaml|
+val check : ('a, 'b, 'c, 'd, 'e, 'f) format6 -> string -> ('a, 'b, 'c, 'd, 'e, 'f) format6
+
+let fmt_check (fmt_type : ('a,'b,'c,'d,'e,'f) format6) (fmt_string : string) : ('a,'b,'c,'d,'e,'f) format6 =
+  let string_fmt_type = Printf.sprintf "%{%}" (Obj.magic fmt_type) in
+  let fmt_string = Obj.magic fmt_string in
+  if string_fmt_type = Printf.sprintf "%{%}" fmt_string then fmt_string
+  else invalid_arg ("check " ^ string_fmt_type)
+;;
+||<
+format6 の事は後述しますが、ここでは format のより複雑なバージョンと思ってください。
+Obj.magic を使ざるを得ませんでしたので、正直危険なコードです。もしかすると実は間違っていて、この関数を使っているとクラッシュするかもしれませんので注意をお願いします。
+fmt_check fmt str は fmt 型を与えるためのフォーマット文字列を指定し、実行時に str がこの fmt とフォーマット文字列として同じ型を持っているかどうかをチェック、同じ型なら無理やりフォーマット文字列に型を変換します。フォーマット文字列を normalize するために、 %{%} を使っています、が、これも、仕様通りの使い方じゃないので、偶然上手く行っているだけかもしれません。ただ、 %{fmt%} の意味のある使い方って正直思いつかないので、 fmt_check みたいな事をやりたいと思って中途半端になっている可能性もあります。(printf.ml をよく読めば、一部実čŁ
+まあ、さて、この fmt_check を使うと、次のような式が書けます:
+>|ocaml|
+let f () = 
+  Printf.printf (fmt_check "%s%d" (read_line ())) "hello" 42
+||<
+標準ĺ
+>|ocaml|
+# f ();;
+hello world    (* "hello world" は "%s%d" とは非互換 *)
+Exception: Invalid_argument "check %s%i".
+# f ();;
+name=%s; value=%d;   (* これなら互換! *)
+name=hoge; value=42;- : unit = ()
+||<
+
+** Printf はまだ続きます、、、
+
+Printf には、あと二つほど触れておくべきĺ†
+OCaml ぎ printf って便利ですよね。C から連綿と続いている半ば常識の % インターフェースに加えて、ちょっと不思議な型推論のおかげで型安ĺ
+
+さて、そんな便利かつ安ĺ
+
+論より証拠、簡単なベンチを取ってみた。"hello" と "world" を concat するのを (^) と Printf.sprintf "%s%s" でやって比べてみます:
+
+>|ocaml|
+for i = 0 to num do "hello" ^ "world" done
+||<
+
+>|ocaml|
+for i = 0 to num do sprintf "%s%s" "hello" "world" done
+||<
+
+大体 sprintf が (^) の 4.6 倍位時間がかかります。もちろん printf の方が高機能ですからある程度オーバーヘッドがあってもいいのですが、こりゃひどすぎる。というわけで、再帰の中で printf を使うのはやめましょう。
+
+でもどうしても使いたい、という時は、printf fmt を部分適用して再帰から外に出してやると、少しよくなる。
+
+>|ocaml|
+let f = sprintf "%s%s" in
+for i = 0 to num do f "hello" "world" done
+||<
+
+これだと (^) ぎ 3.13 倍位の時間。ちょっとまし。なぜ早くなるかというと、sprintf にフォーマットを与えた時点で、printf.ml ĺ†
+
+で、終わりかとういと、まだ終わりません。なぜかというと、折角部分適用してクロージャ生成の回数を減らしても、まだ printf はé
+
+じゃあ、この型検査時のスキャンćƒ

typing/printtyp.ml

   let tree_original = do_heuristic := false; tree_of_typexp sch ty in
   let tree_heuristic = do_heuristic := true; tree_of_typexp sch ty in
   if tree_original <> tree_heuristic then
-    Format.fprintf ppf "@[%a@ ((@[%a@]))@]"
+    Format.fprintf ppf "@[%a@ < @[%a@] >@]"
       !Oprint.out_type tree_heuristic
       !Oprint.out_type tree_original
   else
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.