Real World OCaml

Part II 才开了个头,还有好多没有整理,如:文件、模块、仿函数、对象、类。Part III 原计划整理编译器前端、S-表达式、并发、GC等的。但短期内不会接着写了,原因主要是工作由 OCaml 迁至 LLVM/Clang了,而且感觉 OCaml 的应用领域还是比较窄,以后针对具体应用再写吧。

ISBN 978-7-5123-7637-3

English First Edition PDF

Code Examples

Install Guidence from cornell/cs3110

Part I

based on Core instead of standard library

1 / 3;;
(* - : int = 0 *)

1 / 3.;;
1. /. 3;; 
2. / 3.;;
(* Error: This expression has type float but an expression was expected of type int *)

1. /. 3.;;
(* - : float = 0.333333333333333315 *)

let even x =
  x mod 2 = 0 ;;
(* val even : int -> bool = <fun> *)

Identifiers for variable names:
punctuation is excluded, except for _ and ', must start with a lowercase letter or an underscore.
By default, utop doesn’t print out variables starting with an underscore.

first-class functions

Module names
always start with a capital letter.

type inference

let sum_if_true test first second =
  (if test first then first else 0)
  + (if test second then second else 0)
;;
(* val sum_if_true : (int -> bool) -> int -> int -> int = <fun> *)

let sum_if_true (test : int -> bool) (x : int) (y : int) : int =
  (if test x then x else 0)
  + (if test y then y else 0)
;;

parametric polymorphism

generic type & type variable

let first_if_true test x y =
  if test x then x else y
;;
(* val first_if_true : ('a -> bool) -> 'a -> 'a -> 'a = <fun> *)

* compile time error & runtime exception

built-in simple types:
int, float, bool, char, string, unit

pattern matching

let t = (3, "four", 5.);;
(* val t : int * string * float = (3, "four", 5.) *)
let (x, y, z) = t;;

* tuple: fixed item number, different types

* list: any item number, the same type

~f: labeled argument

let langs = ["ocaml", "cpp", "c"];;
(* a singleton list containing a three-tuple
 * val langs : (string * string * string) list = [("ocaml", "cpp", "c")]
 *)

let langs = ["ocaml"; "cpp"; "c"];;
(* val langs : string list = ["ocaml"; "cpp"; "c"] *)
List.map langs ~f:String.length;;
(* - : int list = [5; 3; 1] *)

"python" :: langs;;
(* - : string list = ["python"; "ocaml"; "cpp"; "c"] *)
langs;;
(* - : string list = ["ocaml"; "cpp"; "c"] *)

["py"; "perl"] @ langs;;
(* - : string list = ["py"; "perl"; "ocaml"; "cpp"; "c"] *)

The bracket notation for lists is really just syntactic sugar for :: (right-associative).

Unlike ::, @ is not a constant-time operation (proportional to the length of the first list).

let my_fav_lang (my_fav :: the_rest) = my_fav;;
(* Here is an example of a case that is not matched: [] *)

let my_fav_lang langs =
  match langs with
  | first :: the_rest -> first
  | [] -> "ocaml"
;;

recursive

let rec sum l =
  match l with
  | [] -> 0
  | hd :: tl -> hd + sum tl
;;

option

let divide x y =
  if y = 0 then None else Some (x/y) ;;
(* val divide : int -> int -> int option = <fun> *)

A let paired with an in can be used to introduce a new binding within any local scope, including a function body.
The in marks the beginning of the scope within which the new variable can be used.

let x = 7 in
  let y = x * x in
    x + y
;;

record

type point2d = {x : float; y : float};;
let p = {x = 3.; y = -4.};;
(* val p : point2d = {x = 3.; y = -4.} *)

variant

type v = 
  | Int of int
  | Point2D of point2d
;;
(* type v = Int of int | Point2D of point2d *)

array

let numbers = [| 1; 2; 3; 4 |];;
(* val numbers : int array = [|1; 2; 3; 4|] *)

numbers.(2) <- 4;;
(* - : unit = () *)

The unit type has only one possible value, written (), generally used as a placeholder.
We use unit for the return value of an operation like setting a mutable field that communicates by side effect rather than by returning a value. It’s also used as the argument to functions that don’t require an input value (like void in C or Java).

mutable record

type running_sum =
{
  mutable sum : float;
  mutable samples : int;
};;

let mean rsum = rsum.sum /. float rsum.samples
let create () = {sum = 0.; samples = 0}
let update rsum x =
  rsum.samples <- rsum.samples + 1;
  rsum.sum <- rsum.sum +. x
;;
(* val mean : running_sum -> float = <fun>
 * val create : unit -> running_sum = <fun>
 * val update : running_sum -> float -> unit = <fun>
 *)

let rsum = create ();;
List.iter [1.;3.;2.;-7.;4.;5.] ~f:(fun x -> update rsum x);;
mean rsum;;

Anonymous functions
are declared using the fun keyword, and don’t need to be ex‐ plicitly named.

ref:
the standard way of simulating the traditional mutable variables

let x = {contents = 0};;
x.contents <- x.contents + 1;;
(* equivalent to *)
let x = ref 0;;
x := !x + 1;;
(* implementations *)
type 'a ref = {mutable contents : 'a}

let ref x = {contents = x}
let (!) r = r.contents
let (:=) r x = r.contents + x
;;

The parentheses around ! and := are needed because these are operators, rather than ordinary functions.

let sum list =
  let sum = ref 0 in
    List.iter list ~f:(fun x -> sum := !sum + x);
  !sum
;;

for & while

out of toplevel

(* sum.ml *)
open Core

let rec read_and_accumulate accum =
  let line = In_channel.input_line In_channel.stdin in
  match line with
  | None -> accum
  | Some x -> read_and_accumulate (accum +. Float.of_string x)

let () =
  printf "Total: %F\n" (read_and_accumulate 0.)
corebuild sum.ml

Part II

anonymous & higer-order function

let increments = [ (fun x -> x + 1); (fun x -> x + 2) ];;
(* val increments : (int -> int) list = [<fun>; <fun>] *)

List.map ~f:(fun g -> g 5) increments;;
(* - : int list = [6; 7] *)

currying

let abs_diff x y = abs (x - y);;
(* equivalent to *)
let abs_diff =
  (fun x -> (fun y -> abs (x - y)));;

(* partial application *)
let dist_from_3 = abs_diff 3;;

or pattern

| [] | [_]

Explicitly marking of recursion doesn’t apply to a pure language like Haskell.

Predetermined identifiers characters set and strings:
! $ % & * + - . / : < = > ? @ ^ | ~
or mod lsl (logical shift left)

(+) 1 2;;
(* - : int = 3 *)

Int.max 3 -4;; (* error *)
Int.max 3 (-4);;

let (|>) x f = f x;;
(* val ( |> ) : 'a -> ('a -> 'b) -> 'b = <fun> *)

Negation has lower precedence than function application.

function keyword

let some_or_zero function
  | Some x -> x
  | None -> 0
;;

labeled argument

let ratio ~num ~denom = float num /. float denom;;
(* val ratio : num:int -> denom:int -> float = <fun> *)

ratio ~denom:10 ~num:3;;

Label punning:
you get to drop the text after the : if the name of the label and the name of the variable being used are the same.

Part III

parsing

parser generators:
ocamllex, ocamlyacc, menhir

猜你喜欢

转载自www.cnblogs.com/humz/p/10679849.html