• Mindscape ๐Ÿ”ฅ
    • Playlist ๐ŸŽง
  • Algorithm

    • 1018๋ฒˆ: ์ฒด์ŠคํŒ ๋‹ค์‹œ ์น ํ•˜๊ธฐ
    • 1966๋ฒˆ: ํ”„๋ฆฐํ„ฐ ํ
    • Python ์‹œ๊ฐ„ ์ดˆ๊ณผ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•œ ํŒ
    • C++ std::vector ์‚ฌ์šฉ๋ฒ• ์ •๋ฆฌ
    • Vim ์‚ฌ์šฉ ๋งค๋‰ด์–ผ
  • Ubuntu

    • ๋ฆฌ๋ˆ…์Šค ์šฐ๋ถ„ํˆฌ GRUB ํฐํŠธ ๋ณ€๊ฒฝ
    • ์šฐ๋ถ„ํˆฌ ์ด๋ฏธ์ง€ ๋น„๋””์˜ค ์ธ๋„ค์ผ(๋ฏธ๋ฆฌ๋ณด๊ธฐ) ์•ˆ ๋ณด์ž„ ๋ฌธ์ œ ํ•ด๊ฒฐ
    • Wine ํ™˜๊ฒฝ์—์„œ ์นด์นด์˜คํ†ก ์‹คํ–‰ ์‹œ explorer.exe ๋œจ์ง€ ์•Š๊ฒŒ ํ•˜๋Š” ๋ฒ•
    • ์šฐ๋ถ„ํˆฌ Wine ์นด์นด์˜คํ†ก ์‚ฌ์ง„ ์ด๋ฏธ์ง€ ์Šคํฌ๋ฆฐ์ƒท ๋ถ™์—ฌ๋„ฃ๊ธฐ
    • Wine ์นด์นด์˜คํ†ก ์ด๋ชจ์ง€ ๊นจ์ง ๋ฌธ์ œ ํ•ด๊ฒฐ
    • Ubuntu ์œˆ๋„์šฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋„๊ธฐ
  • Wellness

    • ์ฐจ์ „์žํ”ผ (Psyllium Husk)
    • ์—‘์ŠคํŠธ๋ผ ๋ฒ„์ง„ ์˜ฌ๋ฆฌ๋ธŒ์œ  (Extra Virgin Olive Oil)
    • ์ž๊ฐ€๋น„๊ฐ•์„ธ์ฒ™ (Nasal Irrigation)
    • QCY HT08 (MeloBuds Pro Plus)
    • ์ฝ˜์„œํƒ€ (Concerta)
    • ์ธ๋ฐ๋†€ (Inderal)
    • ์„คํŠธ๋ž„๋ฆฐ (Sertraline)
    • ๋ฉœ๋ผํ† ๋‹Œ (Melatonin)
    • ์น˜๊ฒฝ๋ถ€ ๋งˆ๋ชจ์ฆ
    • ๋ฐ”๋ฒจ ์Šค์ฟผํŠธ (Barbell Squat)
  • Humanities

    • Nordvik, Russia
    • North Sentinel Island
    • ๋กฑ๊ณ ๋กฑ๊ณ (Rongorongo)
    • ๋ฐ”๋กœํฌ ์Œ์•… (Baroque Music)
  • Design

    • ๊ตฌ๊ธ€์˜ ์•„์ด์ฝ˜ ๋Œ€๊ฐœํŽธ โ€” 6๋…„ ๋งŒ์˜ ์‹ค์ˆ˜ ์ธ์ •
    • ์ œ๋Ÿด๋“œ ์  ํƒ€ โ€” ๋Ÿญ์…”๋ฆฌ ์Šคํฌ์ธ  ์›Œ์น˜์˜ ์ฐฝ์‹œ์ž
    • ๋ฐ”์šฐํ•˜์šฐ์Šค โ€” ํ˜„๋Œ€ ๋””์ž์ธ์˜ ์›์ 
  • Brands

    • NOMOS Glashรผtte
    • Frรฉdรฉrique Constant
    • KZ (Knowledge Zenith)
    • ์—์ŠคํŠธ๋ผ (AESTURA)
    • JINHAO (้‡‘่ฑช)
    • Herman Miller
    • ๋ฐ์Šค์ปค (DESKER)
    • ๋ฌด์‹ ์‚ฌ ์Šคํƒ ๋‹ค๋“œ (Musinsa Standard)
  • Finance

    • ํ˜„๋Œ€์นด๋“œ ZERO โ€” Edition2 vs Edition3 ๋น„๊ต
    • ์‹ ํ•œ์นด๋“œ ์ฒ˜์Œ
    • S&P 500 ETF ํˆฌ์ž ๊ฐ€์ด๋“œ
    • ํŒŒํ‚นํ†ต์žฅ vs CMA ํ†ต์žฅ
    • ๋ฒ„ํฌ์…” ํ•ด์„œ์›จ์ด (Berkshire Hathaway)
    • ๋น„ํŠธ์ฝ”์ธ(Bitcoin)
  • Products

    • ์˜ค๋””์˜ค ์ธํ„ฐํŽ˜์ด์Šค (Audio Interface)
    • ์ฟ ๋ฃจํ† ๊ฐ€ (KURUTOGA)
    • CX31993 DAC ๋™๊ธ€
    • ํด๋ Œ์ง• ๋ฐ€ํฌ (Cleansing Milk)
    • ํ”ผ์ ฏ ํ† ์ด (Fidget Toy)
    • ThinkPad
  • Programming Languages

    • 8.0. Statement Level Control Structures
    • 8. Subprogram
    • 9. Implementing Subprogram
    • 10.1. Abstract Data Types and Encapsulation Constructs
    • 10.2. Support for Object Oriented Programming
    • 11. Concurrency
    • 12. FPL (1)
    • 13. FPL (2)
    • 14. Exception Handling and Event Handling
    • Final Exam

13. FPL (2)

์ž‘์„ฑ 2026. 6. 12.ยท์ˆ˜์ • 2026. 6. 12.

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 โ†’ hd and tl are 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 val statement binds a name to a value (similar to DEFINE in Scheme)
val distance = time * speed;
  • As is the case with DEFINE, val is 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 f and then applies function g to the returned value from f
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

MLHaskell
๋ณ€์ˆ˜ ์„ ์–ธval x = 3;x = 3
๋ฐ”์ธ๋”ฉ ํŠน์„ฑ๋ณ€์ˆ˜ x๋Š” ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€์ด์ง€๋งŒ, val์„ ๋ฐ˜๋ณตํ•ด์„œ ์ƒˆ ์ด๋ฆ„ ์ง€์ • ๊ฐ€๋Šฅx๋Š” ์ƒ์ˆ˜์ฒ˜๋Ÿผ ์ทจ๊ธ‰๋จ (๋ณ€๊ฒฝ ๋ถˆ๊ฐ€), =๋Š” ์ˆ˜ํ•™์  ์ •์˜๋กœ ์‚ฌ์šฉ๋จ (๋Œ€์ž… ์•„๋‹˜)

Basic operations

  • head: 0th element
  • head [2, 3, 4] -- 2
  • tail: All elements except element 0
  • tail [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 element
  • last [2, 3, 4] -- 4
  • take: get elements from front
  • take 2 [2, 3, 4] -- [2, 3]
  • drop: remove elements from front
  • drop 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
[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
ํ•ญ๋ชฉHaskellML (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 otherwise can 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
ํ•ญ๋ชฉHaskellML (SML)
์‹œ์ž‘ ํ‚ค์›Œ๋“œletlet + ๊ฐ ์ค„์— 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 ... inwhere ๊ตฌ๋ฌธ
์œ„์น˜ํ‘œํ˜„์‹ ๋‚ด๋ถ€ํ•จ์ˆ˜ ์ •์˜ ์•„๋ž˜
๋ฌธ๋ฒ• ๊ตฌ์กฐlet <๋ฐ”์ธ๋”ฉ> in <ํ‘œํ˜„์‹><ํ‘œํ˜„์‹> where <๋ฐ”์ธ๋”ฉ>
์Šค์ฝ”ํ”„in ๋’ค ํ‘œํ˜„์‹ ์•ˆ์—์„œ๋งŒ ์œ ํšจํ•ด๋‹น ํ•จ์ˆ˜ ์ •์˜ ์ „์ฒด์—์„œ ์œ ํšจ
์‚ฌ์šฉ ์˜ˆ์‹œlet x = ... in x + 1f 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]
  1. merge ํ•จ์ˆ˜ โ€“ ๋‘ ์ •๋ ฌ๋œ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ณ‘ํ•ฉ: ๋‘ ๋ฆฌ์ŠคํŠธ๊ฐ€ ์ •๋ ฌ๋œ ์ƒํƒœ๋กœ ๋“ค์–ด์˜ค๋ฉด, ์•ž์—์„œ๋ถ€ํ„ฐ ์ž‘์€ ๊ฐ’์„ ํ•˜๋‚˜์”ฉ ๋ฝ‘์•„์„œ ์ƒˆ ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ตฌ์„ฑ. x <= y์ผ ๋•Œ x๊ฐ€ ๋จผ์ € ์˜ค๊ณ , ์•„๋‹ˆ๋ผ๋ฉด y๊ฐ€ ๋จผ์ €
  2. halve ํ•จ์ˆ˜ โ€“ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฐ˜์œผ๋กœ ๋‚˜๋ˆ”: splitAt์€ ๋ฆฌ์ŠคํŠธ๋ฅผ ์ง€์ •ํ•œ ์ธ๋ฑ์Šค์—์„œ ๋‘˜๋กœ ๋‚˜๋ˆ”. length xs \div` 2`๋ฅผ ์ด์šฉํ•ด ๋ฐ˜์œผ๋กœ ์ชผ๊ฐœ๊ธฐ
  3. 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)๊ฐ€ ๊ฐ€๋Šฅํ•จ

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
ํ•ญ๋ชฉListArraySequence (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 let and 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 let in the body of the function creates a new version of x; This terminates the scope of the parameter
  • The second let in 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 rec reserved 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) x is equivalent to the mathematical expression g(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 filter and map (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 { } or do ~ 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
์ตœ๊ทผ ์ˆ˜์ •: 26. 6. 12. ์˜คํ›„ 3:28
Contributors: kmbzn, Claude Sonnet 4.6
Prev
12. FPL (1)
Next
14. Exception Handling and Event Handling

BUILT WITH

CloudflareNode.jsGitHubGitVue.jsJavaScriptVSCodenpm

All trademarks and logos are property of their respective owners.
ยฉ 2026 kmbzn ยท MIT License