OCaml/Pierwszy program

Tym razem program będzie na tyle spory, że proponuję umieścić go w pliku main.ml (lub o innej nazwie), który będziemy kompilować komendą ocamlc -o main main.ml i uruchamiać już klasycznie: ./main

Do większych projektów zbudowanych z paru modułów wykorzystuje się zautomatyzowane mechanizmy budowania. OCaml posiada nakładki na GNU Make, które bardzo ułatwiają z nim pracę.

(* Bez tych linijek musielibyśmy pisać  *
 * Printf.printf i Scanf.scanf		*)
open Printf
open Scanf

(* Typ opisujący rozwiązanie równania;
 * Zwracany przez funkcję *)
type solution = 
	| Zero			(* Brak rozwiązań*)
	| One of float		(* Jedno *)
	| Two of (float * float)(* Dwa *)

(* Funkcja znajdująca pierwiastki
 * równania kwadratowego *)
let solve a b c =
	let delta =
		b *. b
		-. 4.0 *. a *. c in
	
	if (delta < 0.0) then
		Zero
	else 
		let d = sqrt(delta) in
		let denom = 2.0 *. a in
		if (d = 0.0) then
			One (-.b /. denom)
		else	Two ((-.b +. d) /. denom,
			 (-.b -. d) /. denom)

let get_data = 
	(* Funkcja wczytująca dla scanf *) 
	let read a b c = (a, b, c) in

	printf "Proszę podać współczynniki równania:\n";
	flush stdout;
	scanf "%f %f %f" read (* Zwraca krotkę *)

(* Wczytaj dane *)
let a,b,c = get_data;;

match (solve a b c) with
| Zero -> printf "Brak rozwiązań\n";
| One x -> printf "Jedno rozwiązanie: %.2f" x
| Two (x0, x1) -> 
	printf "Dwa rozwiązania: %.2f i %.2f\n" x0 x1

Jak już wspomniałem bardzo wiele funkcji buduje się poprzez wielokrotne wywoływanie let ... in, a następnie kwitowanie tego zwróceniem tak utworzonego wyrażenia (czyli napisaniem jego nazwy na końcu). Takie budowane funkcji również pomaga kompilatorowi w znajdywaniu i optymalizowaniu wspólnych wyrażeń.

W podobny sposób zbudowana jest funkcja solve, na początku obliczamy jakieś wstępne dane i nadajemy im etykiety. W późniejszych obliczeniach wykorzystujemy poprzednie etykiety by ostatecznie zwrócić typ zawierający wynik naszych obliczeń.

Wykorzystanie funkcji printf nie powinno sprawić problemów programistom C. Ze względu na brak zmiennych jako takich w języku trochę inaczej wygląda wywołanie funkcji scanf. Oczekuje ona jako argumentu funkcji, która pobierze odczytane przez scanf dane i zrobi z nimi to, czego życzy sobie użytkownik. Wygodne do zastosowania są tutaj funkcje anonimowe.

Prócz funkcji printf/scanf w module Pervasives dostępne są funkcje niższego poziomu; część z nich używaliśmy już wcześniej:

  1. val print_char : char -> unit
  2. val print_string : string -> unit
  3. val print_int : int -> unit
  4. val print_float : float -> unit
  5. val print_endline : string -> unit
  6. val print_newline : unit -> unit

Oraz funkcje do wczytywania danych:

  1. val read_line : unit -> string
  2. val read_int : unit -> int
  3. val read_float : unit -> float

Bardzo dobrze sprawują się jeśli chcemy wyświetlić coś, czego nie musimy dodatkowo formatować. Są prawdopodobnie szybsze od funkcji printf, choć moim zdaniem mają nieporęcznie długie nazwy.