OCaml/Pierwszy program
Z Wikibooks, biblioteki wolnych podręczników.
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.