2010-03-05 4 views
9

Ich bin ein OCaml-Neuling. Ich spiele mit Schnipsel "Hallo Welt" herum und stieß auf diese Situation. Hier ist eine Sitzung mit dem Dolmetscher mit einigen zusätzlichen Bemerkungen:Warum funktioniert dieses kurze OCaml-Snippet mit Printf.printf nicht?

# let average a b = 
    (a +. b) /. 2.;; 
val average : float -> float -> float = <fun> 
# average 1. 4.;; 
- : float = 2.5 
# string_of_float (average 1. 4.);; 
- : string = "2.5" 

(* this fails...*) 
# let _ = Printf.printf (string_of_float (average 1. 4.));; 
Error: This expression has type string but an expression was expected of type 
     ('a, out_channel, unit) format = 
      ('a, out_channel, unit, unit, unit, unit) format6 

(* yet this works *) 
# "hello!";; 
- : string = "hello!" 
# let _ = Printf.printf "hello!";; 
hello!- : unit =() 

(* another failed attempt *) 
# let s = string_of_float (average 1. 4.);; 
val s : string = "2.5" 
# s;; 
- : string = "2.5" 
# let _ = Printf.printf s;; 
Error: This expression has type string but an expression was expected of type 
     ('a, out_channel, unit) format = 
      ('a, out_channel, unit, unit, unit, unit) format6 

(* and this also works?? *) 
# let _ = Printf.printf "2.5";; 
2.5- : unit =() 

Also hier ist die Situation. string_of_float (average 1. 4.) gibt eine Zeichenkette zurück, wie "hello!". Wenn ich "hello!" in Printf.printf gebe, funktioniert es wie erwartet . Wenn ich string_of_float (average 1. 4.) zu Printf.printf gebe, schlägt es fehl und teilt mit, dass es erwartete keine Schnur aber diesen anderen sonderbaren Typ erwartete. Aber warum funktionieren "hello!" und "2.5" dann?

Was ist los?

Antwort

15

Es gibt eine Art "Überladung" der Bedeutung von String-Literalen in OCaml. Zur Kompilierzeit können sie entweder als String oder als Format interpretiert werden (was im Typsystem völlig unterschiedliche Dinge sind), je nachdem, was der Typ-Prüfer denkt. Wenn es entscheidet, dass es ein Format sein soll, dann wird die Formatzeichenkette direkt zur Kompilierzeit geparst (deshalb ist es möglich, die Argumente zu printf zur Kompilierzeit zu prüfen). (Im Gegensatz zu C, das die Zeichenfolge zur Laufzeit analysiert.) Es gibt jedoch keine einfache Möglichkeit, zur Laufzeit von einer Zeichenfolge in ein Format zu konvertieren. Wenn Sie also Printf.printf "2.5" sehen, ist "2.5" eigentlich kein String, sondern ein spezieller Formattyp, der zur Kompilierzeit analysiert wurde. Deshalb können Sie keine Zeichenfolge dafür ersetzen. Wenn Sie nur eine Zeichenfolge drucken möchten, möchten Sie möglicherweise print_string (oder print_endline, wenn Sie eine neue Zeile möchten) verwenden.

+0

Das macht für mich Sinn. Vielen Dank! – dimatura

+1

Nicht in der Lage, ein "format6" zur Laufzeit zu konstruieren ist wirklich ... fehlt. Gibt es einen Weg (um es selbst zu überarbeiten), um dasselbe Ziel zu erreichen? (Wie können Sie zum Beispiel eine Zeichenkette durch eine beliebige Anzahl von zur Laufzeit bestimmten Leerzeichen auffüllen? In C können wir 'sprintf (sprintf (" %%% ds ", width), str)'. – kizzx2

+0

@ kizzx2: für diese spezielle Aufgabe Es gibt eine alternative Lösung: Die Formatzeichenfolge-Syntax erlaubt es, die Breite oder die Genauigkeit durch ein Sternchen zu ersetzen, in diesem Fall wird es von einem zusätzlichen Argument vor dem Hauptargument genommen: 'Printf.sprintf"% * s "Breite str "; diese Syntax ist auch in C99, Perl, Python, Ruby, Go usw. verfügbar. Aber natürlich ist dies nur ein spezieller Fall, für andere Dinge kann es sein, dass Sie Pech haben. Es gibt einen Operator' ^^ 'in OCaml zum Anhängen von Formaten zusammen, aber Sie brauchen noch Formate zum Anhängen, also weiß ich nicht, wie nützlich das ist. – newacct

3
Printf.printf "%s" anyStringExpr 

wird funktionieren. Das erste Argument für printf ist etwas magisch. (Andere füllen die Details aus.)

+0

Ja, das erste Argument ist eine Formatzeichenfolge, die Anweisungen enthält, und wenn sie angewendet wird, gibt sie eine Funktion mit dieser Anzahl von Direktiven als Parameter zurück. Definitiv magisch. – nlucaroni

+1

Art von. Wie auch immer, es ist wirklich toll, eine vollständig typgeprüfte Druckfunktion zu haben. – bltxd

+0

Hm ... das wirft mehr Fragen auf. Aus einer funktionalen Perspektive würde ich erwarten, dass, wenn f (x) = y, g (f (x)) = g (y). Warum funktioniert das nicht mit printf? Hat das etwas damit zu tun, dass printf von sich aus Nebenwirkungen haben muss? Ist diese Art von "Magie" üblich? – dimatura

Verwandte Themen