[one; 2; 3]
[]
means an empty list.[2; 3]
[2; 3]
.head :: tail
. The lines below are completely equivalent to each other.[one; 2; 3] 12; 3] 1 :: 2 :: [3] 1 :: 2 :: 3 :: []
int list
; The common type for a connected list of objects of type foo
will be foo list
.'a list
), which are very useful when writing generic functions that work with a "list of something." (Note that 'a list
does not mean that the elements of the list can be of different types, you still cannot, for example, make a list consisting of a mixture of integers and strings. This form of record means "a list of elements of any type, but one the same type ").length
defined in the List
module. It doesn't matter if the list contains integers, or strings, or objects, or small furry animals, the List.length
function can be used for any type of list. Thus the type List.lenght
:List.length: 'a list -> int
struct
(abbreviation for structure). Java has classes that can be used in a similar way, although using them requires more laborious work.struct pair_of_ints { int a, b; };
(3,4)
, whose type is int * int
. Unlike lists, tuples can contain elements of different types, so, for example, (3, "helo", 'x') is of type int * string * char
.type pair_of_ints = {a: int; b: int} ;;
{a = 3; b = 5}
# type pair_of_ints = {a: int; b: int} ;; type pair_of_ints = {a: int; b: int; } # {a = 3; b = 5} ;; -: pair_of_ints = {a = 3; b = 5} # {a = 3} ;; Some record field labels are undefined: b
struct foo { int type; #define TYPE_INT 1 #define TYPE_PAIR_OF_INTS 2 #define TYPE_STRING 3 union { int i; // If type == TYPE_INT. int pair [2]; // If type == TYPE_PAIR_OF_INTS. char * str; // If type == TYPE_STRING. } u; };
ui
at the moment when the structure contains a string. And the compiler does not help here to verify that all possible values ​​have been checked inside the switch statement (this problem is partially solved using the enum
). The programmer may forget to set the value of the type
field, which will lead to all sorts of fun and games with beetles. Yes, and all this is cumbersome.type foo = Nothing | Int of int | Pair of int * int | String of string ;;
of part
, where type
always begins with a small letter. In the example above, Nothing
is a constant, and all other constructors are used with values.Nothing Int 3 Pair (4, 5) String hello & c.
foo
.of
used in the type definition, but NOT in the creation of elements of the specified type.enum sign {positive, zero, negative};
type sign = Positive | Zero | Negative ;;
type binary_tree = Leaf of int | Tree of binary_tree * binary_tree ;;
Leaf 3 Tree (Leaf 3, Leaf 4) Tree (Tree (Leaf 3, Leaf 4), Leaf 5) Tree (Tree (Leaf 3, Leaf 4), Tree (Tree (Leaf 3, Leaf 4), Leaf 5))
type 'a binary_tree = Leaf of' a | Tree of 'a binary_tree *' a binary_tree ;;
int binary_tree
. Similarly, the exact type that stores strings in each sheet is called string binary_tree
. In the following example, we will define the types of some instances in the top level and let the type inference system show their types for us:# Leaf "hello" ;; -: string binary_tree = Leaf "hello" # Leaf 3.0 ;; -: float binary_tree = Leaf 3.
int list
.a' list
also written in the same "in reverse order". Lists are just parameterized variants with the following (somewhat strange) definition:type 'a list = [] | :: of 'a *' a list (* this is not real OCaml code *)
# type 'a list = Nil | :: of 'a *' a list ;; type 'a list = Nil | :: of 'a *' a list # Nil ;; -: 'a list = Nil # 1 :: Nil ;; -: int list = :: (1, Nil) # 1 :: 2 :: Nil ;; -: int list = :: (1, :: (2, Nil))
[1; 2; 3]
[1; 2; 3]
[1; 2; 3]
, or more formally, as 1 :: 2 :: 3 :: []
. If we look at the definition of a' list
above, we will see an explanation of the formal record.OCaml name | Example Type Definition | Use example |
---|---|---|
list | int list | [one; 2; 3] |
tuple | int * string | (3, "hello") |
record | type pair = {a: int; b: string} | {a = 3; b = "hello"} |
variant | type foo = int of int | Pair of int * string | Int 3 |
variant | type sign = Positive | Zero | Negative | Positive Zero |
parameterized variant | type 'a my_list = Empty | Cons of 'a *' a my_list | Cons (1, Cons (2, Empty)) |
type expr = Plus of expr * expr (* means a + b *) | Minus of expr * expr (* means a - b *) | Times of expr * expr (* means a * b *) | Divide of expr * expr (* means a / b *) | Value of string (* "x", "y", "n", etc. *) ;;
Times (Value "n", Plus (Value "x", Value "y"))
(Value "n", Plus (Value "x", Value "y"))
as something more similar to n * (x+y)
. To do this, we will write two functions, one of which converts the expression into a beautiful string and one that outputs it (the reason for the separation is that I may need to write the same string file and I do not want to repeat the whole function for this).let rec to_string e = match e with Plus (left, right) -> "(" ^ (to_string left) ^ "+" ^ (to_string right) ^ ")" | Minus (left, right) -> "(" ^ (to_string left) ^ "-" ^ (to_string right) ^ ")" | Times (left, right) -> "(" ^ (to_string left) ^ "*" ^ (to_string right) ^ ")" | Divide (left, right) -> "(" ^ (to_string left) ^ "/" ^ (to_string right) ^ ")" | Value v -> v ;; let print_expr e = print_endline (to_string e) ;;
^
operator is used to concatenate strings)# print_expr (Times (Value "n", Plus (Value "x", Value "y"))) ;; (n * (x + y))
match object with pattern -> result | pattern -> result ...
to_string
in the example above), or complex, with attachments. The next example is our function to multiply expressions in the form of n * (x + y)
or in the form of (x + y) * n
. For this we will use nested samples:let rec multiply_out e = match e with Times (e1, Plus (e2, e3)) -> Plus (Times (multiply_out e1, multiply_out e2), Times (multiply_out e1, multiply_out e3)) | Times (Plus (e1, e2), e3) -> Plus (Times (multiply_out e1, multiply_out e3), Times (multiply_out e2, multiply_out e3)) | Plus (left, right) -> Plus (multiply_out left, multiply_out right) | Minus (left, right) -> Minus (multiply_out left, multiply_out right) | Times (left, right) -> Times (multiply_out left, multiply_out right) | Divide (left, right) -> Divide (multiply_out left, multiply_out right) | Value v -> Value v ;;
# print_expr (multiply_out (Times (Value "n", Plus (Value "x", Value "y")))) ;; ((n * x) + (n * y))
multiply_out
work? The key point is the first two samples. The first sample is Time (e1, Plus (e2, e3))
, which matches expressions of the form e1 * (e2 + e3)
. Now take a look at the right side of the first comparison with the sample and make sure that it is equivalent to (e1 * e2) + (e1 * e3)
.(e1 + e2) * e3
.multiply_out
on subexpressions. This provides the multiplication of all subexpressions (if you want to multiply only the top level of the expression, then you should replace all the remaining patterns with the simple rule e -> e
).let factorize e = match e with Plus (Times (e1, e2), Times (e3, e4)) when e1 = e3 -> Times (e1, Plus (e2, e4)) | Plus (Times (e1, e2), Times (e3, e4)) when e2 = e4 -> Times (Plus (e1, e3), e4) | e -> e ;;
# factorize (Plus (Times (Value "n", Value "x"), Times (Value "n", Value "y"))) ;; -: expr = Times (Value "n", Plus (Value "x", Value "y"))
when
satisfied.match object with pattern [when condition] -> result pattern [when condition] -> result ...
=
, which checks the “structural correspondence” between expressions. This means that it passes recursively into each expression and checks them to match at all levels.expr
adding the Product
option to it:type expr = Plus of expr * expr (* means a + b *) | Minus of expr * expr (* means a - b *) | Times of expr * expr (* means a * b *) | Divide of expr * expr (* means a / b *) | Product of expr list (* means a * b * c * ... *) | Value of string (* "x", "y", "n", etc. *) ;;
Warning: this pattern-matching is not exhaustive. This is not matched: Product _
Source: https://habr.com/ru/post/108920/
All Articles