13. FPL (2)
Common Lisp
- Common Lisp๋ 1980๋ ๋ ์ด๋ฐ ์ฌ๋ฌ Lisp ๋ฐฉ์ธ(dialect)์ ํน์ง์ ํตํฉํ์ฌ ๋ง๋ค์ด์ง ๋ํ ์ธ์ด์
- Scheme์ด ์๊ณ ๊ฐ๊ฒฐํ ์ฒ ํ์ ์ง๋ ์ธ์ด์ธ ๊ฒ๊ณผ ๋ฌ๋ฆฌ, Common Lisp์ ํ๋ถํ ๊ธฐ๋ฅ๊ณผ ๋ณต์ก์ฑ์ ๊ฐ์ง ์ธ์ด
- Features include:
- Records
- Arrays
- Complex numbers
- Character strings
- Powerful I/O capabilities
- Iterative control statements
DEFSTRUCT๋ฅผ ์ฌ์ฉํ RECORDS
- DEFSTRUCT๋ ๊ตฌ์กฐ์ฒด๋ฅผ ๋ง๋๋ ๋ช ๋ น์ด
(DEFSTRUCT PERSON
NAME
AGE)
(SETF P1 (MAKE-PERSON :NAME "ALICE" :AGE 30))
(PERSON-NAME P1) ; โ "ALICE"
(PERSON-AGE P1) ; โ 30
MAKE-PERSON์:NAME๊ณผ:AGE๋ฅผ ๋ฐ์์ ๊ตฌ์กฐ์ฒด ๊ฐ์ฒด๋ฅผ ์์ฑ, ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ณ์ P1์ ํ ๋นํจ- P1 ๊ตฌ์กฐ์ฒด์์ NAME ์ฌ๋กฏ์ ๊บผ๋. ๊ฒฐ๊ณผ๋ "ALICE"๊ฐ ๋จ
MAKE-ARRAY๋ฅผ ์ฌ์ฉํ ๋ฐฐ์ด
(SETF A (MAKE-ARRAY '(2 3) :INITIAL-CONTENTS '((1 2 3) (4 5 6))))
(AREF A 0 1) ; โ 2
- 2x3์ง๋ฆฌ 2์ฐจ์ ๋ฐฐ์ด A๋ฅผ ๋ง๋ค๊ณ , ์ด๊ธฐ๊ฐ์ผ๋ก
((1 2 3) (4 5 6))์ ์ค์ AREF๋ ๋ฐฐ์ด์ ํน์ ์์น ๊ฐ์ ๊บผ๋ผ ๋ ์ฌ์ฉํ๋ ํจ์- ์์์๋ 0ํ 1์ด์ ๊ฐ์ ๊บผ๋ด๋ 2๊ฐ ๋์ด
COMPLEX NUMBERS
(SETF C #C(3 4))
(REALPART C) ; โ 3
(IMAGPART C) ; โ 4
(ABS C) ; โ 5.0
#C(3 4)๋ ๋ณต์์ 3 + 4i๋ฅผ ํํํ๋ ๋ฐฉ๋ฒREALPART: ์ค์๋ถ๋ง ๊บผ๋IMAGPART: ํ์๋ถ๋ง ๊บผ๋ABS: ๋ณต์์์ ํฌ๊ธฐ๋ฅผ ๊ณ์ฐ (โ(3ยฒ + 4ยฒ) = 5.0)
๋ฌธ์์ด(String)
(SETF S "HELLO, LISP!")
(SUBSEQ S 0 5) ; โ "HELLO"
- ๋ฌธ์์ด ๋ณ์ S๋ฅผ ์ ์
SUBSEQ๋ ๋ถ๋ถ ๋ฌธ์์ด์ ์๋ฅผ ๋ ์ฌ์ฉ (0๋ถํฐ 5 ์ ๊น์ง)
ํ์ผ ์ ์ถ๋ ฅ (I/O)
(WITH-OPEN-FILE (STREAM "OUTPUT.TXT" :DIRECTION :OUTPUT :IF-EXISTS :SUPERSEDE)
(FORMAT STREAM "HELLO, FILE!~%"))
"OUTPUT.TXT"๋ผ๋ ํ์ผ์ ์ฐ๊ธฐ ๋ชจ๋๋ก ์ด์ด์, "HELLO, FILE!"์ด๋ผ๋ ๋ฌธ์ฅ์ ์WITH-OPEN-FILE์ ํ์ผ์ ์ด๊ณ ๋ซ๋ ๊ฒ์ ์๋์ผ๋ก ์ฒ๋ฆฌSTREAM์ ํ์ผ ์คํธ๋ฆผ(์ ์ถ๋ ฅ ํต๋ก)์ ๋ํ๋ด๋ ๋ณ์๋ช:DIRECTION :OUTPUTโ ํ์ผ์ ์ฐ๊ธฐ ๋ชจ๋๋ก ์ฐ๋ค๋ ๋ป:IF-EXISTS :SUPERSEDEโ ํ์ผ์ด ์ด๋ฏธ ์์ผ๋ฉด ๋ฎ์ด์ด๋ค(๊ธฐ์กด ๋ด์ฉ ์ญ์ )๋ ์๋ฏธ
๋ฐ๋ณต๋ฌธ: LOOP
(LOOP FOR I FROM 1 TO 5 DO
(FORMAT T "I = ~A~%" I))
FOR I FROM 1 TO 5๋ 1๋ถํฐ 5๊น์ง ๋ฐ๋ณต- ๊ฐ ๋ฐ๋ณต๋ง๋ค
I = 1,I = 2, ... ๋ฅผ ์ถ๋ ฅ ~A๋ ๊ฐ์ ์๋์ผ๋ก ๋ฌธ์์ด๋ก ๋ณํํด ์ถ๋ ฅํจT๋ ์ถ๋ ฅ ๋์์ ์ฝ์(ํฐ๋ฏธ๋)๋ก- Common LISP allows both static and dynamic scoping
- The default scoping for variables is static
- Declaring a variable to be "special," that variable becomes dynamically scoped
Function
(DEFUN *functionname* (*param1 param2 ... paramN*) (*statements*))
(DEFUN square (x) (* x x))
(DEFUN iterative_member (atm lst)
(PROG ()
loop_1
(COND
((NULL lst) (RETURN NIL)) ; ๋ฆฌ์คํธ ๋ โ ๋ชป ์ฐพ์
((EQUAL atm (CAR lst)) (RETURN T)) ; ์ฐพ์ โ TRUE
)
(SETQ lst (CDR lst)) ; ๋ฆฌ์คํธ ๋ค์ ์์๋ก ์ด๋
(GO loop_1) ; ๋ค์ ๋ฐ๋ณต
))
(DEFUN recursive_member (atm lst)
(COND
((NULL lst) NIL)
((EQUAL atm (CAR lst)) T)
(T (recursive_member atm (CDR lst)))
))
PROG: ๋ด๋ถ์ ๋ ์ด๋ธ(label)์ ์ ์ํ๊ณ , ์ฝ๋ ์คํ ํ๋ฆ์ ์ ์ดํ ์ ์๋ ๊ตฌ์กฐ. ๊ฐ๋จํ ๋ฃจํ๋ ์ ํ(๋น์ถ์ฒ)๋ฅผ ๋ง๋ค ๋ ์ฌ์ฉGO: PROG ๋ด์์ ์ ์ํ ๋ ์ด๋ธ๋ก ์ ํํจRETURN: PROG ๋ธ๋ก์์ ๊ฐ์ ๋ฐํํ๋ฉฐ ํ์ถ
Backquote operator (`)
- Similar to the Scheme's QUOTE, except that some parts of the parameter can be unquoted by preceding them with commas
- Common Lisp์์๋
,(์ผํ)๋ฅผ ์ด์ฉํด์ ์ผ๋ถ ํํ์์ "ํ๊ฐ ๊ฐ๋ฅํ๊ฒ ๋ง๋ค ์ ์๋ค"๋ ๊ฒ ํฐ ํน์ง`(a (* 3 4) c)evaluates to(a (* 3 4) c)`(a ,(* 3 4) c)evaluates to(a 12 c),(* 3 4)โ(* 3 4)๋ถ๋ถ๋ง ๊ณ์ฐ๋จ
Macros
- Macros are often used in Common LISP to extend the language
- ๋งคํฌ๋ก๋ ์ฝ๋ ์กฐ๊ฐ์ ์์ฑํ๊ฑฐ๋ ๋ฐ๊ฟ์ฃผ๋ ๋๊ตฌ
- ์ผ๋ฐ ํจ์์ ๋ฌ๋ฆฌ, ๋งคํฌ๋ก๋ "์ฝ๋๋ฅผ ๋ฐํํ๋ ํจ์"์ฒ๋ผ ๋์ํจ
- Create their effect in two steps:
- ํ์ฅ (Expansion) โ ์ฝ๋๊ฐ ๋งคํฌ๋ก ์ ์๋๋ก ์นํ๋จ
- ํ๊ฐ (Evaluation) โ ํ์ฅ๋ ์ฝ๋๊ฐ ์คํ๋จ
- Lisp ์ฝ๋๋ฅผ ํ์ฅํ๊ณ ์ ์ด ๊ตฌ์กฐ๋ฅผ ์๋ก ์ ์ํ ์ ์์
์๋ก์ด ์ธ์ด ๊ธฐ๋ฅ์ฒ๋ผ ๋ง๋ค ์ ์์ด์ ์ธ์ด๋ฅผ ํ์ฅํ๋ ๋๊ตฌ๋ก ์ฌ์ฉ๋จ
- Some of the predefined functions of Common Lisp are actually macros.
- ์ผ๋ถ Common Lisp์ ๊ธฐ๋ณธ ์ ์ด๋ฌธ๋ค๋ ์ฌ์ค์ ํจ์๊ฐ ์๋๋ผ ๋งคํฌ๋ก์
- i.e.
DOTIMES(์ฃผ์ด์ง ํ์๋งํผ ๋ฐ๋ณต),DOLIST(๋ฆฌ์คํธ๋ฅผ ๋ฐ๋ณต),DO
- Users can define their own macros with
DEFMACRO(์ฌ์ฉ์ ์ ์ ๋งคํฌ๋ก)
(defun say-hello (x)
(dotimes (i x 'Stop!)
(format t "~% Hello ~s" i)))
(say-hello 3)
; Hello 0
; Hello 1
; ...
Common Lisp Symbol Data Type
- Common Lisp has a symbol data type (similar to that of Ruby)
- ์ฌ๋ณผ(symbol)์ Lisp์์ ์ด๋ฆ(name)์ ํํํ๋ ๋ฐ์ดํฐ ํ์
- The reserved words are symbols that evaluate to themselves (i.e. T and NIL)
- Symbols are either bound or unbound
- Bound โ ์ฌ๋ณผ์ด ์ด๋ค ๊ฐ์ ๋ฐ์ธ๋ฉ(์ฐ๊ฒฐ) ๋์ด ์์
- Unbound โ ์ฌ๋ณผ์ด ์์ง ์ด๋ค ๊ฐ์๋ ๋ฐ์ธ๋ฉ๋์ง ์์ (์๋ฌ ๋ฐ์ ๊ฐ๋ฅ)
(DEFUN EXAMPLE (X)
(+ X 1))
(SETF A 10) ; A๋ 10์ ๋ฐ์ธ๋ฉ๋จ
B ; ์๋ฌด๋ฐ ๋ฐ์ธ๋ฉ์ด ์์ผ๋ฉด โ ์๋ฌ: unbound variable B
- ํจ์ ์ธ์ โ ํจ์ ์คํ ์ค ๋ฐ์ธ๋ฉ
SETF,LET๋ฑ์ผ๋ก ๊ฐ ํ ๋น๋ ๋ณ์ โ ๋ฐ์ธ๋ฉ๋จ- ์๋ฌด ํ ๋น๋ ์๋ ์ผ๋ฐ ์ฌ๋ณผ โ ๋ฐ์ธ๋ฉ๋์ง ์์ โ ์๋ฌ ๋ฐ์
ML
A static-scoped functional language with syntax that is closer to Pascal than to Lisp
- ML์ Lisp ๊ณ์ด์ฒ๋ผ ๊ดํธ๋ก ๊ฐ๋ํ ๋ฌธ๋ฒ์ด ์๋
ํ์ ์ ์ธ(type declarations) ๊ฐ๋ฅํ์ง๋ง, ๋๋ถ๋ถ์ ์๋์ผ๋ก ์ถ๋ก (type inferencing)๋จ
fun add x y = x + y; (* ML์ด x์ y์ ํ์
์ int๋ก ์ถ๋ก *)
- It is strongly typed and has no type coercions
- ํ์ ๊ฐ ์๋ฌต์ ๋ณํ(coercion)์ ํ์ฉํ์ง ์์
- Type of every variable and expression can be statically determined
- Does not have imperative-style variables
- ๋ณ์๋ ๋ถ๋ณ(immutable)์ด๊ณ , ์๋ก์ด ๊ฐ์ ๋ง๋ค์ง ๊ธฐ์กด ๊ฐ์ ๋ณ๊ฒฝํ์ง ์์
- Includes exception handling and a module facility for implementing abstract data types
- Includes lists and list operations
ML Specifics
A table called the evaluation environment stores the names of all identifiers in a program, along with their types (like a run-time symbol table)
ํ๊ฐ ํ๊ฒฝ(evaluation environment)์ ์ผ์ข ์ ๋ฐํ์ ์ฌ๋ณผ ํ ์ด๋ธ
ํ๋ก๊ทธ๋จ ๋ด์ ๋ชจ๋ ์๋ณ์(identifier)์ ๋ํด ์ด๋ฆ, ํ์ , (ํจ์์ธ ๊ฒฝ์ฐ) ๋ฐ๋๋ฅผ ์ ์ฅํ๊ณ , ์ ์ ์ค์ฝํ ๊ท์น์ ๋ฐ๋ผ ํ๊ฐํ ๋ ์ฌ์ฉ๋จ
Function declaration form
fun name (formal parameters) = expression;
- Note that infix!
fun cube(x : int) = x * x * x;
(* [In Scheme] (DEFINE (cube x) (* x x x)) *)
- x๋ int ํ์ ์ผ๋ก ๋ช ์๋จ
- ๊ฒฐ๊ณผ๊ฐ์ ๋ง์ง๋ง ํํ์์ธ
x * x * x - ํจ์์ ๋ฐํ๊ฐ(return value)์ ๋ง์ง๋ง ํํ์์ ๊ฐ
- Expression can be a list of expressions
- Separated by semicolons and surrounded by parentheses
- Return value is from the last expression
- Without side effect, expressions before the last serve no purpose!
fun test () = (
print "ML is nice\n";
3 + 4;
11
);
์ฌ๋ฌ ํํ์์ ํ ์ค์ ์ธ ์ ์๊ณ , ;๋ก ๊ตฌ๋ถํ๋ฉฐ ()๋ก ๊ฐ์
๋ง์ง๋ง ํํ์ 11์ด ์ด ํจ์์ ๋ฐํ๊ฐ. ML์ ์์ ํจ์ํ ์ธ์ด๋ผ, ๋ถ์ ํจ๊ณผ(side effect)๊ฐ ์์ผ๋ฉด ์์ ํํ์์ ๋ฌด์๋จ
Type inference
์ ์ ํ์ ์ธ์ด์ง๋ง, ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ํ์ ์ ๋ช ์ํ ํ์๊ฐ ์์
์๋ํ๋ฉด ML์ ํ์ ์ถ๋ก ์์คํ ์ด ๋ฌธ๋งฅ์ ๋ณด๊ณ ํ์ ์ ์๋์ผ๋ก ๊ฒฐ์ ํ๊ธฐ ๋๋ฌธ
fun circumf(r) = 3.14159 * r * r;
- Takes a floating-point and produces a floating-point result
- Types are inferred from type of the literal in expression
fun square(x) = x * x;
- No literal and arithmetic operator (๋ช ํํ ๋ฆฌํฐ๋ด์ด ์์ ๊ฒฝ์ฐ, ์ฐ์ฐ์๋ฅผ ๊ธฐ์ค์ผ๋ก ์ถ๋ก )
โ type of the parameter and the function are assumed to be numeric
- Default numeric type: int
square(2.75);โ cause an error (not coerce real) (square๋ int -> int)- โ
square_real(2.75); - The type could be attached to return value, as in:
fun square(x) : real = x * x;
fun square(x: real): real = x * x;
- ์ฌ๊ธฐ์
: real์ ํจ์ ๋ฐํ๊ฐ์ ํ์ ์ ์ง์ ํ ๊ฒ - With no type specified, it would default to int (the default for numeric values)
- User-defined overloaded functions are not allowed, so if we wanted a cube function for real parameters, it would need to have a different name
- ์:
fun square(x: int)์fun square(x: real)์ ๋์์ ์ ์ ๋ถ๊ฐ โ ์ด๋ฆ์ ๋ค๋ฅด๊ฒ ์ง์ด์ผ ํจ
- ์:
fun square_int(x: int) = x * x;
fun square_real(x: real) = x *. x;
(* ๋ค์ํ ์์น์์์ ํ์
๋ช
์ *)
fun square(x : real) = x * x;
fun square(x) = (x : real) * x;
fun square(x) = x * (x : real);
ML selection
- Similar to that of the imperative languages
if expression then then_expression
else else_expression
where the first expression must evaluate to a Boolean value
fun fact(n : int): int = if n <= 1 then 1
else n * fact(n โ 1);
(* [In Scheme]
(DEFINE (factorial n)
(IF (<= n 1)
1
(* n (factorial (โ n 1))))) *)
- Pattern matching is used to allow a function to operate on different parameter forms
fun fact(0) = 1
| fact(1) = 1
| fact(n : int) : int = n * fact(n โ 1)
Lists
- Literal lists are specified in brackets
[3, 5, 7][]: the empty list
- CONS: binary infix operator
::- ์์ ์์๋ฅผ ๋ถ์ฌ์ ์๋ก์ด ๋ฆฌ์คํธ๋ฅผ ๋ง๋๋ ์ดํญ ์ฐ์ฐ์
4 :: [3, 5, 7], which evaluates to[4, 3, 5, 7]- right-associative:
1 :: 2 :: 3 :: []๋1 :: (2 :: (3 :: []))
- CAR is the unary operator
hd- ๋ฆฌ์คํธ์ ์ฒซ ๋ฒ์งธ ์์๋ฅผ ๋ฐํ, ๋น ๋ฆฌ์คํธ์๋ ์ง์ ์ ์ฉX
hd [3, 5, 7]โ3
- CDR is the unary operator
tl- ๋ฆฌ์คํธ์ ๋๋จธ์ง ๋ฆฌ์คํธ๋ฅผ ๋ฐํ (์ฒซ ์์ ์ ์ธ), ๋น ๋ฆฌ์คํธ์๋ ์ง์ ์ ์ฉX
tl [3, 5, 7]โ[5, 7]
- Availability of patterned function parameters โ
hdandtlare much less frequently used in ML than CAR and CDR in Scheme
fun length([]) = 0
| length(h :: t) = 1 + length(t);
fun append([], lis2) = lis2
| append(h :: t, lis2) = h :: append(t, lis2);
(* [In Scheme]
(DEFINE (length list)
(COND
((NULL? list) 0)
(ELSE (+ 1 (length (CDR list))))))
(DEFINE (append list1 list2)
(COND
((NULL? list1) list2)
(ELSE (CONS (CAR list1)
(append (CDR list1) list2))))) *)
- ML์ ํจํด ๋งค์นญ ๋๋ถ์
hd,tl๊ฐ์ ์ฐ์ฐ์๊ฐ ํจ์ ์ ์ ๋ด๋ถ์์ ๊ตฌ์กฐ์ ์ผ๋ก ๋ น์๋ค์ด ์์
val statement
- The
valstatement binds a name to a value (similar toDEFINEin Scheme)
val distance = time * speed;
- As is the case with
DEFINE,valis nothing like an assignment statement in an imperative language โ name cannot be later rebound to a new value - If there are two val statements for the same identifier, the first is hidden by the second
val statements are often used in let constructs (์ง์ญ ๋ณ์ ๋ธ๋ก์ ๋ง๋๋ ํํ์)
let
val radius = 2.7
val pi = 3.14159
in
pi * radius * radius
end;
(* [In Scheme]
(LET ((radius 2.7) (pi 3.14159))
(* pi radius radius)) *)
filter
A higher-order filtering function for lists
๋ฆฌ์คํธ์์ ์กฐ๊ฑด์ ๋ง์กฑํ๋ ์์๋ง ๋จ๊ธฐ๋ ๊ณ ์ฐจ ํจ์
- Takes a predicate function as its parameter, often in the form of a lambda expression
fn(parameter) => expression
- Lambda expressions are defined like functions, except with the reserved word
fn
filter(fn(x) => x < 100, [25, 1, 711, 50, 100]);
(* This returns [25, 1, 50] *)
(* ์ผ๋ฐํจ์๋ ์ฌ์ฉ ๊ฐ๋ฅ *)
fun isSmall(x) = x < 100;
filter(isSmall, [25, 1, 711, 50, 100]);
map
A higher-order function that takes a single parameter, a function
๋ฆฌ์คํธ์ ๊ฐ ์์์ ํจ์๋ฅผ ์ ์ฉํด์ ์๋ก์ด ๋ฆฌ์คํธ๋ฅผ ๋ง๋๋ ๊ณ ์ฐจ ํจ์
- Applies the parameter function to each element of a list and returns a list of results
fun cube x = x * x * x;
val cubeList = map cube;
val newList = cubeList [1, 3, 5];
(* This sets newList to [1, 27, 125] *)
(* Alternative: use a lambda expression *)
val newList = map (fn x => x * x * x, [1, 3, 5]);
Function Composition
๋ ํจ์๋ฅผ ๊ฒฐํฉํ์ฌ ์๋ก์ด ํจ์๋ฅผ ๋ง๋๋ ๊ฒ
- Use the unary operator,
o(a lowercase "oh")
val h = g o f;
- First applies function
fand then applies functiongto the returned value fromf
fun double x = 2 * x;
fun square x = x * x;
val doubleThenSquare = square o double;
val result = doubleThenSquare(3); (* ๊ฒฐ๊ณผ: square(double(3)) = square(6) = 36 *)
Currying
- ML functions actually take just one parameter
- If more, it considers the parameters a tuple (commas required)
fun addTuple (a, b) = a + b; (* tuple ์ธ์ *)
addTuple(3, 5); (* OK *)
- Currying: Replaces a function with more than one parameter (์ฌ๋ฌ ์ธ์๋ฅผ ๋ฐ๋ ํจ์๋ฅผ, ํ๋์ ์ธ์๋ง ๋ฐ๋ ํจ์๋ค์ ์ฐ์์ผ๋ก ๋ฐ๊พธ๋ ๋ฐฉ์)
โ a function with one parameter that returns a function that takes the other parameters of the original function
- Can be defined in curried form by leaving out the commas in the parameters
์ผํ ์์ด ์ธ์๋ฅผ ๋์ดํ๋ฉด ์๋์ผ๋ก ์ปค๋ง๋ ํจ์๊ฐ ์ ์๋จ
fun add a b = a + b;
(* โ fun add a = fn b => a + b; *)
(* add๋ ๋จผ์ a๋ฅผ ๋ฐ๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ก b๋ฅผ ๋ฐ๋ ํจ์๋ฅผ ๋ฐํ *)
val result = add 3 5; (* = 8 *)
val add3 = add 3; (* = fn b => 3 + b *)
val r = add3 10; (* = 13 *)
- Tuple vs Curried Form
| ํ์ | ์ ์ | ํธ์ถ ๋ฐฉ์ |
|---|---|---|
| Tuple ์ธ์ | fun f (a, b) = ... | f(3, 4) |
| Curried ์ธ์ | fun f a b = ... | f 3 4 |
์ปค๋ง์ ์ฐ๋ฉด ๋ถ๋ถ ์ ์ฉ์ด ๊ฐ๋ฅํด์ ์ ์ฉํจ
Partial Evaluation (๋ถ๋ถ ํ๊ฐ)
- Curried functions can be used to create new functions by partial evaluation
- Partial evaluation means that the function is evaluated with actual parameters for one or more of the leftmost actual parameters
๋ถ๋ถ ํ๊ฐ๋ ํจ์์ ์ผ๋ถ ์ธ์๋ง ๋จผ์ ์ ์ฉํด์ ์๋ก์ด ํจ์๋ฅผ ๋ง๋๋ ๋ฐฉ์
ํนํ ์ปค๋ง๋ ํจ์์์ ์ผ์ชฝ๋ถํฐ ์ผ๋ถ ์ธ์์๋ง ๊ฐ์ ์ฃผ๋ฉด, ๊ทธ ๊ฐ์ด ๊ณ ์ ๋ ์๋ก์ด ํจ์๊ฐ ๋ฐํ
fun add a b = a + b;
fun add5 x = add 5 x;
(* Takes the actual parameter 5 and evaluates the add function with 5 as the value of its first formal parameter. *)
(* Returns a function that adds 5 to its single parameter *)
val num = add5 10; (* sets num to 15 *)
(* [In Scheme]
(DEFINE (add x y) (+ x y))
[Curried version]
(DEFINE (add y) (LAMBDA (x) (+ y x)))
((add 3) 4) *)
์ปค๋ง + partial evaluation์ ์ด์ฉํ๋ฉด, ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ํนํ ํจ์๋ฅผ ์ฝ๊ฒ ๋ง๋ค ์ ์์
Haskell
- Similar to ML: Syntax, static scoped, strongly typed, type inferencing, pattern matching
| ํญ๋ชฉ | ์ค๋ช |
|---|---|
| ๋ฌธ๋ฒ (Syntax) | ํจ์ ์ค์ฌ ๋ฌธ๋ฒ ๊ตฌ์กฐ๊ฐ ๋น์ทํจ |
| ์ ์ ์ค์ฝํ (Static scoping) | ๋ณ์ ๋ฐ์ธ๋ฉ์ ์ ์๋ ์์น ๊ธฐ์ค์ผ๋ก ๊ฒฐ์ |
| ๊ฐํ ํ์ (Strong typing) | ํ์ ์ด ๋ช ํํ๊ณ , ์๋ชป๋ ํ์ ์ฌ์ฉ์ ์ปดํ์ผ ์๋ฌ |
| ํ์ ์ถ๋ก (Type inferencing) | ๋ช ์ํ์ง ์์๋ ๋๋ถ๋ถ ํ์ ์ ์๋์ผ๋ก ์ถ๋ก |
| ํจํด ๋งค์นญ (Pattern matching) | ๋ฆฌ์คํธ, ํํ, ๋ฐ์ดํฐ ํ์ ๋ฑ์์ ์ฝ๊ฒ ๋ถํด ๊ฐ๋ฅ |
- Different from ML (and most other functional languages):
| ํญ๋ชฉ | ์ค๋ช |
|---|---|
| ํจ์ ์ค๋ฒ๋ก๋ฉ ๊ฐ๋ฅ | ML์์๋ ์ฌ์ฉ์ ์ ์ ํจ์์ ์ค๋ฒ๋ก๋ฉ์ด ๋ถ๊ฐ๋ฅํ์ง๋ง, Haskell์ type class๋ฅผ ํตํด ์ค๋ฒ๋ก๋ฉ ์ง์ |
| Nonstrict semantics (์ง์ฐ ํ๊ฐ) | ์ธ์๊ฐ ์ค์ ๋ก ์ฌ์ฉ๋ ๋๊น์ง ๊ณ์ฐ๋์ง ์์ (lazy evaluation) |
| ์์ ํจ์ํ ์ธ์ด (Purely functional) | ๋ณ์ ์์, ๋์ ๋ฌธ ์์, ๋ถ์ ํจ๊ณผ ์์ โ ๋ชจ๋ ํจ์๋ ์ ๋ ฅ๋ง์ผ๋ก ๊ฒฐ๊ณผ๊ฐ ๊ฒฐ์ ๋จ |
e.g., no variables, no assignment statements, and no side effects of any kind
| ML | Haskell | |
|---|---|---|
| ๋ณ์ ์ ์ธ | val x = 3; | x = 3 |
| ๋ฐ์ธ๋ฉ ํน์ฑ | ๋ณ์ x๋ ๋ณ๊ฒฝ ๋ถ๊ฐ์ด์ง๋ง, val์ ๋ฐ๋ณตํด์ ์ ์ด๋ฆ ์ง์ ๊ฐ๋ฅ | x๋ ์์์ฒ๋ผ ์ทจ๊ธ๋จ (๋ณ๊ฒฝ ๋ถ๊ฐ), =๋ ์ํ์ ์ ์๋ก ์ฌ์ฉ๋จ (๋์
์๋) |
Basic operations
head: 0th elementhead [2, 3, 4]--2tail: All elements except element 0tail [2, 3, 4]--[3, 4]init: all elements except the last element (initial part of a list)init [2, 3, 4]--[2, 3]last: last elementlast [2, 3, 4]--4take: get elements from fronttake 2 [2, 3, 4]--[2, 3]drop: remove elements from frontdrop 2 [2, 3, 4]--[4]
head, tail, init, last๋ ๋น ๋ฆฌ์คํธ []์ ์ฌ์ฉํ๋ฉด ์๋ฌ๊ฐ ๋ฐ์. ์: head [] โ runtime error
take๊ณผ drop์ ๊ธธ์ด๋ฅผ ๋๋ ๊ฐ์ ๋ฃ์ด๋ ์์ ํจ.
take 5 [1, 2]โ[1, 2]drop 5 [1, 2]โ[]- Indexing from 0 using
!!- n๋ฒ์งธ ์ธ๋ฑ์ค์ ์๋ ๊ฐ์ ๋ฐํ:
list !! index
- n๋ฒ์งธ ์ธ๋ฑ์ค์ ์๋ ๊ฐ์ ๋ฐํ:
[1, 2, 3, 4] !! 1 -- 2
[1, 2, 3, 4] !! 2 -- 3
[1, 2, 3, 4, 5] !! 0 -- 1
[1, 2, 3] !! 5 -- ์๋ฌ: index too large
[1, 2, 3] !! (-1) -- ์๋ฌ: negative index
Haskell Lists
- List notation: put elements in brackets (๋ฆฌ์คํธ๋ ๋์ผํ ํ์ ์ ๊ฐ๋ค๋ก ๊ตฌ์ฑ)
directions = ["north", "south", "east", "west"]
length directions -- ๊ฒฐ๊ณผ: 4
- Arithmetic series with the
..operator (์ฐ์ ์์ด)- ์ฒซ ๋ฒ์งธ ๋ ํญ๋ชฉ์ ๊ธฐ๋ฐ์ผ๋ก ์ฆ๊ฐ ๊ฐ๊ฒฉ์ ์ถ์
[2, 4..10]is[2, 4, 6, 8, 10]- ๋ฌดํ ๋ฆฌ์คํธ๋ ์์ฑ ๊ฐ๋ฅ
[1,3..]-- ๋ฌดํํ 2์ฉ ์ฆ๊ฐํ๋ ์์ดtake 5 [1,3..]--[1,3,5,7,9]
- Concatenation is with
++(๋ฆฌ์คํธ ์ฐ๊ฒฐ)[1, 3] ++ [5, 7]results in[1, 3, 5, 7]- ์๊ฐ ๋ณต์ก๋๋ ์ ๋ฆฌ์คํธ์ ๊ธธ์ด์ ๋น๋กํ๋ฏ๋ก ์์ชฝ ๋ฆฌ์คํธ๊ฐ ๋๋ฌด ๊ธธ๋ฉด ์ฑ๋ฅ ์ ํ ๊ฐ๋ฅ
- CONS, CAR, CDR via the colon operator (infix version of CONS)
1:[3, 5, 7]results in[1, 3, 5, 7]
A list can only contain the same type
- ์:
[1, 2, 3]์ OK, ํ์ง๋ง[1, "two", 3]์ ํ์ ์ค๋ฅ
product [1, 2, 3, 4, 5] -- 120
sum [1, 2, 3, 4, 5] -- 15
length [1, 2, 3, 4, 5] -- 5
reverse [1, 2, 3, 4] -- [4, 3, 2, 1]
product,sum์ Num ํ์ ํด๋์ค์ ์ธ์คํด์ค(์: Int, Integer, Float ๋ฑ)์๋ง ์ ์ฉ ๊ฐ๋ฅreverse๋ ์ด๋ค ํ์ ์ ๋ฆฌ์คํธ์๋ ๋์. (์:reverse "hello"โ"olleh")
Haskell Tuples
A tuple can contain the different type
- ์ด์ง์ฑ: ํํ์ ๊ฐ ์์๊ฐ ์๋ก ๋ค๋ฅธ ํ์ ์ผ ์ ์์
(False, 'a', True) -- (Bool, Char, Bool)
(True, ['a', 'b']) -- (Bool, [Char]) ๋๋ (Bool, String)
์(pair), ์ฆ ๋ ์์์ง๋ฆฌ ํํ์ ๋ํด์ ํธ๋ฆฌํ ์ ๊ทผ ํจ์
fst: ๋ ์์์ง๋ฆฌ ํํ์ ์ฒซ ๋ฒ์งธ ์์๋ฅผ ๋ฐํsnd: ๋ ์์์ง๋ฆฌ ํํ์ ๋ ๋ฒ์งธ ์์๋ฅผ ๋ฐํ
fst (1, "Hello") -- 1
snd (1, "Hello") -- "Hello"
fst (snd (1, (2, 3))) -- 2
Haskell Functions
- Haskell์์๋ ๊ดํธ ์์ด ๊ณต๋ฐฑ์ผ๋ก ํจ์ ์ธ์๋ฅผ ์ ๋ฌ
f x -- f(x)
f x y -- f(x, y) โ ์ค์ ๋ก๋ ((f x) y) ํํ๋ก ์ผ์ชฝ๋ถํฐ ์ ์ฉ
f (g x) -- f(g(x))
f x (g y) -- f(x, g(y))
f x * g y -- f(x) * g(y) โ *๊ฐ f x์ g y ๊ฒฐ๊ณผ๋ฅผ ๊ณฑํจ
double x = x + x
quadruple x = double (double x)
take (double 2) [1, 2, 3, 4, 5, 6] -- [1, 2, 3, 4]
avg ns = sum ns `div` length ns -- average
-- Function surrounded by backticks: infix binary operator
-- avg ns = div (sum ns) (length ns)
- Because Haskell support polymorphism, this works for any numeric type of x
์๋ ํจ์๋ ๊ตฌ์ฒด์ ์ธ ํ์ ๋ช ์ ์์ด ์ ์๋์ง๋ง, Haskell์ ํ์ ์ถ๋ก ๊ณผ ๋คํ์ฑ ์ง์ ๋๋ถ์, x๊ฐ Num ํ์ ํด๋์ค์ ์ํ ์ด๋ค ํ์ ์ด๋ ์ฌ์ฉํ ์ ์์
square x = x * x
-- [In ML]
-- fun cube(x : int) = x * x * x;
-- ML์์๋ ๊ตฌ์ฒด์ ์ธ ํ์
ํ์
- Syntax differences from ML
| ํญ๋ชฉ | Haskell | ML (Standard ML) |
|---|---|---|
| ํจ์ ์ ์ ์์ ํค์๋ | ์์ (์ง์ ์ด๋ฆ์ผ๋ก ์์) | fun ์ฌ์ฉ ํ์ |
| ํจํด ๋งค์นญ | ๊ฐ ์ ์๋ฅผ ๋ณ๋ ์ค๋ก ์์ฑ (์ง๊ด์ ) | ` |
| ํจ์ ์ด๋ฆ ๋ฐ๋ณต | ์ค๋ง๋ค ํจ์ ์ด๋ฆ ๋ค์ ์์ฑ | ํ๋์ ํจ์ ์ ์ ๋ด์์ ์ด๋ฆ์ ๋ฐ๋ณตํ์ง ์์ |
fact 0 = 1
fact 1 = 1
fact n = n * fact (n โ 1)
fib 0 = 1
fib 1 = 1
fib (n + 2) = fib (n + 1) + fib n
-- ์์ ๊ธฐ๋ฐ ํจํด ๋งค์นญ: ์ด ํํ์ Haskell์์๋ง ๊ฐ๋ฅ
-- n + 2๋ ํจํด ๋งค์นญ ์ ์ญ์ผ๋ก "2 ์ด์"์ ์๋ฏธํจ
{- [In ML]
fun fact(0) = 1
| fact(1) = 1
| fact(n : int) : int = n * fact(n โ 1)
fun fib(0) = 1
| fact(1) = 1
| fact(n : int) : int = fact(n โ 1) * fact(n โ 2)
-- ML์ ์ ์ ์ฐ์ฐ ํํ์ ๊ธฐ๋ฐ ํจํด์ ํ์ฉํ์ง ์์
-- ์ฆ, fib(n + 2) ๊ฐ์ ํํ๋ ์ฌ์ฉ ๋ถ๊ฐ -}
- Guards can be added to lines of a function definition to specify the circumstances under which the definition can be applied
|(ํ์ดํ) ๊ธฐํธ๋ฅผ ์ฌ์ฉํด ์กฐ๊ฑด์ ๋์ดํจ- ๊ฐ ์กฐ๊ฑด์ด True๊ฐ ๋๋ฉด ํด๋น ๊ฒฐ๊ณผ๋ฅผ ๋ฐํ
- An
otherwisecan appear as the last condition in a conditional expression - ๋ง์ง๋ง์
otherwise๋ฅผ ์จ์ ๊ธฐ๋ณธ(default) ์กฐ๊ฑด์ ์ง์ ํ ์ ์์ - (
otherwise๋ ์ฌ์ค True๋ก ์ ์๋ ์์)
fact n
| n == 0 = 1
| n == 1 = 1
| n > 0 = n * fact(n โ 1)
sub n
| n < 10 = 0
| n > 100 = 2
| otherwise = 1
if-then-else๋ ํ๋์ ์กฐ๊ฑด๋ง ๋ค๋ฃจ์ง๋ง guards๋ ์ฌ๋ฌ ์กฐ๊ฑด์ ๊น๋ํ๊ฒ ๋์ดํ ์ ์์
Pattern Parameters
-- product: ์ฌ๊ท์ ์ผ๋ก ๊ณฑ์
์ํ
product [] = 1
product (a:x) = a * product x
-- length
length [] = 0
length (a:x) = 1 + length x
-- reverse
reverse [] = []
reverse (a:x) = reverse(x) ++ [a]
-- Factorial:
fact n = product [1..n]
-- drop: ๋ฆฌ์คํธ์์ ์์์๋ถํฐ n๊ฐ๋ฅผ ์ ๊ฑฐ
drop 0 x = x
drop _ [] = []
drop n (a:x) = drop (n-1) x
-- ++: ๋ฆฌ์คํธ ์ฐ๊ฒฐ ์ฐ์ฐ์ ์ง์ ์ ์ํ ๊ฒ
[] ++ y = y
(a:x) ++ y = a : (x ++ y)
-- *: ์์ฐ์์ ๋ํ ๊ณฑ์
์ ์ฌ๊ท์ ์ผ๋ก ์ ์ํ ๊ฒ
m * 0 = 0
m * n = m + (m * (n - 1))
let ... in construct
- Similar to ML's let and val
let <๋ฐ์ธ๋ฉ๋ค> in <ํํ์>
let ์์์๋ ์ฌ๋ฌ ๊ฐ์ ์ง์ญ ๋ณ์(๋๋ ํจ์)๋ฅผ ์ ์ํ ์ ์๊ณ , in ๋ค์๋ ์ค์ ๋ก ๊ณ์ฐํ ํํ์์ ์ ์
๋ชจ๋ let ๋ด๋ถ ๋ฐ์ธ๋ฉ์ immutable(๋ถ๋ณ)
quadratic_root a b c =
let
minus_b_over_2a = โ b / (2.0 * a)
root_part_over_2a = sqrt(b ^ 2 โ 4.0 * a * c) / (2.0 * a)
in
minus_b_over_2a โ root_part_over_2a,
minus_b_over_2a + root_part_over_2a
| ํญ๋ชฉ | Haskell | ML (SML) |
|---|---|---|
| ์์ ํค์๋ | let | let + ๊ฐ ์ค์ val |
| ์ข ๋ฃ ํค์๋ | in ์ดํ ๋ฐ๋ก ๊ฒฐ๊ณผ ํํ | in ... end ๋ธ๋ก์ผ๋ก ๋ช
ํํ ๋๋ |
where construct
ํจ์ ๋ณธ๋ฌธ ์๋์ ์ง์ญ ๋ณ์๋ฅผ ์ ์ํ๋ ๋ฐฉ์
์ฌ๋ฌ ํํ์์์ ๊ณตํต์ผ๋ก ์ฐ์ด๋ ์ค๊ฐ ๊ฒฐ๊ณผ๋ฅผ ๊น๋ํ๊ฒ ์ ๋ฆฌํ ์ ์์
let ... in์ ํํ์ ๋ด๋ถ, where๋ ํจ์ ์ ์ ๋ฐ๊นฅ์ ์์น
quadratic_root a b c =
[minus_b_over_2a - root_part_over_2a,
minus_b_over_2a + root_part_over_2a]
where
minus_b_over_2a = - b / (2.0 * a)
root_part_over_2a = sqrt (b ^ 2 - 4.0 * a * c) / (2.0 * a)
| ํญ๋ชฉ | let ... in | where ๊ตฌ๋ฌธ |
|---|---|---|
| ์์น | ํํ์ ๋ด๋ถ | ํจ์ ์ ์ ์๋ |
| ๋ฌธ๋ฒ ๊ตฌ์กฐ | let <๋ฐ์ธ๋ฉ> in <ํํ์> | <ํํ์> where <๋ฐ์ธ๋ฉ> |
| ์ค์ฝํ | in ๋ค ํํ์ ์์์๋ง ์ ํจ | ํด๋น ํจ์ ์ ์ ์ ์ฒด์์ ์ ํจ |
| ์ฌ์ฉ ์์ | let x = ... in x + 1 | f x = x + y where y = 3 |
List Comprehensions
- Python์ ๋ฆฌ์คํธ ๋ดํฌ์ ๋น์ทํ์ง๋ง, Haskell ํน์ ์ ํํ๋ ฅ๊ณผ ์ํ์ ์ธ ๋ฌธ๋ฒ์ ์ ๋ณด์ฌ์ฃผ๋ ๊ธฐ๋ฅ
[<expression> | <qualifiers>]
<expression>: ๊ฒฐ๊ณผ๋ก ๋ง๋ค์ด์ง ๊ฐ ์์<qualifiers>: ๋ณ์ ์์ฑ, ์กฐ๊ฑด ํํฐ๋ง ๋ฑ
[n * n * n | n <- [1..50]]
-- "a list of all n*n*n such that n is taken from the range of 1 to 50"
-- The qualifier in this example has the form of a generator (n <- [1..50])
- Multiple generators
[(x, y) | x <- [1..3], y <- [4..5]]
-- [(1,4),(1,5),(2,4),(2,5),(3,4),(3,5)]
-- x๋ 1๋ถํฐ 3๊น์ง, y๋ 4๋ถํฐ 5๊น์ง โ ๋ชจ๋ ์กฐํฉ
- Dependant generator
[(x, y) | x <- [1..3], y <- [x + 1]]
-- [(1,2),(2,3),(3,4)]
-- y๋ x์ ์์กดํด์ ๊ฒฐ์ ๋จ
concat ์ ์ (๋ฆฌ์คํธ ํํํ)
concat xss = [x | xs <- xss, x <- xs]
concat [[1, 2, 3], [4, 5], [6]] -- [1,2,3,4,5,6]
-- ์ธ๋ถ ๋ฆฌ์คํธ xss์์ ๋ฆฌ์คํธ xs๋ฅผ ํ๋์ฉ ๊บผ๋ด๊ณ , ๊ฐ xs ์์ ์์ x๋ฅผ ๊บผ๋ด ๋ฆฌ์คํธ๋ก ๋ง๋ฆ
์กฐ๊ฑด ํํฐ(guard)
[x | x <- [1..n], ์กฐ๊ฑด์]
- It could be in the form of a test. If qualifiers are in the form of Boolean expressions (guards)
์ฝ์ ๊ตฌํ๊ธฐ (factors)
factors n = [x | x <- [1..n], n `mod` x == 0]
-- x๊ฐ 1๋ถํฐ n๊น์ง ์ํํ๋ฉด์, n์ด x๋ก ๋๋์ด ๋จ์ด์ง๋ฉด (n mod x == 0) ๋ฆฌ์คํธ์ ํฌํจ
factors 17 -- [1,17]
factors 15 -- [1,3,5,15]
์์ ํ๋ณ (prime)
prime n = factors n == [1, n]
-- ์์๋ ์ค์ง 1๊ณผ ์๊ธฐ ์์ ๋ง ์ฝ์๋ก ๊ฐ์ง๋ฏ๋ก, factors n == [1, n]์ด๋ฉด ์์
- The backticks specify the function is used as a binary operator
zip: takes two lists and creates one list
zip [1, 2, 3] ['a', 'b', 'c', 'd'] -- [(1,'a'),(2,'b'),(3,'c')]
-- ๋ ๋ฆฌ์คํธ์ ๊ฐ ์์๋ฅผ ์์ผ๋ก ๋ฌถ์. ๋ ์งง์ ๋ฆฌ์คํธ ๊ธฐ์ค์ผ๋ก ์๋ฆผ
pairs ํจ์: ์ธ์ ์ ๋ง๋ค๊ธฐ
pairs xs = zip xs (tail xs)
pairs [1, 2, 3, 4] -- [(1,2),(2,3),(3,4)]
-- xs์ tail xs๋ฅผ ๋ฌถ์ด์ (xโ,xโ), (xโ,xโ), ... ํํ์ ์ธ์ ์ ๋ฆฌ์คํธ ์์ฑ
sorted ํจ์: ์ ๋ ฌ ์ฌ๋ถ ๊ฒ์ฌ
sorted = and [x <= y | (x, y) <- pairs xs]
-- pairs xs๋ก ์ธ์ ํ ์์ ์๋ค์ ๋ง๋ค๊ณ , x <= y ์กฐ๊ฑด์ ๊ฒ์ฌํ์ฌ ๋ฆฌ์คํธ ์์ฑ
-- and๋ ๊ทธ ๋ฆฌ์คํธ์ ๋ชจ๋ ๊ฐ์ด True์ธ์ง ํ์ธ (์ ๋ ฌ ํ์ธ)
sorted [1, 2, 3, 4] -- True
sorted [1, 2, 5, 3, 4] -- False
positions ํจ์: ํน์ ๊ฐ์ ์์น ์ฐพ๊ธฐ
positions x xs = [i | (x', i) <- zip xs [0..n], x == x']
where n = (length xs) - 1
-- zip xs [0..n]์ ํตํด ๊ฐ ์์์ ๊ทธ ์ธ๋ฑ์ค๋ฅผ ์์ผ๋ก ๋ง๋ฆ
-- ์กฐ๊ฑด x == x'์ธ ๊ฒฝ์ฐ์๋ง ์ธ๋ฑ์ค i๋ฅผ ๊ฒฐ๊ณผ ๋ฆฌ์คํธ์ ํฌํจ
positions 0 [0, 1, 0, 1, 1, 1, 1, 0] -- [0,2,7]
String Comprehensions
- String is list of characters. ๋ฌธ์์ด๋ ๋ฆฌ์คํธ ํจ์์ comprehension์ ๊ทธ๋๋ก ์ฌ์ฉ
zip "abc" [1, 2, 3] -- [('a',1),('b',2),('c',3)]
take 3 "asdasd" -- "asd"
length "adasd" -- 5
[c | c <- "hello", c /= 'l'] -- "heo"
Quicksort
๋ฆฌ์คํธ ๋ดํฌ(list comprehension)์ ํจํด ๋งค์นญ์ ํ์ฉํด ์์ฃผ ๊ฐ๊ฒฐํ๊ฒ ํํ
sort [] = []
sort (h:t) =
sort [b | b <- t, b <= h]
++ [h] ++
sort [b | b <- t, b > h]
-- sort [3, 1, 4, 2]
-- h = 3, t = [1, 4, 2]
-- ์ผ์ชฝ = [1, 2]
-- ์ค๋ฅธ์ชฝ = [4]
-- ์ต์ข
๊ฒฐ๊ณผ = sort [1, 2] ++ [3] ++ sort [4]
-- ์ฌ๊ท์ ์ผ๋ก ์ํ๋์ด [1, 2, 3, 4]
- Illustrates the concision of Haskell
Insertion sort
-- ์ฝ์
ํจ์
insert x [] = [x]
insert x (y:ys)
| x <= y = x : y : ys
| otherwise = y : insert x ys
-- ์ ์ฒด ์ ๋ ฌ ํจ์
isort [] = []
isort (x:xs) = insert x (isort xs)
-- isort [3, 1, 4, 2]
-- = insert 3 (isort [1,4,2])
-- = insert 3 (insert 1 (isort [4,2]))
-- = insert 3 (insert 1 (insert 4 (isort [2])))
-- = insert 3 (insert 1 (insert 4 (insert 2 [])))
-- ์์ฐจ์ ์ผ๋ก ์ ๋ ฌ๋์ด [1,2,3,4]
{- [In scheme]
(DEFINE (insert atm lst)
(COND
((NULL? lst) (CONS atm '()))
((< atm (CAR lst)) (CONS atm lst))
(ELSE (CONS (CAR lst) (insert atm (CDR lst))))))
(DEFINE (sort lst)
(if (NULL? lst)
'()
(insert (CAR lst) (sort (CDR lst))))) -}
insert: x๋ฅผ ์ ๋ ฌ๋ ๋ฆฌ์คํธ์ ์ฌ๋ฐ๋ฅธ ์์น์ ์ฝ์ ํจ. ๋น ๋ฆฌ์คํธ๋ฉด[x]๋ฅผ ๋ฐํ. ์์ ์์ y์ ๋น๊ตํด์ x <= y์ด๋ฉด x๋ฅผ ์์ ๋ถ์ด๊ณ , ๊ทธ๋ ์ง ์์ผ๋ฉด y๋ฅผ ์ ์งํ๊ณ ๋๋จธ์ง์ ๋ํด ์ฌ๊ท์ ์ผ๋ก ์ฝ์isort: ์ ๋ ฅ ๋ฆฌ์คํธ๋ฅผ ์ผ์ชฝ๋ถํฐ ํ๋์ฉ ๋ถํดํ๋ฉด์ ๋๋จธ์ง๋ฅผ ๋จผ์ ์ ๋ ฌํ ํ, insert๋ก ํ๋์ฉ ์์ ๋ผ์ ๋ฃ๋ ๋ฐฉ์ (์ฌ๊ท)
Merge sort
merge [] ys = ys
merge xs [] = xs
merge (x:xs) (y:ys) =
if x <= y then x : merge xs (y:ys) else y : merge (x:xs) ys
halve xs = splitAt (length xs `div` 2) xs
msort [] = []
msort [x] = [x]
msort xs = merge (msort ys) (msort zs)
where (ys, zs) = halve xs
-- msort [4,1,5,2,6,3]
-- halve: ([4,1,5],[2,6,3])
-- ์ฌ๊ท์ ์ผ๋ก msort ๋ ๋ฒ ์ ์ฉ
-- ๊ฒฐ๊ณผ: [1,2,3,4,5,6]
mergeํจ์ โ ๋ ์ ๋ ฌ๋ ๋ฆฌ์คํธ๋ฅผ ๋ณํฉ: ๋ ๋ฆฌ์คํธ๊ฐ ์ ๋ ฌ๋ ์ํ๋ก ๋ค์ด์ค๋ฉด, ์์์๋ถํฐ ์์ ๊ฐ์ ํ๋์ฉ ๋ฝ์์ ์ ๋ฆฌ์คํธ๋ฅผ ๊ตฌ์ฑ. x <= y์ผ ๋ x๊ฐ ๋จผ์ ์ค๊ณ , ์๋๋ผ๋ฉด y๊ฐ ๋จผ์ halveํจ์ โ ๋ฆฌ์คํธ๋ฅผ ๋ฐ์ผ๋ก ๋๋:splitAt์ ๋ฆฌ์คํธ๋ฅผ ์ง์ ํ ์ธ๋ฑ์ค์์ ๋๋ก ๋๋.length xs \div` 2`๋ฅผ ์ด์ฉํด ๋ฐ์ผ๋ก ์ชผ๊ฐ๊ธฐmsortํจ์ โ ๋ณํฉ ์ ๋ ฌ ์ ์ฒด ๊ตฌํ: ๊ธฐ์ ์กฐ๊ฑด์ผ๋ก ๋น ๋ฆฌ์คํธ, ํ๋์ง๋ฆฌ ๋ฆฌ์คํธ๋ ์ ๋ ฌ๋ ์ํ๋ก ๊ทธ๋๋ก ๋ฐํ. ๊ทธ๋ ์ง ์์ผ๋ฉด ๋ฆฌ์คํธ๋ฅผ ๋ฐ์ผ๋ก ๋๋๊ณ , ์์ชฝ์ ์ฌ๊ท์ ์ผ๋ก ์ ๋ ฌํ ๋ค mergeํจ.where๊ตฌ๋ฌธ์ผ๋กys,zs์ ์
Higher-order functions
- Higher-order function: ํจ์๋ฅผ ์ธ์๋ก ๋ฐ๊ฑฐ๋ ํจ์๋ฅผ ๊ฒฐ๊ณผ๋ก ๋ฐํํ๋ ํจ์
map: ๋ฆฌ์คํธ์ ๊ฐ ์์์ ํจ์๋ฅผ ์ ์ฉํ์ฌ ์๋ก์ด ๋ฆฌ์คํธ๋ฅผ ๋ง๋๋ ํจ์
map์ ํจ์ f๋ฅผ ์ธ์๋ก ๋ฐ๋ ํจ์์ด๋ฏ๋ก ๊ณ ์ฐจ ํจ์์
map (+1) [1, 3, 5, 7] -- [2, 4, 6, 8]
-- 1. ๋ฆฌ์คํธ ๋ดํฌ (List Comprehension)
map f xs = [f x | x <- xs]
-- 2. ์ฌ๊ท (Recursive Function)
map f [] = []
map f (x:xs) = f x : map f xs
foldr: ๋ฆฌ์คํธ๋ฅผ ์ค๋ฅธ์ชฝ์์ ์ผ์ชฝ์ผ๋ก ์ค์ฌ๋๊ฐ๋ ํจ์ (right fold)
f [] = v
f (x:xs) = x `pred` f xs
- If it is an empty element, it returns a specific value v. Otherwise, pred is applied to element x, and f is applied to the remaining tail xs
๊ตฌ๋ฌธ ํํ: foldr <operator> <initial_value> <list>
์๋ ๋ฐฉ์: foldr f v [x1, x2, x3] = x1 \f` (x2 `f` (x3 `f` v))`
-- v = 0, pred = +
sum [] = 0
sum (x:xs) = x + sum xs
sum = foldr (+) 0
-- v = 1, pred = *
product [] = 1
product (x:xs) = x * product xs
product = foldr (*) 1
-- v = True, pred = &&
and [] = True
and (x:xs) = x && and xs
and = foldr (&&) True
-- v = 0, pred = \_ n -> 1 + n
length [] = 0
length (x:xs) = 1 + length xs
length = foldr (\_ n -> 1 + n) 0
-- v = [], pred = \x xs -> xs ++ [x]
reverse [] = []
reverse (x:xs) = reverse xs ++ [x]
reverse = foldr (\x xs -> xs ++ [x]) []
-- length [10,20,30]
-- = 1 + (1 + (1 + 0)) = 3
-- reverse [1,2,3]
-- = 3 : [] โ [3]
-- = 2 : [3] โ [3,2]
-- = 1 : [3,2] โ [3,2,1]
composition
f . g = \x -> f(g x)
-- \x -> expression: ์ต๋ช
ํจ์(anonymous function)
-- f . g๋ g๋ฅผ ๋จผ์ ์ ์ฉํ๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ f์ ๋๊น
-- ์ค๋ฅธ์ชฝ๋ถํฐ ์ผ์ชฝ์ผ๋ก ์ ์ฉ๋จ
-- ์: odd = not . even
all: ๋ชจ๋ ์์๊ฐ ์กฐ๊ฑด์ ๋ง์กฑํ๋๊ฐ?
all p xs = and [p x | x <- xs]
-- ๋ฆฌ์คํธ xs์ ๊ฐ ์์์ ํจ์ p๋ฅผ ์ ์ฉํด์ [Bool] ๋ฆฌ์คํธ๋ฅผ ๋ง๋ค๊ณ , and๋ฅผ ์ด์ฉํด ๋ชจ๋ ๊ฐ์ด True์ธ์ง ํ์ธ
all even [2, 4, 6] -- True
all even [2, 3, 6] -- False
any: ํ๋๋ผ๋ ์กฐ๊ฑด์ ๋ง์กฑํ๋๊ฐ?
any p xs = or [p x | x <- xs]
-- ๋ฆฌ์คํธ xs์ ๊ฐ ์์์ ํจ์ p๋ฅผ ์ ์ฉํด์ [Bool] ๋ฆฌ์คํธ๋ฅผ ๋ง๋ค๊ณ , or๋ฅผ ์ด์ฉํด ํ๋๋ผ๋ True์ธ์ง ํ์ธ
any odd [2, 4, 6] -- False
any odd [2, 3, 6] -- True
Lazy Evaluation
A language is strict if it requires all actual parameters to be fully evaluated
- Does not depend on the order in which the parameters are evaluated
- ์์: Scheme, ML, Java, Python
f(x + y)๋ฅผ ํธ์ถํ๋ฉด,x + y๋ ๋ฌด์กฐ๊ฑด ๋จผ์ ๊ณ์ฐ๋จ- ๋จ์ : ์ด๋ค ์ธ์๋ ์ค์ ๋ก ํจ์ ๋ด๋ถ์์ ์ฌ์ฉ๋์ง ์๋๋ผ๋ ๋ฌด์กฐ๊ฑด ๊ณ์ฐ๋จ
A language is nonstrict if it does not have the strict requirement
- ์ธ์๊ฐ ์ฌ์ฉ๋ ๋๊น์ง ํ๊ฐ๋ฅผ ๋ฏธ๋ฃธ (โ ํ์ํ ๋๋ง ํ๊ฐ)
- lazy evaluation์ ์ง์ํ๋ ๋ฐฉ์์ผ๋ก ๊ตฌํ ๊ฐ๋ฅ
- ์์: Haskell
- Benefits:
- [1] Nonstrict languages are more efficient: Some evaluation is avoided (related to short-circuit evaluation)
- [2] Lazy evaluation - Only compute those values that are necessary
- Recall that in Scheme the parameters are fully evaluated before function call
- Actual parameters are evaluated only once, even if the same actual parameter appears more than once
- We can make infinite lists
- Infinite data structures: lazy evaluation(์ง์ฐ ํ๊ฐ) ๋๋ถ์ ๋ฌดํํ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ ์์ ํ๊ฒ ๋ค๋ฃฐ ์ ์์
positives = [0..] -- 0๋ถํฐ ์์ํ๋ ๋ชจ๋ ์์ฐ์
evens = [2, 4..] -- 2๋ถํฐ ์์ํด์ 2์ฉ ์ฆ๊ฐ
squares = [n * n | n <- [0..]]
-- Determining if 16 is a square number
squares = [n * n | n <- [0..]]
member squares 16
Member Revisited
member b [] = False
member b (a:x) = (a == b) || member b x
๋ฌดํ ๋ฆฌ์คํธ์์ ํน์ ๊ฐ์ด ์กด์ฌํ๋์ง ์์ ํ๊ฒ ๊ฒ์ฌํ๋ ๋ฐฉ๋ฒ: ๋ฆฌ์คํธ๋ฅผ ์์์๋ถํฐ ์ํํ๋ฉฐ, b์ ๊ฐ์ ์์๊ฐ ์์ผ๋ฉด True ๋ฐํ. ๊ทธ๋ฌ๋ ๋ฌดํ ๋ฆฌ์คํธ์ ๋ํด ์ฌ์ฉํ๋ฉด ์ํํจ โ ์: member 5 [n*n | n <- [0..]]์์ 5๋ ์์ผ๋ฏ๋ก ๋์์ด ํ์ํจ
member2 n (m:x)
| m < n = member2 n x
| m == n = True
| otherwise = False
- ๋ฆฌ์คํธ
(m:x)๋ ์ ๋ ฌ๋ ์ํ๋ผ๊ณ ๊ฐ์ (์: [0ยฒ, 1ยฒ, 2ยฒ, ...]) - m < n์ด๋ฉด ๊ณ์ ํ์
- m == n์ด๋ฉด True
- m > n์ด๋ฉด ๋ ์ด์ ์ฐพ์ ํ์ ์์ โ False ์ฆ์ ๋ฐํ
- ๋ถํ์ํ ํ๊ฐ ์์ด ๋น ๋ฅด๊ฒ ์ข ๋ฃ ๊ฐ๋ฅ
- ๋ฌดํ ๋ฆฌ์คํธ์ ๋ํด์๋ ์์ ํ๊ฒ ๋์
squares = [n * n | n <- [0..]]
member2 16 squares -- True
member2 5 squares -- False (๋น ๋ฅด๊ฒ ์ข
๋ฃ)
F#
- Based on Ocaml, which is a descendant of ML and Haskell
- Fundamentally a functional language, but with imperative features (mutable, loop) and supports OOP
- Has a full-featured IDE, an extensive library of utilities
- Interoperates with other .NET languages
- Free from Microsoft and supported by Visual Studio
- Interoperability between C# and F# is quite easy
- Compared to C#:
- Simple to write code and easy to maintain
- Immutability
- Function itself is a first-class object
- Supports lazy evaluation
let binding
๊ฐ์ ์ ์(๋ฐ์ธ๋ฉ)ํ๊ฑฐ๋ ํจ์๋ฅผ ์ ์ธํ ๋ ์ฌ์ฉ
let square x = x * x // function for square
let output = square 5 // 25
let a = 7 // 7
- Mutable function is possible
let mutable mutableX = 5
mutableX <- mutableX + 1 // 6, <- is assignment operator
- It should be carefully considered to use mutable because it breaks functional programming paradigm โ Usually, it is possible to implement program without mutable
- Similar to Haskell: Statically typed, strongly typed, type inferencing, pattern matching
- Primitive types:
bool,byte,int,float,double,char,unit(no value, ๊ฐ์ด ์์) - Includes a variety of data types: Tuples, lists, discriminated unions, records, and mutable and immutable arrays
Tuple
- Group of values of one or more types. ๋ถ๋ณ(immutable) ๋ฐ์ดํฐ ๊ตฌ์กฐ
- Useful for getting multiple inputs and returning outputs
(1,2,3)
("one", 2, a)
- Assessing each element
let (a, b, c) = (1, 2, 3) // a = 1, b = 2, c = 3
fst์ snd๋ 2-ํํ ์ ์ฉ ํจ์
let c = fst (1, 2, 3) // c = 1
let d = snd (1, 2, 3) // d = 2
3๊ฐ ์ด์์ ํํ์์๋ ์ง์ ํจํด ๋งค์นญ์ด ํ์ํจ
let third (_, _, c) = c // Function that returns the third element
third (1, 2, 3) // ๊ฒฐ๊ณผ: 3
Struct tuple
- Value type tuple (<-> reference type)
- Stored in the stack, not the heap (๋ ์ฑ๋ฅ์ด ์ฐ์ํ ์ ์์)
let str = struct (1, 2, 3) // ์ ์ธ
let struct (a, b) = struct (1, 2) // a = 1, b = 2, ๊ตฌ์กฐ ๋ถํด
structํค์๋๋ฅผ ํํ ์์ ๋ถ์ฌ ์ ์ธ- ๋ถํดํ ๋๋ ๋ฐ๋์
structํค์๋ ์ฌ์ฉํด์ผ ํจ - Implicit conversion is not possible (struct tuple๊ณผ ์ผ๋ฐ tuple ๊ฐ์ ์๋ฌต์ ๋ณํ์ ๋ถ๊ฐ๋ฅ)
let (a, b) = struct (1, 2) // ์ค๋ฅ: ๊ตฌ์กฐ ๋ถํด๋ struct๋ก ํด์ผ ํจ
let struct (c, d) = (1, 2) // ์ค๋ฅ: ์ผ๋ฐ ํํ์ struct ๋ถํด ๋ถ๊ฐ
- When we need?
- Improve performance when the number of elements in the tuple to be used is large or individual elements have complex types
- Interop with C#
- Supports generic sequences, whose values can be created with generators and through iteration
- from the .NET namespace
System.Collections.Generic.IEnumerable seq<'T>๋ .NET์System.Collections.Generic.IEnumerable<T>์ F# ํํ- ์ฆ, ์ผ๋ฐํ๋ ์ํ์ค ํ์ โ ๋ฆฌ์คํธ, ๋ฐฐ์ด, ์งํฉ ๋ฑ๊ณผ ์ ์ฌํ์ง๋ง ์ง์ฐ ํ๊ฐ(lazy evaluation)๊ฐ ๊ฐ๋ฅํจ
- from the .NET namespace
List
A List is an ordered, immutable collection of elements of the same type
- Internally implemented as a Singly linked list
let x1 = [] // empty list
let x2 = [ 1; 2; 3; ] // list with elements 1, 2, and 3
let x3 = [ 1 .. 1000 ] // List with integers from 1 to 1000 as elements
// CONS โ ::
let x = 0 :: [ 1; 2; 3; ] // [ 0; 1; 2; 3; ]
// LIST โ @
let x = [ 1; 2; 3; ] @ [ 4; 5; 6; ] // [ 1; 2; 3; 4; 5; 6; ] ๋ฆฌ์คํธ ์ฐ๊ฒฐ (append)
// CAR โ Head
let y = x.Head // 1
// CDR โ Tail
let y = x.Tail // [ 2; 3; ]
Array
- Fixed-sized collection of mutable elements of the same type
- Supports fast random access. .NET์
System.Array์ ๊ธฐ๋ฐํจ
let x1 = Array.empty // empty array
let x2 = [| 1; 2; 3; |] // Array with elements 1, 2, and 3
let x3: int array = Array.zeroCreate 10 // array with 10 zeros
let x = [| 1; 2; 3; 4; 5; |]
let ele0 = array1[0] // a = 1
let subx1 = array1[2..4] // array2 = [| 3; 4; 5; |]
let subx2 = array1[..1] // array3 = [| 1; 2; |] slicing
let subx3 = array1[3..] // array4 = [| 4; 5; |] slicing
- Slicing copy and return a new array (not change original array)
Sequences
A collection of elements of the same type, which support lazy evaluation
- Generated with range expressions
let x = seq {1..4};; // generates seq [1; 2; 3; 4], ;; is used in interactive mode
let y = seq {0..10000000};; // only the needed elements are generated
- Generation of sequence values is lazy
| ํญ๋ชฉ | List | Array | Sequence (seq) |
|---|---|---|---|
| ๊ฐ๋ณ์ฑ | ๋ถ๋ณ (Immutable) | ๊ฐ๋ณ (Mutable) | ๋ถ๋ณ (์ฝ๊ธฐ ์ ์ฉ) |
| ํ๊ฐ | ์ฆ์ ํ๊ฐ (Eager) | ์ฆ์ ํ๊ฐ (Eager) | ์ง์ฐ ํ๊ฐ (Lazy) |
| ๊ตฌ์กฐ | ์ฐ๊ฒฐ ๋ฆฌ์คํธ | ์ฐ์๋ ๋ฉ๋ชจ๋ฆฌ ๋ฐฐ์ด | ๋ฐ๋ณต์ (IEnumerable) ๊ธฐ๋ฐ |
| ์ ๊ทผ | ์์ชฝ๋ง ๋น ๋ฆ | ์ธ๋ฑ์ค ์ ๊ทผ ๋น ๋ฆ (O(1)) | ์์ฐจ ์ ๊ทผ๋ง ๊ฐ๋ฅ |
| ๋ฌดํ ์ง์ | ๋ถ๊ฐ | ๋ถ๊ฐ | ๊ฐ๋ฅ (ํ์ํ ๋๋ง ์์ฑ๋จ) |
- Default stepsize is 1, but it can be any number
let seq1 = seq {1..2..7}
// Sets seq1 to [1; 3; 5; 7]
- Iterated with a for-in construct
let seq1 = seq {0..3..11};;
for value in seq1 do printfn "value = %d" value;;
// value = 0
// value = 3
// value = 6
// value = 9
- Iterators โ not lazy for lists and arrays (sequences are lazy)
let cubes = seq {for i in 1..4 -> (i, i * i * i)};;
// Sets cubes to [(1, 1); (2, 8); (3, 27); (4, 64)]
Functions
- If named, defined with
let
let x = a / b // โ ๋งค๊ฐ๋ณ์ ์๋ ํจ์์ฒ๋ผ ์๋
- No difference between a name defined with
letand a function without parameters - The extent of a function is defined by indentation. ํจ์ ๋ด๋ถ์์๋
let์ ์ค์ฒฉ ์ฌ์ฉ ๊ฐ๋ฅ
let f =
let pi = 3.14159
let twoPi = 2.0 * pi;; // 2 instead of 2.0 causes error
twoPi;;
- Does not coerce numeric values (์์น ์ฐ์ฐ ์ ์ ์/์ค์ ํ์ ๋ช ํํ ์ง์ ํด์ผ ํจ)
- Names in functions can be outscoped, which ends their scope
let์ผ๋ก ๊ฐ์ ์ด๋ฆ์ ๋ค์ ์ ์ํ๋ฉด ์ด์ ๋ณ์์ ์ค์ฝํ๊ฐ ์ข
๋ฃ๋จ
let x4 =
let x = x * x
let x = x * x
x;;
- The first
letin the body of the function creates a new version of x; This terminates the scope of the parameter - The second
letin the body creates another x, Terminating the scope of the second x - If lambda expressions, defined with
fun
fun a b -> a / b
fun x y -> let swap (a, b) = (b, a) in swap (x, y) // ๋๋ค ์์์ ๋ด๋ถ ํจ์ ์ ์
fun a b -> expr๋ ์ต๋ช ํจ์์ด๋ฉฐ,let์์ด๋ ์ ์ ๊ฐ๋ฅ- ๋๋ค ๋ด๋ถ์์๋
let์ผ๋ก ์ง์ญ ํจ์ ์ ์ ๊ฐ๋ฅ - If a function is recursive, its definition must include the
recreserved word
F#์์ ๊ธฐ๋ณธ์ ์ผ๋ก ํจ์ ์ด๋ฆ์ ๋ด๋ถ์์ ์ ๊ทผ ๋ถ๊ฐ (์๊ธฐ ์ฐธ์กฐ ๊ธ์ง)
let rec factorial x =
if x <= 1 then 1
else n * factorial(n โ 1)
์ฌ๊ท ํจ์์ ํจํด ๋งค์นญ
let rec factorial n =
match n with
| 0 -> 1
| 1 -> 1
| _ -> n * factorial (n - 1)
recํ์ โ ์ฌ๊ท ๊ฐ๋ฅmatch ... withโ ๋ค์ค ๋ถ๊ธฐ ์ ๊ฐ๋ ์ฑ ๋ฐ ์์ ์ฑ ํฅ์_โ ๊ธฐ๋ณธ๊ฐ ๋๋ "๊ทธ ์ธ ๋ชจ๋ ๊ฒฝ์ฐ" ์ฒ๋ฆฌ- Currying is supported. If a function has multiple arguments, it is automatically treated as currying
F#์ ๊ธฐ๋ณธ์ ์ผ๋ก ํจ์์ ์ธ์๊ฐ ์ฌ๋ฌ ๊ฐ ์์ด๋ ์๋์ผ๋ก ์ปค๋ง ์ฒ๋ฆฌํจ
ํํ (a, b)์ ํ๋์ ์ธ์๋ก ๋ฐ์ผ๋ฉด ์ปค๋ง์ด ๋์ง ์์
let add (a:int) (b:int) = a + b
let add5 = add 5
let result = add5 3 // 8
// If want to prevent currying - Use tuple
let add1 a b = a + b
let add2 (a, b) = a + b
inline
inline ํค์๋์ ๋คํ์ฑ(polymorphism)์ ์ด์ฉํด ํจ์ ์ค๋ฒ๋ก๋ฉ์ ํ๋ด๋ผ ์ ์์
์ปดํ์ผ ํ์์ ํจ์ ๋ณธ๋ฌธ์ด ์ง์ ์ฝ์ (inlined)๋จ. ๋๋ถ์ ์ ๋ค๋ฆญ ํ์ ์ฐ์ฐ์ (+, * ๋ฑ)๊ฐ ํ์ ์ถ๋ก ์ ํตํด ๋ค์ํ ํ์ ์์ ์ฌ์ฉ ๊ฐ๋ฅํด์ง
let add1 a b = a + b
let result1 = add1 1 2
let result2 = add1 "dog" "cat" // error! ๋ฌธ์์ด + ๋ ์ง์ ์๋จ
let inline add2 a b = a + b
let result3 = add2 1 2 // 3
let result4 = add2 "dog" "cat" // "dogcat" ํ์
์ถ๋ก ๊ธฐ๋ฐ ์ฐ์ฐ ํ์ฉ โ ์ค๋ฒ๋ก๋ฉ์ฒ๋ผ ๋์ ๊ฐ๋ฅ
- Can mimic function overloading
Functions (collection)
iter: ์ฃผ์ด์ง ํจ์๋ฅผ ๋ฆฌ์คํธ์ ๊ฐ ์์์ ๋ํด ์คํ (๋ฐํ๊ฐ ์์)
let l1 = [1; 2; 3]
List.iter (fun x -> printf "%d " x) l1
List.iteri (fun i x -> printfn "Index %d: %d " i x) l1
map: ๋ฆฌ์คํธ์ ๊ฐ ์์์ ํจ์๋ฅผ ์ ์ฉํ๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ ๋ฆฌ์คํธ๋ก ๋ฐํ
let l1 = [1; 2; 3]
let r1 = List.map (fun x -> x + 1) l1 // [2; 3; 4]
let r2 = List.mapi (fun i x -> x + i) l1 // [1; 3; 5]
filter: ์ฃผ์ด์ง ์กฐ๊ฑด ํจ์(pred)๋ฅผ ๊ฐ ์์์ ์ ์ฉํ๊ณ , ๊ฒฐ๊ณผ๊ฐ true์ธ ์์๋ง ์ถ์ถ
let l1 = [1; 2; 3; 4]
let evenList = List.filter (fun x -> x % 2 = 0) l1 // [2; 4]
fold: ๋ฆฌ์คํธ์ ๊ฐ ์์๋ฅผ ๋์ ์(acc)์ ํจ๊ป ์ฒ๋ฆฌํ์ฌ ์ต์ข
๊ฒฐ๊ณผ ๊ณ์ฐ
- Results from the previous element are accumulated and applied. Need accumulator (need to set initial)
let sum list = List.fold (fun acc x -> acc + x) 0 list
let r1 = sum [1; 2; 3;] // 6 (์์: ((0 + 1) + 2) + 3)
let reverse list = List.fold (fun acc x -> x::acc) [] list
let r2 = reverse [1; 2; 3;] // [3; 2; 1]
// ์์: [] |> 1::[] โ [1] |> 2::[1] โ [2;1] |> 3::[2;1] โ [3;2;1]
do: When we need to do it without saving the result. ๋ฐํ๊ฐ์ ๋ฌด์๋จ (unit์ผ๋ก ์ฒ๋ฆฌ๋จ)
do printf "Hello World"
Function Composition
let negate x = -1 * x
let square x = x * x
let temp = square 3 // 9
let result = negate temp // -9
// Or:
let result = negate (square 3) // -9
- Composition (
>>): ํจ์ ์กฐํฉ ์ฐ์ฐ์ - Builds a function that applies its left operand to a given parameter (a function) and then passes the result returned from the function to its right operand (another function)
- The F# expression
(f >> g) xis equivalent to the mathematical expressiong(f(x))
let composed = square >> negate
let result = composed 3 // ๊ฒฐ๊ณผ: -9 (square 3 = 9 โ negate 9 = -9)
let inline (>>) f g x = g (f x) // Forward composition
let inline (<<) f g x = f (g x) // Reverse composition
let negateSquare = square >> negate
let result = negateSquare 3 // -9
Pipeline
let even values = List.filter (fun n -> n % 2 = 0) values
let mulFive values = List.map (fun n -> 5 * n) values
let combine1 values =
let evens = List.filter (fun n -> n % 2 = 0) values
let mulFives = List.map (fun n -> 5 * n) evens
mulFives
- In fact, if it is clear that the previous result value is to be passed to value name in the next function, this way would be an unnecessary syntax
- Pipeline (
|>): A binary operator that sends the value of its left operand to the last parameter of the call (the right operand)
let inline (|>) x f = f x
let inline (<|) f x = f x
- Used to chain function calls together
let myNums = [1; 2; 3; 4; 5]
let evensTimesFive = myNums
|> List.filter (fun n -> n % 2 = 0)
|> List.map (fun n -> 5 * n)
// The return value is [10; 20]
Insert sort
let rec insert n ls =
match ls with
| [] -> [n]
| x::xs ->
if n > x then x :: insert n xs
else n :: ls;;
let rec insertSort ls =
match ls with
| [] -> []
| x::xs -> insert x (insertSort xs);;
// insertSort [3; 1; 4; 2];;
// ๊ฒฐ๊ณผ: [1; 2; 3; 4]
Quicksort
let rec quicksort list =
match list with
| [] -> []
| firstElem::otherElements ->
let smallerElements =
otherElements
|> List.filter (fun e -> e < firstElem)
|> quicksort
let largerElements =
otherElements
|> List.filter (fun e -> e >= firstElem)
|> quicksort
smallerElements @ [firstElem] @ largerElements
| ๋ถ๋ถ | ์๋ฏธ |
|---|---|
[] | ์ ๋ ฅ ๋ฆฌ์คํธ๊ฐ ๋น ๊ฒฝ์ฐ ์ข ๋ฃ์กฐ๊ฑด |
firstElem::otherElements | ๋ฆฌ์คํธ๋ฅผ ํผ๋ฒ๊ณผ ๋๋จธ์ง๋ก ๋ถํด |
List.filter (fun e -> e < firstElem) | ํผ๋ฒ๋ณด๋ค ์์ ์์ ์ถ์ถ |
List.filter (fun e -> e >= firstElem) | ํผ๋ฒ๋ณด๋ค ํฌ๊ฑฐ๋ ๊ฐ์ ์์ ์ถ์ถ |
|> quicksort | ์ฌ๊ท ํธ์ถ, ๊ฐ ๋ถ๋ถ์ ์ ๋ ฌ |
@ | ๋ฆฌ์คํธ ๊ฒฐํฉ (์ข: ์์ ์์๋ค + ํผ๋ฒ + ์ฐ: ํฐ ์์๋ค) |
Why F# is Interesting
- It builds on previous functional languages (ML, Haskell, OCaml ๋ฑ ๊ธฐ์กด ํจ์ํ ์ธ์ด๋ค์ ์ด๋ก ์ /์ค์ฉ์ ๊ธฐ๋ฐ ์์ ์ค๊ณ๋จ)
- It supports virtually all programming methodologies in widespread use today (ํจ์ํ๋ฟ ์๋๋ผ ๋ช ๋ นํ, ๊ฐ์ฒด์งํฅ, ๋ฐ์ดํฐ ํ๋ฆ ๊ธฐ๋ฐ ํ๋ก๊ทธ๋๋ฐ ๋ฑ ์ฌ๋ฌ ๋ฐฉ์์ ๊ฐ๋ฐ์ ๋ชจ๋ ์ง์ํจ)
- It is the first functional language that is designed for interoperability with other widely used languages (๋๋ฆฌ ์ฌ์ฉ๋๋ ์ธ์ด(C#, VB.NET ๋ฑ)๋ค๊ณผ์ .NET ๊ธฐ๋ฐ ์ํธ์ด์ฉ์ฑ์ด ๋ฐ์ด๋จ. ์ฆ, F# ์ฝ๋์ C# ์ฝ๋๋ฅผ ์์ด์ ์ฌ์ฉํ ์ ์์)
- At its release, it had an elaborate and well developed IDE and library of utility software (Visual Studio IDE ๋ฐ ๋ค์ํ ์ ํธ๋ฆฌํฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํจ๊ป ์ถ์๋์ด ์ค๋ฌด์ ๋ฐ๋ก ์ฌ์ฉํ ์ ์๋ ํ๊ฒฝ์ ์ ๊ณตํจ)
Support for Functional Programming in Primarily Imperative Languages
- Support for functional programming is increasingly creeping into imperative languages
- ์์: ๋๋ค ํํ์, ๋ถ๋ณ ๋ณ์, ์คํธ๋ฆผ API ๋ฑ์ FP์ ๊ฐ๋ ์ ์ฐจ์ฉํ ๊ฒ์
- The most important restriction: Lack of support for higher-order functions
- ๊ณ ์ฐจ ํจ์๋, ํจ์๋ฅผ ์ธ์๋ก ๋ฐ๊ฑฐ๋ ํจ์๋ก ๋ฐํํ๋ ํจ์๋ฅผ ์๋ฏธ
- ์ด ์ ์ฝ ๋๋ฌธ์ ์ง์ ํ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ์คํ์ผ์ ๊ตฌํํ๊ธฐ ์ด๋ ต๋ค๋ ํ๊ณ๊ฐ ์์
- Although modern imperative languages support many functional features, they still primarily rely on mutable state and side effects
Anonymous functions (lambda expressions)
ํจ์์ ์ด๋ฆ์ ๋ถ์ด์ง ์๊ณ ์ ์ํ๋ ๋ฐฉ์ โ ์ผํ์ฑ ์ฌ์ฉ์ด๋ ๊ณ ์ฐจ ํจ์ ์ธ์๋ก ๋๊ธธ ๋ ์ ์ฉํจ
- JavaScript: leave the name out of a function definition
function name (formal-parameters) {
body
}
const square = function(x) {
return x * x;
};
// function(x) { return x * x; } โ ์ด๋ฆ์ด ์๋ ํจ์ ์ ์
C#: (input-parameters) => expression
// i => (i % 2) == 0 (returns true or false depending on whether the parameter is even or odd)
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
var evenNumbers = numbers.Where(i => i % 2 == 0).ToList();
// ๊ฒฐ๊ณผ: [2, 4]
// ์ฌ๊ธฐ์ Where๋ ๊ณ ์ฐจ ํจ์ (ํจ์๋ฅผ ์ธ์๋ก ๋ฐ๋ ํจ์), i => i % 2 == 0๋ ์ต๋ช
ํจ์ (๋๋ค)
- Python:
lambda ๋งค๊ฐ๋ณ์๋ค : ํํ์
f = lambda a, b: 2 * a - b
print(f(3, 1)) # ์ถ๋ ฅ: 5
nums = [1, 2, 3, 4]
squares = list(map(lambda x: x * x, nums))
print(squares) # ์ถ๋ ฅ: [1, 4, 9, 16]
- Python supports the higher-order functions
filterandmap(often use lambda expressions as their first parameters)filter(function, iterable): ์ฃผ์ด์ง ํจ์์ ์กฐ๊ฑด์ ๋ง์กฑํ๋ ์์๋ง ๋จ๊น
list(filter(lambda x: x % 2 == 0, [1, 2, 3, 4, 5]))
# Returns [2, 4]
map: iterable์ ๋ชจ๋ ์์์ ํจ์๋ฅผ ์ ์ฉํ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํ
map(lambda x : x ** 3, [2, 4, 6, 8])
# Returns [8, 64, 216, 512]
์ด๋ฐ ๊ณ ์ฐจ ํจ์๋ ํจ์๋ฅผ ๊ฐ์ฒ๋ผ ๋ค๋ฃฐ ์ ์๋ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ํน์ง
- Python supports partial function applications: ์ฌ๋ฌ ๊ฐ์ ์ธ์๋ฅผ ๋ฐ๋ ํจ์ ์ค ์ผ๋ถ ์ธ์๋ฅผ ๋ฏธ๋ฆฌ ๊ณ ์ ํด ๋๊ณ , ๋๋จธ์ง ์ธ์๋ง ๋์ค์ ๋ฐ์์ ํธ์ถํ๋ ๋ฐฉ์
- There are libraries for functional programming:
functools,itertools,operator
from functools import partial
def add (x, y, z):
return x + y + z
add5 = partial(add, 5)
add5(15) # return 20
from operator import add
add5 = partial(add, 5)
add5(15) # return 20
def multiply(x, y):
return x * y
double = partial(multiply, 2) # x๋ฅผ 2๋ก ๊ณ ์ (2๋ฐฐ ํจ์)
numbers = [1, 2, 3, 4]
doubled = list(map(double, numbers)) # [2, 4, 6, 8]
print(doubled)
strings = ['apple', 'banana', 'kiwi', 'grape']
print(sorted(strings)) # ['apple', 'banana', 'grape', 'kiwi']
sort_by_length = partial(sorted, key=len)
print(sort_by_length(strings)) # ['kiwi', 'apple', 'grape', 'banana']
| ํจ์ | ์ค๋ช | partial ํ์ฉ |
|---|---|---|
map | ๊ฐ ์์์ ํจ์ ์ ์ฉ | ์ธ์ ์ผ๋ถ๋ฅผ ๊ณ ์ ํ ํจ์ ์ ๋ฌ |
sorted | ์ ๋ ฌ ๊ธฐ์ค์ ์ ํจ | key ์ธ์๋ฅผ ๊ณ ์ |
Ruby Blocks
- Are effectively subprograms that are sent to methods, which makes the method a higher order subprogram
- Ruby์์๋ ๋ฉ์๋๊ฐ block์ ๋ฐ์ผ๋ฉด, ๊ทธ block์ ์คํํ ์ ์์. ์ฆ, ํด๋น ๋ฉ์๋๋ ๊ณ ์ฐจ ํจ์(higher-order function)๋ก ์๋
- We can create blocks with
{ }ordo ~ end. Block can be passed to method
3.times { |i| puts i }
3.times do |i|
x = i * 2
p x
end
์ ์ฝ๋์์ times ๋ฉ์๋๋ ๋ธ๋ก์ ๋ฐ์์, i๋ฅผ 0๋ถํฐ 2๊น์ง ๋๊ฒจ์ฃผ๊ณ , ๋ธ๋ก ๋ด ์ฝ๋๋ฅผ ์คํ
- Block์ ์ฌ์ฉํ๋ ์ด์ : ๋ฐ๋ณต ์ ์ด๋ฅผ ๊ฐ๊ฒฐํ๊ฒ ์ฒ๋ฆฌ, ์ฝ๋ฐฑ ์ฒ๋ฆฌ
def custom_each(array)
for item in array
yield(item) # ๋๊ฒจ๋ฐ์ block ์คํ
end
end
custom_each([1, 2, 3]) do |n|
puts n * 10
end
# ์ถ๋ ฅ: 10, 20, 30
์ฌ๊ธฐ์ yield๋ ๋ฉ์๋ ์์์ block์ ํธ์ถํ๋ ํค์๋. block์ ๋ฉ์๋ ์ธ๋ถ์์ ์ ์๋์ง๋ง, ๋ง์น ๋ฉ์๋ ์์ ์๋ ๊ฒ์ฒ๋ผ ์คํ
A block can be converted to a subprogram object with lambda
times = lambda {|a, b| a * b}
x = times.(3, 4) # sets x to 12
times5 = times.curry.(5) # ์ฒซ ๋ฒ์งธ ์ธ์๋ฅผ 5๋ก ๊ณ ์ ํ ์๋ก์ด ํจ์
x5 = times5.(3) # sets x5 to 15 (๋ ๋ฒ์งธ ์ธ์๋ง ์ฃผ๋ฉด ๊ฒฐ๊ณผ ๊ณ์ฐ๋จ)
Comparing Functional and Imperative Languages
- Imperative Languages:
- Complex semantics (์ํ ๋ณํ, ์ ์ด ํ๋ฆ ์ค์ฌ)
- Complex syntax (๋ฐ๋ณต๋ฌธ, ๋์ ๋ฌธ, ์ฝ๋ ๋ธ๋ก ๋ฑ)
- Efficient execution (์ผ๋ฐ์ ์ผ๋ก ๋ ํจ์จ์ ์)
- Concurrency is programmer designed (ํ๋ก๊ทธ๋๋จธ๊ฐ ์ง์ ๋ณ๋ ฌ ์ฒ๋ฆฌ ์ค๊ณ)
- ๊ฐ๋ณ ์ํ(mutable state) ์ฌ์ฉ
- ๋ถ์ ํจ๊ณผ ๋ง์ (๋์ , ์ ์ถ๋ ฅ ๋ฑ)
- Functional Languages:
- Simple semantics (์ํ ์๋ ๊ณ์ฐ, ํํ์ ์ค์ฌ)
- Simple syntax (ํจ์ ์ค์ฌ, ํํ์ ๊ธฐ๋ฐ)
- Less efficient execution (์๋์ ์ผ๋ก ๋ ํจ์จ์ ์ (๋ถ๋ณ์ฑ, ์ฌ๊ท ๋ฑ์ผ๋ก ์ธํด))
- Programs can automatically be made concurrent (์๋ ๋ณ๋ ฌํ ๊ฐ๋ฅ์ฑ์ด ๋์)
- ๋ถ๋ณ ์ํ(immutable state) ์งํฅ
- ๋ถ์ ํจ๊ณผ๋ฅผ ํผํจ (์์ ํจ์ ์ฌ์ฉ)
Summary
- Functional programming languages use function application, conditional expressions, recursion, and functional forms to control program execution
- Lisp began as a purely functional language and later included imperative features
- Scheme is a relatively simple dialect of Lisp that uses static scoping exclusively
- Common Lisp is a large Lisp-based language
- ML is a static-scoped and strongly typed functional language that uses type inference
- Haskell is a lazy functional language supporting infinite lists and set comprehension
- F# is a .NET functional language that also supports imperative and object-oriented programming
- Some primarily imperative languages now incorporate some support for functional programming
- Purely functional languages have advantages over imperative alternatives, but still are not very widely used

