Hello everyone. This is (something like) my first OCaml program. I look forward to your comments towards improving style.
(* Ask user for coordinates of two points.
* Echo the inputs.
* Print the Euclidean distance between the two points. *)
let askf prompt =
print_string (prompt ^ ": ");
read_float ()
let showf label value =
print_string (label ^ " = " ^ string_of_float value ^ "\n")
type point = { name : string; x : float; y : float; z : float }
let ask_point name =
print_endline ("For the " ^ name);
let x = askf "X" in
let y = askf "Y" in
let z = askf "Z" in
{ name = name; x = x; y = y; z = z }
let show_point p =
print_endline (p.name);
showf "X" p.x;
showf "Y" p.y;
showf "Z" p.z;
()
let distance p1 p2 =
let square x = x *. x in
sqrt ((square (p1.x -. p2.x)) +.
(square (p1.y -. p2.y)) +.
(square (p1.z -. p2.z)))
let main =
let p1 = ask_point "Point 1" in
let p2 = ask_point "Point 2" in
print_newline ();
show_point p1;
show_point p2;
print_newline ();
showf ("Distance between the points") (distance p1 p2);
exit 0
let () = main
I think the solution is fine for the problem at hand. If you want to think ahead, you might want to define point differently. Because if you create intermediate points you now need to name each, which is inconvenient. So I would decouple names from the type representing points.
OCaml is eagerly evaluated, so when you do something like:
let main = print "Hi"
let () = main
The Hi is printed as soon as the first line is evaluated, therefore the second line is redundant. To delay the evaluation of the first line, you need to make it a function:
let main () = print "Hi"
let () = main ()
One more thing, in this definition,
let distance p1 p2 =
let square x = x *. x in
...
Technically thereâs nothing wrong with it, it will work as intended. Itâs just slightly inefficient because in theory, it recreates a new square function every time distance is called. Now practically there may be nothing wrong with thisâdifferent compiler versions might just optimize away the definition. But you donât need to rely on thatâyou can lift out the square function yourself:
let square x = x *. x
let distance p1 p2 =
...
Now, square is defined once, in the same module, no matter how many times distance is called.
Iâm curious, does the vanilla or flamda compiler always remove these kind of redundant closures? I know that the compiler doesnât do too many optimisations, but this seems like an obvious one that wouldnât change the runtime behaviour of a function in too much of an unexpected way. If not, I guess I have quite a few functions to rewrite I guess. I like to nest functions for the purpose of code clarity and scoping - but explicitly shifting the function parameters to the end of the definition I find hurts readability.
Incidentally, I noticed in one of the replies that when quoting code here, if I use Markdownâs ~~~~ instead of <pre></pre> tags, I get the syntax highlighting.
I wasnât aware of the ~ syntax, I usually use the triple-backtick âfenced code blockâ to format code with syntax highlighting: Extended Syntax | Markdown Guide
⌠I guess there is no better way of grabbing three consecutive inputs than with repeated let expressions?
There is Scanf, which works similarly to Câs scanf function and allows formatted data entry from the input. E.g.:
(* test.ml *)
type point = { x : float; y : float; z : float }
let ask_point name =
Printf.printf "Input point %s x,y,z: %!" name;
Scanf.scanf "%f,%f,%f" begin fun x y z -> { x; y; z } end
let () =
let { x; y; z } = ask_point "origin" in
Printf.printf "x=%f,y=%f,z=%f\n%!" x y z
You can run it like so:
$ ocaml test.ml
Input point origin x,y,z: 1,2,3
x=1.000000,y=2.000000,z=3.000000
Oh, thatâs nice! I thought scanf would require a ref, like in C, or â I think I saw something about a function argument but I didnât get it. Now I see how it works. Cool!
Destructuring a list directly is not recommended as it doesnât handle lists of other lengths. The compiler warns about that and dune turns warnings into errors.