[PL] OCaml Basic2
👑 OCaml Basic 2
💡 Module 이란?
OCaml에서의 module : 자료들의 집합 (= 객체화가 불가능한 클래스)
OCaml은 Module system을 지원한다. 프로그램은 여러개의 모듈로 구성되어 있으며, 각각의 모듈은 자료(변수)와 행동(함수)으로 구성된다.
모듈은 그 자체로 타입은 아니며, 객체화가 불가능하여 모듈의 함수를 호출하거나 자료를 읽는 행위만 가능하다.
모든 소스파일은 그 자체로 모듈이다.
모듈에 접근할 때, 모듈 이름의 첫 글자를 대문자로 해야한다.
- e.g. module1.ml 의 num에 접근 :
Module1.num
(* module1.ml *)
let num = 10
let mul x y = x * y
(* module2.ml *)
let _ = Format.printf "Result : %d\n" Module1.num
let _ = Format.printf "Result : %d\n" (Module1.mul 2 4)
-
모듈 내에 모듈을 정의할 수 있다. (
Nested module)-
C++ or Java의
inner class와 유사하다. -
module [module_name] = struct [defs] end를 통해 생성 가능하다.- ❗
module_name은 반드시 대문자로 시작해야 한다.
- ❗
(* module1.ml *) module IntAdd = struct let add x y = x + y end module FloatAdd = struct let add x y = x + y end (* module2.ml *) let _ = Format.printf "Result : %d\n" (Module1.IntAdd.add 2 4) let _ = Format.printf "Result : %d\n" (Module2.FloatAdd.add 2.0 4.0) -
-
모듈을
open하여 모듈 접근 시에 모듈 이름을 생략할 수 있다.-
open [module_name]: 현재scope내에서 모듈 내 변수를 모듈 이름 없이 접근 가능 -
C++ 의
namespace와 유사하다. -
다른 여러 모듈들에 같은 이름이 있을 경우
comflict가 발생 가능함에 유의해야 한다.
(* module2.ml *) open Module1 let _ = Format.printf "Result : %d\n" (IntAdd.add 2 4) let _ = Format.printf "Result : %d\n" (FloatAdd.add 2.0 4.0)- 특정
scope내에서만 모듈을 개방할 수도 있다.
let _ = let open Module1.IntAdd in let _ = Format.printf "Result : %d\n" (add 2 4) in let _ = Format.printf "Result : %d\n" (add 1 2) in Format.printf "Result : %d\n" (add 4 5) -
-
모듈 이름이 긴 경우,
renaming이 가능하다.module [abbreviation] = [module_name]
(* module2.ml *) module M1I = Module1.IntAdd module M1F = Module1.FloatAdd module F = Format begin F.printf "Result : %d\n" (M1I,add 3 7); F.printf "Result : %d\n" (M1F.add 2.0 3.0) end
💡 Pattern Matching
OCaml은 C++, Java 의 switch-case와 유사한 match-with expression을 제공한다.
값의 “형태”에 따라 다르게 처리해야 하는 경우에 유용하게 사용된다.
-
매칭되는 경우에 해당 expression을 계산하고, 그 결과가 전체의 결과이다.
-
pattern은constant와variable로 구성된다.-
상수인 경우 : 계산 값이 패턴과 일치하는 지를 매칭
-
변수인 경우 : 계산 값을 변수에
binding(항상 매칭에 성공)- 즉, 변수가 앞 패턴에 등장 -> 이후의 패턴은 매칭되지 않는다.
-
match expression with (* 1. expression 계산 *)
| pattern1 -> expression1 (* 2. 계산 결과를 pattern1과 매칭 *)
| pattern2 -> expression2 (* 3. 매칭 실패 시 pattern2와 매칭 *)
.
.
.
| patternN -> expressionN (* ... *)
let _ =
let check_value x =
match x with
| 0 -> 0
| y -> y
| 1 -> 1
in
Format.printf "Result : %d\n" (check_value 2)
-> warning 발생 [redundant-case]: this match case is unused.
-
pattern에 wildcard
_사용 가능-
항상 매칭에 성공한다.
-
값이
binding되지 X -> 매칭된 값 사용 안함 -
만약 아래의 코드에서
wildcard패턴이 빠진다면 -> warningi mod 2의 결과는 항상 0 or 1 이지만, 코드에 포함되어야 한다!
-
OCaml 컴파일러는
expression의 타입을 기반으로 패턴매칭의 완전성을 검사한다.- 예를 들어, expression이 int 타입인 경우 -> 모든 int 값을 커버할 수 있어야 함.
-
let _ =
let even_or_odd i =
match i mod 2 with
| 0 -> Format.printf "Even\n"
| 1 -> Format.printf "Odd\n"
| _ -> Format.printf "Unknown\n"
in
let _ = even_or_odd 0 in (* Even *)
let _ = even_or_odd 1 in (* Odd *)
even_or_odd 3 (* Odd *)
..기호를 사용하여 연속된 문자 혹은 숫자에 대한 패턴 생성 가능
let _ =
let check_lower c =
match c with
| 'a' .. 'z' -> true (* 'a' 부터 'z' 까지 *)
| _ -> false
in
let _ = Format.printf "c : %b\n" (check_lower 'c') in (* true *)
Format.printf "C : %b\n" (check_lower 'C') (* false *)
-
match-with expression도 이 자체로expression이기에 하나의 타입만을 가져야 한다.- 각 match case의 expression이 모두 동일한 타입을 가져야만 한다.
let _ =
let check_lower c =
match c with
| 'a' .. 'z' -> true (* 'a' 부터 'z' 까지 *)
| _ -> 0
in
... (* 각각의 expression이 bool, int 타입으로 서로 다르기 때문에 에러 발생 *)
tuple등의 자료구조에 대해서도 패턴 매칭이 가능하다.
let _ =
let get_first t =
match t with
| (first, _) -> first
in
let get_second t =
match t with
| (_, second) -> second
in
let _ = Format.printf "first : %d\n" (get_first (2, 5)) in (* 2 *)
Format.printf "second : %d\n" (get_second (3, 4)) (* 4 *)
💡 List
서로 다른 타입의 원소를 가질 수 있는 튜플과는 다르게,
리스트의 모든 원소는 동일한 타입을 가져야만 한다.
- OCaml 에서 리스트의 생성은
[]를 사용하고, 원소끼리의 구분은;을 이용한다.
[1; 2; 3; 4], ['a'; 'b'; 'c'], ["Hello"; "World"]
-
리스트 연산자
::연산자 : 리스트앞에 원소를 삽입하여새로운 리스트를반환
let lst = 0 :: [1; 2; 3] (* [0; 1; 2; 3] *)@연산자 :두 개의 리스트를 연결하여새로운 리스트를반환
let lst = [1; 2] @ [3; 4] (* [1; 2; 3; 4] *)
-
-
OCaml은 List에 대한 내장 라이브러리를 제공한다.
-
주요 함수
'a는 임의의 타입을 가리킴-
List.iter : ('a -> unit) -> 'a list -> unit🔹
함수와리스트를 인자로 받아, 각 리스트의 원소에 함수를 apply
🔹 반환값이unit= 반환값 없음
let print_square x = Format.printf "%d " (x * x) let lst = [1; 2; 3; 4] in List.iter print_square lst (* 출력 : 1 4 9 16 *)-
List.map : ('a -> 'b) -> 'a list -> 'b list🔹
함수와리스트를 인자로 받아, 리스트의 각 원소에 함수를 apply한 새로운리스트를 반환하는 함수
let _ = let lst = [1; 2; 3] in let new_lst = List.map (fun x -> x + 1) lst ... (* new_lst = [2; 3; 4] *)-
List.fold_left : ('acc -> 'a -> 'acc) -> 'acc -> 'a list -> 'acc🔹
함수,초기값,리스트를 인자로 받아, 리스트의 첫 원소부터 함수를 적용하여 누적 값을 갱신하고최종 누적 값을 반환하는 함수
let sum acc x = acc + x let lst = [1; 2; 3; 4] in let result = List.fold_left sum 0 lst in Format.printf "Sum : %d\n" result (* Sum : 10 *) (* - 첫번째 원소 1에 대해 0 (acc) + 1 -> acc = 1 *) (* - 두번째 원소 2에 대해 1 (acc) + 2 -> acc = 3 *) (* - 세번째 원소 3에 대해 3 (acc) + 3 -> acc = 6 *) (* - 네번째 원소 4에 대해 6 (acc) + 4 -> acc = 10 *) -
-
- List와 Pattern matching
let rec len lst =
match lst with
| [] -> 0 (* list가 비어있을 경우 *)
| _ :: t -> (* list가 값을 가질 경우 *)
1 + (len t)
(* -------------------- *)
let lst = [1; 2; 3; 4]
match lst with
| [] -> print_endline "Empty list"
| [x] -> print_endline "Single element list"
| x :: y :: rest -> print_endline "Two or More element list"
(*
[] 패턴 : 리스트가 비어 있는 경우
[x] 패턴 : 리스트에 하나의 요소만 있는 경우
x :: y :: rest 패턴 : 리스트에 적어도 두 개의 요소가 있는 경우
'x'는 첫 번째 요소, 'y'는 두 번째 요소, rest는 리스트의 나머지 요소들
위의 lst에서 x = 1, y = 2, rest = [3; 4]
*)
💡 Type Definition
OCaml 에서는 사용자가 새로운 타입을 정의할 수 있다.
type [type_name] = [type] 의 형태로 사용하며,
C/C++의 typedef와 유사하다.
일반적으로 OCaml은 강력한 타입 추론 기능을 가지고 있어, 타입을 명시적으로
지정해줄 필요가 없는 경우가 많지만, 새로운 타입을 정의한 경우, 코드의 가독성을
높이고, 의도를 명확하게 전달하기 위해 명시적으로 타입을 지정해주는 것이 좋다.
type INT = int
let add_two_integers (x : INT) (y : INT) : INT = x + y
let x : INT = 10
let y : INT = 20
let sum : INT = add_two_integers x y
Format.printf "sum : %d\n" sum
-
Disjoint union
-
OCaml에서
Disjoint union이란 여러 개의 다른 타입을 하나의 타입으로 묶는 데사용되는 개념이다. 이를 통해 하나의 값이 여러 다른 형식을 가질 수 있으며,
Variant records라고도 부른다. -
type키워드와|를 사용하여 정의한다. -
아래 코드에서
Circle,Rectangle과 같은 식별자는대문자로 시작해야하며,Constructor라고 불린다.
type shape = | Circle of float | Rectangle of float * float let cal_area = function | Circle r -> 3.14 *. r *. r | Rectangle (w, h) -> w *. h let circle_area = calculate_area (Circle 2.0) let rectangle_area = calculate_area (Rectangle (3.0, 4.0)) -
Leave a comment