2010-03-05 8 views
9

Soy un novato OCaml. Estoy jugando con fragmentos de tipo "hello world" y me encontré con esta situación. Aquí es una sesión con el intérprete con algunos comentarios adicionales:¿Por qué no funciona este pequeño fragmento de OCaml con Printf.printf?

# 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 =() 

Así que aquí es la situación. string_of_float (average 1. 4.) devuelve una cadena, como "hello!" hace. Cuando doy "hello!" en Printf.printf, funciona como se esperaba. Cuando doy string_of_float (average 1. 4.) a Printf.printf, falla y me dice que esperaba una cadena, pero ese otro tipo extraño. ¿Pero por qué funcionan entonces "hello!" y "2.5"?

¿Qué está pasando?

Respuesta

15

Existe una especie de "sobrecarga" del significado de los literales de cadena en OCaml. En tiempo de compilación, pueden interpretarse como una cadena o como un formato (que son cosas completamente diferentes en el sistema de tipos), dependiendo de lo que piense el verificador de tipos. Si decide que debe ser un formato, entonces la cadena de formato se analiza directamente en tiempo de compilación (es por eso que es capaz de escribir-verificar los argumentos a printf en tiempo de compilación). (A diferencia de C, que analiza la cadena en tiempo de ejecución). Sin embargo, no hay una forma sencilla de convertir de una cadena a un formato en tiempo de ejecución. Entonces, cuando ve Printf.printf "2.5", el "2.5" no es realmente una cadena, sino un tipo de formato especial que fue analizado en tiempo de compilación. Es por eso que no puedes sustituirlo por una cuerda.

En una nota no relacionada, si solo desea imprimir una cadena, es posible que desee utilizar print_string (o print_endline si desea una línea nueva).

+0

Eso tiene sentido para mí. ¡Gracias! – dimatura

+1

No ser capaz de construir un 'format6' en tiempo de ejecución es realmente ... deficiente. ¿Hay alguna manera (a parte de reescribirlo yo mismo) para lograr ese mismo objetivo? (Por ejemplo, ¿cómo rellenas una cadena con un número arbitrario de espacios en tiempo de ejecución? En C podemos 'sprintf (sprintf (" %%% ds ", ancho), str)'. – kizzx2

+0

@ kizzx2: para esta tarea en particular , hay una solución alternativa: la sintaxis de la cadena de formato permite que el ancho o la precisión sean reemplazados por un asterisco, en cuyo caso lo tomará de un argumento adicional que precede al argumento principal: 'Printf.sprintf"% * s "width str'; esta sintaxis también está disponible en C99, Perl, Python, Ruby, Go, etc. Pero, por supuesto, este es solo un caso particular, para otras cosas puede que no tenga suerte. Hay un operador '^^' en OCaml para anexar formatos, pero aún necesita formatos para agregar, en primer lugar, así que no sé qué tan útil es. – newacct

3
Printf.printf "%s" anyStringExpr 

funcionará. El primer argumento para printf es algo mágico. (Otros completarán los detalles.)

+0

Sí, el primer argumento es una cadena de formato que contiene directivas, y cuando se aplica devuelve una función con ese número de directivas como parámetros. Definitivamente mágico. – nlucaroni

+1

Tipo de. De todos modos, es realmente genial tener una instalación de impresión completamente tipeada. – bltxd

+0

Hm ... esto genera más preguntas. Desde una perspectiva funcional, esperaría que si f (x) = y, g (f (x)) = g (y). ¿Por qué esto no funciona con printf? ¿Tiene algo que ver con el hecho de que printf tendrá inherentemente efectos secundarios? ¿Es este tipo de 'magia' común? – dimatura

Cuestiones relacionadas