13

なぜこの動作が発生するのですか?

# Printf.sprintf ("Foo %d %s") 2 "bar";;
- : string = "Foo 2 bar"

# Printf.sprintf ("Foo %d" ^ " %s") 2 "bar";;
  Printf.sprintf ("Foo %d" ^ " %s") 2 "bar";;
Error: This expression has type string but an expression was expected of type
         ('a -> 'b -> 'c, unit, string) format =
           ('a -> 'b -> 'c, unit, string, string, string, string) format6

文字列の連結が最初に評価されるので、すべてが通常どおりに進行することを期待します。これは、Printfが採用している型システムのトリックと関係がありますか?

4

2 に答える 2

19

はい、それは型システムのトリックと関係があります。フォーマット文字列を作成する場合は、(^^)演算子を使用する必要があります。

# Printf.sprintf ("Foo %d" ^^ " %s") 2 "bar";;
- : string = "Foo 2 bar"

私はこのトリックに精通していませんが、タイピングコンテキストで必要な場合は、コンパイラが文字列定数をprintf形式にプロモートする用意があると思います。ただし、の結果は("Foo %d" ^ " %s")文字列定数ではないため、昇格されません。(^^)演算子は、両方のオペランドが文字列定数である場合にプロモートできるタイピングコンテキストを作成します。

文字列定数である必要がある理由がわかります。そうでない場合、(出力される値の)関連するタイプを判別できません。

于 2012-05-02T01:19:22.763 に答える
10

^この問題は、オペレーターだけでなく、はるかに広範囲に発生します。基本的に、OCamlコンパイラは、フォーマット文字列がリテラル文字列であることを認識している必要があり、リテラル文字列はコンパイル時に認識されている必要があります。そうでなければ、OCamlはコンパイル時に文字列をこのBLAHBLAH format6タイプにキャストできません。このPrintfモジュールは、コンパイル時に完全に認識されているフォーマット文字列、またはすでにBLAHBLAH format型にキャストされているフォーマット文字列でのみ正しく機能します。

通常、この問題は、演算子を使用し、コードでこれらの文字列を使用するに、すべてのリテラル文字列を型に^^明示的にキャストすることで解決できます。BLAHBLAH format

別の例を次に示します。

  # Printf.sprintf (if true then "%d" else "%d ") 2;;
  Error: This expression has type string but an expression was expected of type
     ('a -> 'b, unit, string) format =
       ('a -> 'b, unit, string, string, string, string) format6
  (* define a type abbreviation for brevity *)
  # type ('a,'b) fformat = ('a ->'b, unit, string) format;;
  type ('a, 'b) fformat = ('a -> 'b, unit, string) format
  # Printf.sprintf (if true then ("%d":('a,'b)fformat) else ("%d ":('a,'b)fformat)) 2;;
  - : string = "2"

if ... then "a" else "b"OCamlシステムはにキャストできることを認識できませんBLAHBLAH format各リテラル文字列を自分でにキャストするBLAHBLAH formatと、すべてが機能します。(注: OCamlは文字列がリテラルであることを確認できないため、全体if/then/elseをにキャストしようとしても機能しません。)BLAHBLAH format

問題の原因は型の安全性の要件です。OCamlはすべてなどに正しい型の引数があることを要求し%dコンパイル時%sにこれを保証します。コンパイル時にフォーマット文字列全体がわかっていない限り、で型の安全性を保証することはできません。したがって、たとえばランダムに選択するなど、複雑なアルゴリズムを介して計算されたフォーマット文字列で使用することはできません。PrintfPrintf%s%d

フォーマット文字列を計算するために使用する場合if/then/else、OCamlは複雑なアルゴリズムであり、コンパイル時に型の安全性を検証することはできません。オペレーターは型^^について知ってBLAHBLAH formatおり、フォーマット文字列を連結するときに正しい結果を生成します。しかし、if/then/elseについてはわかりませんBLAHBLAH format。また、に代わる組み込みの方法はありませんif/then/else(ただし、そのようなことを自分で定義できると思います)。

于 2012-05-03T08:03:34.610 に答える