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:
- val print_char : char -> unit
- val print_string : string -> unit
- val print_int : int -> unit
- val print_float : float -> unit
- val print_endline : string -> unit
- val print_newline : unit -> unit
Oraz funkcje do wczytywania danych:
- val read_line : unit -> string
- val read_int : unit -> int
- 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.