Ver3.5 ๐ Clojure: LISP ๊ธฐ๋ฐ ํจ์ํ ์ธ์ด์๋ถ๋ณ ๋ฐ์ดํฐ ๊ตฌ์กฐ ์์ ์ ๋ณต ๊ฐ์ด๋

๐ Clojure: LISP ๊ธฐ๋ฐ ํจ์ํ ์ธ์ด์
๋ถ๋ณ ๋ฐ์ดํฐ ๊ตฌ์กฐ ์์ ์ ๋ณต ๊ฐ์ด๋
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ฒ ํ๋ถํฐ ์ค์ ํ์ฉ๊น์ง, ์ง์ง ์ธ๋ชจ์๋ ์ง์๋ง ๋ด์์ด์ โจ
์์งํ ๋งํ๋ฉด... ์ฒ์์ Clojure ๋ค์์ ๋ "์ด๊ฒ ๋ญ์ผ ใ
ใ
๊ดํธ ๋ฉ์ด๋ฆฌ ์๋์ผ?" ์ถ์๋ ์ฌ๋ ์ ๐
๊ทผ๋ฐ ํ๊ณ ๋ค๋ฉด ํ๊ณ ๋ค์๋ก ์ด ์ธ์ด๊ฐ ์ง์ง ๋ฏธ์ณค๋ค๋ ๊ฑธ ์๊ฒ ๋จใ
ใ
2007๋
์ Rich Hickey๋ผ๋ ๊ฐ๋ฐ์๊ฐ ๋ง๋ค์๋๋ฐ, ์ด ์ฌ๋์ด ๊ทธ๋ฅ ๋ง๋ ๊ฒ ์๋๋ผ 10๋
๋๊ฒ ์ค๋ฌด์์ ๊ฒช์ ๊ณ ํต์ ํด๊ฒฐํ๋ ค๊ณ ์ค๊ณํ ์ธ์ด์ผ.
ํนํ ๋ฉํฐ์ค๋ ๋ ํ๊ฒฝ์์ ๊ณต์ ์ํ(shared mutable state) ๋๋ฌธ์ ์๊ธฐ๋ ๋ฒ๊ทธ๋ค... ์ด๊ฑฐ ์ง์ง ๊ฐ๋ฐ์๋ค ๋ฐค์๊ฒ ๋ง๋๋ ์ฃผ๋ฒ์ด์์?
Clojure๋ ์ด ๋ฌธ์ ๋ฅผ ๋ถ๋ณ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ก ๊ทผ๋ณธ์ ์ผ๋ก ํด๊ฒฐํด๋ฒ๋ฆผ.
๊ทธ๋ฆฌ๊ณ JVM ์์์ ๋์๊ฐ๋๊น Java ์ํ๊ณ ๊ทธ๋๋ก ์ธ ์ ์๊ณ , ์ฑ๋ฅ๋ ๋ณด์ฅ๋จ.
์ด๊ฒ ์ ์ค์ํ๋๋ฉด... ์ค๋ฌด์์ "์ธ์ด๋ ์ข์๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์์ด์ ๋ชป ์ฐ๊ฒ ๋ค"๋ ์ํฉ์ด ์ผ๋ง๋ ๋ง์์ง ์์์ ๐
LISP(LISt Processing)์ 1958๋
MIT์ John McCarthy๊ฐ ๋ง๋ ์ธ์ด์ผ.
C์ธ์ด๋ณด๋ค๋ ์ค๋๋๊ณ , ์ฌ์ง์ด FORTRAN ๋ค์์ผ๋ก ์ค๋๋ ๊ณ ์์ค ์ธ์ด์.
๊ทผ๋ฐ ์ด๊ฒ ์์ง๋ ์ฐ์ด๊ณ , ์ฌ์ง์ด Clojure ๊ฐ์ ํ๋์ ์ธ ์ธ์ด์ ๊ธฐ๋ฐ์ด ๋๋ค๋ ๊ฒ ์ ๊ธฐํ์ง ์์? ๐ค
LISP์ด ์ด์๋จ์ ์ด์ ๋ ๋ฑ ํ๋์ผ: ํต์ฌ ์์ด๋์ด๊ฐ ๋๋ฌด ๊ฐ๋ ฅํด์.
LISP์ ํต์ฌ ๊ฐ๋
๋ค์ ๋ณด๋ฉด:
1 ํธ๋ชจ์ด์ฝ๋์ฑ(Homoiconicity) - ์ฝ๋์ ๋ฐ์ดํฐ๊ฐ ๊ฐ์ ํํ(S-ํํ์)๋ก ํํ๋จ
2 ์ผ๊ธ ํจ์(First-class Functions) - ํจ์๋ฅผ ๊ฐ์ฒ๋ผ ๋ค๋ฃฐ ์ ์์
3 ๊ฐ๋น์ง ์ปฌ๋ ์
- LISP์ด ์ต์ด๋ก ๋์
ํ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ๋ฐฉ์
4 ๋์ ํ์ดํ - ๋ฐํ์์ ํ์
๊ฒฐ์
5 ์ฌ๊ท(Recursion) - ๋ฐ๋ณต๋ณด๋ค ์ฌ๊ท๋ฅผ ํตํ ๋ฌธ์ ํด๊ฒฐ
6 ๋งคํฌ๋ก ์์คํ
- ์ธ์ด ์์ฒด๋ฅผ ํ์ฅํ ์ ์๋ ๋ฅ๋ ฅ
์ด ์ค์์ ๊ฐ๋น์ง ์ปฌ๋ ์
์ ์ง๊ธ Java, Python, JavaScript ๋ค ์ฐ๊ณ ์๊ณ ,
์ผ๊ธ ํจ์๋ JavaScript ES6+, Python, Kotlin ๋ค ์ฑํํ๊ณ ,
๋งคํฌ๋ก๋... ์ด๊ฑด ์์ง๋ LISP ๊ณ์ด ์ธ์ด๋ค๋ง ์ ๋๋ก ์ง์ํจ ใ
ใ
์ฆ, ํ๋ ์ธ์ด๋ค์ด LISP์์ ์์ด๋์ด๋ฅผ ๋น๋ ค๊ฐ ๊ฑฐ์ง, LISP์ด ํ๋ ์ธ์ด๋ฅผ ๋ฐ๋ผ๊ฐ ๊ฒ ์๋. ๋ ์ ๋ ในใ
Rich Hickey๋ Clojure๋ฅผ ๋ง๋ค๊ธฐ ์ ์ Java์ C++๋ก ์ค๋ซ๋์ ์ค๋ฌด ๊ฐ๋ฐ์ ํ์ด.
๊ทธ๊ฐ ๊ฐ์ฅ ํฌ๊ฒ ๋๋ ๋ฌธ์ ๋ "๊ฐ๋ณ ์ํ(mutable state)์ ๋์์ฑ(concurrency)์ ์กฐํฉ์ด ๋๋ฌด ์ํํ๋ค"๋ ๊ฑฐ์์.
์๊ฐํด๋ด. ๋ฉํฐ์ค๋ ๋ ํ๊ฒฝ์์ ์ฌ๋ฌ ์ค๋ ๋๊ฐ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋์์ ์์ ํ๋ฉด?
โ ๋ ์ด์ค ์ปจ๋์
(Race Condition) ๋ฐ์
โ ๋ฐ๋๋ฝ(Deadlock) ๋ฐ์
โ ๋๋ฒ๊น
์ง์ฅ ์์ ๐ฑ
Java์์๋ ์ด๊ฑธ synchronized, volatile, Lock ๊ฐ์ ๊ฑธ๋ก ํด๊ฒฐํ๋ ค ํ๋๋ฐ,
์ด๊ฒ ์ฝ๋๋ฅผ ์์ฒญ ๋ณต์กํ๊ฒ ๋ง๋ค๊ณ , ์ค์ํ๋ฉด ๋ ํฐ ๋ฒ๊ทธ๊ฐ ์๊น.
Rich Hickey์ ํด๊ฒฐ์ฑ
์ ๋จ์ํ์ด: "์ ์ด์ ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝ ๋ชป ํ๊ฒ ํ๋ฉด ๋์์?"
์ด๊ฒ Clojure์ ํต์ฌ ์ฒ ํ์ด์ผ. ๋ถ๋ณ์ฑ(Immutability)์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ๋ง๋ค์ด๋ฒ๋ฆฐ ๊ฑฐ์ง.
๊ทธ๋ฆฌ๊ณ LISP์ ๊ฐ๊ฒฐํ ๋ฌธ๋ฒ๊ณผ ๋งคํฌ๋ก ์์คํ
์ ๊ฐ์ ธ์์, JVM ์์์ ์คํ๋๋ ์ค์ฉ์ ์ธ ์ธ์ด๋ฅผ ๋ง๋ ๊ฑฐ์.
Clojure ์ฝ๋๋ฅผ ์ฒ์ ๋ณด๋ฉด ์ด๋ฐ ๋๋์:
(+ 1 2 3) โ ์ด๊ฒ ๋ญ์ผ ใ
ใ
๊ทผ๋ฐ ์ด๊ฒ ์ฌ์ค ์์ฒญ ๋
ผ๋ฆฌ์ ์ธ ๊ตฌ์กฐ์ผ.
์ผ๋ฐ ์ธ์ด์์ 1 + 2 + 3 ์ด๋ผ๊ณ ์ฐ๋ ๊ฑธ,
Clojure์์๋ (ํจ์ ์ธ์1 ์ธ์2 ์ธ์3) ํํ๋ก ์.
์ฆ, ๋ชจ๋ ๊ฒ์ด ํจ์ ํธ์ถ์ด์ผ. ์ฐ์ฐ์๋ ํจ์๊ณ , ์กฐ๊ฑด๋ฌธ๋ ํจ์๊ณ , ๋ฐ๋ณต๋ ํจ์์.
์ด๊ฒ ์ฒ์์ ์ด์ํ๋ฐ, ์ต์ํด์ง๋ฉด ์คํ๋ ค ์ผ๊ด์ฑ์ด ์์ด์ ํธํจ.
; ์ฃผ์์ ์ธ๋ฏธ์ฝ๋ก ์ผ๋ก ์์ํด
; ๊ธฐ๋ณธ ์ฐ์ฐ
(+ 1 2 3) ; => 6
(* 2 3 4) ; => 24
(- 10 3) ; => 7
(/ 10 2) ; => 5
; ๋ฌธ์์ด
(str "์๋
" " " "ํด๋ก์ !") ; => "์๋
ํด๋ก์ !"
(count "hello") ; => 5
; ๋ณ์ ์ ์ (def)
(def my-name "ํด๋ก์ ๊ฐ๋ฐ์")
(def pi 3.14159)
; ํจ์ ์ ์ (defn)
(defn greet [name]
(str "์๋
ํ์ธ์, " name "๋!"))
(greet "์ฌ๋ฅ๋ท ์ ์ ") ; => "์๋
ํ์ธ์, ์ฌ๋ฅ๋ท ์ ์ ๋!"
; ์กฐ๊ฑด๋ฌธ (if)
(if (> 10 5)
"10์ด ๋ ํฌ๋ค"
"5๊ฐ ๋ ํฌ๋ค")
; => "10์ด ๋ ํฌ๋ค"
; let์ผ๋ก ์ง์ญ ๋ณ์ ๋ฐ์ธ๋ฉ
(let [x 10
y 20
sum (+ x y)]
(str "ํฉ๊ณ: " sum))
; => "ํฉ๊ณ: 30"
def๋ ์ ์ญ ๋ณ์, let์ ์ง์ญ ๋ณ์ ๋ฐ์ธ๋ฉ์ด์ผ. ๊ทธ๋ฆฌ๊ณ ํ๋ฒ ๋ฐ์ธ๋ฉ๋ ๊ฐ์ ๋ณ๊ฒฝ ๋ถ๊ฐ! ์ด๊ฒ ๋ถ๋ณ์ฑ์ ์์์.| ํ์ | ์์ | ์ค๋ช |
|---|---|---|
| ์ซ์ | 42, 3.14, 22/7 | ์ ์, ์ค์, ์ ๋ฆฌ์ ์ง์ |
| ๋ฌธ์์ด | "hello" | Java String๊ณผ ๋์ผ |
| ํค์๋ | :name, :age | ์ฝ๋ก ์ผ๋ก ์์, ์๊ธฐ ์์ ์ด ๊ฐ |
| ์ฌ๋ณผ | foo, my-var | ๋ณ์๋ ํจ์๋ฅผ ์ฐธ์กฐ |
| ๋ถ๋ฆฌ์ธ | true, false | nil๋ false๋ก ์ทจ๊ธ |
| nil | nil | Java์ null๊ณผ ๋์ผ |
| ๋ฌธ์ | \a, \newline | ๋ฐฑ์ฌ๋์๋ก ์์ |
; ๋ฒกํฐ (Vector) - ์์ ์๋ ์ปฌ๋ ์
, ์ธ๋ฑ์ค ์ ๊ทผ O(1)
(def my-vec [1 2 3 4 5])
(get my-vec 0) ; => 1
(nth my-vec 2) ; => 3
(count my-vec) ; => 5
; ๋ฆฌ์คํธ (List) - ์ฐ๊ฒฐ ๋ฆฌ์คํธ, ์์์ ์ถ๊ฐ O(1)
(def my-list '(1 2 3 4 5))
(first my-list) ; => 1
(rest my-list) ; => (2 3 4 5)
; ๋งต (Map) - ํค-๊ฐ ์
(def person {:name "๊นํด๋ก์ "
:age 30
:job "๊ฐ๋ฐ์"})
(get person :name) ; => "๊นํด๋ก์ "
(:age person) ; => 30 (ํค์๋๋ฅผ ํจ์์ฒ๋ผ ์ฌ์ฉ!)
; ์
(Set) - ์ค๋ณต ์๋ ์ปฌ๋ ์
(def my-set #{1 2 3 4 5})
(contains? my-set 3) ; => true
(contains? my-set 6) ; => false
๋ถ๋ณ์ฑ(Immutability)... ์ด๊ฑฐ ์ฒ์ ๋ค์ผ๋ฉด "๊ทธ๋ผ ๋ฐ์ดํฐ๋ฅผ ์ด๋ป๊ฒ ๋ฐ๊ฟ?" ์ถ์์ ใ
ใ
๊ทผ๋ฐ ์๊ฐํด๋ด. ์ฐ๋ฆฌ๊ฐ ์ผ์์์ ์ฐ๋ ์ซ์ 5๋ ๋ณํ์ง ์์์?
5 + 3 = 8์ด๋ผ๊ณ ํด์ 5๊ฐ 8์ด ๋๋ ๊ฒ ์๋๋ผ, ์๋ก์ด ๊ฐ 8์ด ์๊ธฐ๋ ๊ฑฐ์์.
Clojure๋ ์ด ๊ฐ๋
์ ๋ชจ๋ ๋ฐ์ดํฐ ๊ตฌ์กฐ์ ์ ์ฉํ ๊ฑฐ์ผ.
๋ฒกํฐ์ ์์๋ฅผ ์ถ๊ฐํ๋ฉด? โ ์๋ก์ด ๋ฒกํฐ๊ฐ ๋ฐํ๋จ. ์๋ณธ์ ๊ทธ๋๋ก.
๋งต์์ ๊ฐ์ ๋ฐ๊พธ๋ฉด? โ ์๋ก์ด ๋งต์ด ๋ฐํ๋จ. ์๋ณธ์ ๊ทธ๋๋ก.
์ด๊ฒ ์ ์ข๋๋ฉด:
1 ์ค๋ ๋ ์์ ์ฑ - ์ฌ๋ฌ ์ค๋ ๋๊ฐ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด๋ ์ ๋ ์ถฉ๋ ์์
2 ์์ธก ๊ฐ๋ฅ์ฑ - ํจ์์ ๋ฐ์ดํฐ๋ฅผ ๋๊ฒจ๋ ์๋ณธ์ด ๋ฐ๋ ๊ฑฑ์ ์์
3 ๋๋ฒ๊น
์ฉ์ด - ๋ฐ์ดํฐ๊ฐ ์ธ์ ์ด๋์ ๋ฐ๋์๋์ง ์ถ์ ๋ถํ์
4 ์๊ฐ ์ฌํ ๋๋ฒ๊น
- ๊ณผ๊ฑฐ ์ํ๋ฅผ ์ธ์ ๋ ์ฐธ์กฐ ๊ฐ๋ฅ
5 ์บ์ฑ ์ต์ ํ - ๋ถ๋ณ ๋ฐ์ดํฐ๋ ์์ ํ๊ฒ ์บ์ ๊ฐ๋ฅ
"๋ถ๋ณ์ด๋ฉด ๋ฐ์ดํฐ ๋ฐ๊ฟ ๋๋ง๋ค ์ ์ฒด๋ฅผ ๋ณต์ฌํด์ผ ํ๋ ๊ฑฐ ์๋์ผ? ๊ทธ๋ผ ๋ฉ๋ชจ๋ฆฌ ๋ญ๋น ์ฌํ์ง ์์?" ๐ค
์ด ์ง๋ฌธ ์ง์ง ์ข์ ์ง๋ฌธ์! ๊ทธ๋ฆฌ๊ณ Clojure๋ ์ด ๋ฌธ์ ๋ฅผ ๊ตฌ์กฐ์ ๊ณต์ (Structural Sharing)๋ก ํด๊ฒฐํด.
ํต์ฌ ์์ด๋์ด: ๋ณ๊ฒฝ๋ ๋ถ๋ถ๋ง ์๋ก ๋ง๋ค๊ณ , ๋ณ๊ฒฝ๋์ง ์์ ๋ถ๋ถ์ ๊ณต์ ํจ.
Clojure์ ์์์ ๋ฐ์ดํฐ ๊ตฌ์กฐ(Persistent Data Structures)๋ HAMT(Hash Array Mapped Trie) ์๊ณ ๋ฆฌ์ฆ์ ๊ธฐ๋ฐ์ผ๋ก ํด.
Phil Bagwell์ด 2001๋
์ ๋ฐํํ ์๊ณ ๋ฆฌ์ฆ์ธ๋ฐ, Rich Hickey๊ฐ ์ด๊ฑธ Clojure์ ์ ์ฉํ ๊ฑฐ์ผ.
HAMT์ ํน์ง:
โข ํธ๋ฆฌ ๊ตฌ์กฐ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ์ฅ
โข ์์ ์ ๋ณ๊ฒฝ๋ ๊ฒฝ๋ก์ ๋
ธ๋๋ง ์๋ก ์์ฑ
โข ๋๋จธ์ง ๋
ธ๋๋ ์ด์ ๋ฒ์ ๊ณผ ๊ณต์
โข ๋๋ถ๋ถ์ ์ฐ์ฐ์ด O(logโโ n) ์๊ฐ ๋ณต์ก๋ (์ฌ์ค์ O(1)์ ๊ฐ๊น์)
โข 32-way branching factor๋ก ํธ๋ฆฌ ๊น์ด ์ต์ํ
; ๋ฒกํฐ ์กฐ์
(def v [1 2 3 4 5])
(conj v 6) ; => [1 2 3 4 5 6] (๋์ ์ถ๊ฐ)
(assoc v 2 99) ; => [1 2 99 4 5] (์ธ๋ฑ์ค 2๋ฅผ 99๋ก)
(dissoc v 2) ; ๋ฒกํฐ์๋ dissoc ์ ์
(subvec v 1 3) ; => [2 3] (์ฌ๋ผ์ด์ฑ)
(into [] (rest v)) ; => [2 3 4 5] (์ฒซ ์์ ์ ๊ฑฐ)
; ์๋ณธ์ ๊ทธ๋๋ก!
v ; => [1 2 3 4 5] ๋ณํ์ง ์์!
; ๋งต ์กฐ์
(def m {:a 1 :b 2 :c 3})
(assoc m :d 4) ; => {:a 1, :b 2, :c 3, :d 4}
(dissoc m :b) ; => {:a 1, :c 3}
(update m :a inc) ; => {:a 2, :b 2, :c 3}
(merge m {:d 4 :e 5}) ; => {:a 1, :b 2, :c 3, :d 4, :e 5}
; ์ค์ฒฉ ๋งต ์กฐ์
(def nested {:user {:name "๊นํด๋ก์ " :age 30}})
(assoc-in nested [:user :age] 31)
; => {:user {:name "๊นํด๋ก์ ", :age 31}}
(get-in nested [:user :name])
; => "๊นํด๋ก์ "
(update-in nested [:user :age] inc)
; => {:user {:name "๊นํด๋ก์ ", :age 31}}
assoc-in, get-in, update-in์ ์ค์ฒฉ๋ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ๋ค๋ฃฐ ๋ ์ง์ง ์ ์ฉํด. ๊ฒฝ๋ก๋ฅผ ๋ฒกํฐ๋ก ์ง์ ํ๋ฉด ๊น์ ๊ณณ๊น์ง ์ ๊ทผ ๊ฐ๋ฅ!ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ๊ฝ์ด๋ผ๊ณ ํ ์ ์๋ ๊ณ ์ฐจ ํจ์!
ํจ์๋ฅผ ์ธ์๋ก ๋ฐ๊ฑฐ๋, ํจ์๋ฅผ ๋ฐํํ๋ ํจ์๋ฅผ ๋งํด.
Clojure์์ ๊ฐ์ฅ ๋ง์ด ์ฐ๋ ๊ณ ์ฐจ ํจ์๋ค:
; map - ์ปฌ๋ ์
์ ๊ฐ ์์์ ํจ์ ์ ์ฉ
(map inc [1 2 3 4 5])
; => (2 3 4 5 6)
(map #(* % 2) [1 2 3 4 5])
; => (2 4 6 8 10)
(map str [1 2 3])
; => ("1" "2" "3")
; filter - ์กฐ๊ฑด์ ๋ง๋ ์์๋ง ์ถ์ถ
(filter even? [1 2 3 4 5 6])
; => (2 4 6)
(filter #(> % 3) [1 2 3 4 5])
; => (4 5)
; reduce - ์ปฌ๋ ์
์ ํ๋์ ๊ฐ์ผ๋ก ์ถ์ฝ
(reduce + [1 2 3 4 5])
; => 15
(reduce * [1 2 3 4 5])
; => 120
(reduce (fn [acc x] (+ acc (* x x))) 0 [1 2 3 4 5])
; => 55 (1ยฒ + 2ยฒ + 3ยฒ + 4ยฒ + 5ยฒ)
; ์กฐํฉํด์ ์ฌ์ฉํ๊ธฐ!
(->> [1 2 3 4 5 6 7 8 9 10]
(filter even?)
(map #(* % %))
(reduce +))
; => 220 (2ยฒ + 4ยฒ + 6ยฒ + 8ยฒ + 10ยฒ)
Clojure์์ ์ง์ง ํธํ ๊ธฐ๋ฅ ์ค ํ๋๊ฐ ์ค๋ ๋ฉ ๋งคํฌ๋ก์ผ.
๋ฐ์ดํฐ ๋ณํ ํ์ดํ๋ผ์ธ์ ์ฝ๊ธฐ ์ฝ๊ฒ ํํํ ์ ์์ด.
-> (์ค๋ ๋ ํผ์คํธ): ์ฒซ ๋ฒ์งธ ์ธ์ ์์น์ ์ฝ์
->> (์ค๋ ๋ ๋ผ์คํธ): ๋ง์ง๋ง ์ธ์ ์์น์ ์ฝ์
; ์ค๋ ๋ฉ ๋งคํฌ๋ก ์์ด (์ฝ๊ธฐ ์ด๋ ค์ ใ
ใ
)
(str (clojure.string/upper-case
(clojure.string/trim
" hello world ")))
; => "HELLO WORLD"
; -> ์ค๋ ๋ฉ ๋งคํฌ๋ก ์ฌ์ฉ (ํจ์ฌ ์ฝ๊ธฐ ์ฌ์!)
(-> " hello world "
clojure.string/trim
clojure.string/upper-case
str)
; => "HELLO WORLD"
; ->> ์ค๋ ๋ฉ ๋งคํฌ๋ก (์ปฌ๋ ์
์ฒ๋ฆฌ์ ์ ์ฉ)
(->> (range 1 11)
(filter odd?)
(map #(* % %))
(reduce +))
; => 165 (1ยฒ + 3ยฒ + 5ยฒ + 7ยฒ + 9ยฒ)
; as-> ์ค๋ ๋ฉ ๋งคํฌ๋ก (์์น๋ฅผ ์ง์ ์ง์ )
(as-> 5 x
(* x x)
(+ x 10)
(str "๊ฒฐ๊ณผ: " x))
; => "๊ฒฐ๊ณผ: 35"
Clojure๋ผ๋ ์ด๋ฆ์ด ์ฌ์ค ํด๋ก์ (Closure)์์ ์์ด.
(C, L, J๋ฅผ ํฌํจํด์ C-L-J-ure... Java, Lisp, Clojure์ ์๊ธ์ ์กฐํฉ์ด๊ธฐ๋ ํจ ใ
ใ
)
ํด๋ก์ (Closure)๋ ํจ์๊ฐ ์์ ์ด ์ ์๋ ํ๊ฒฝ์ ๋ณ์๋ฅผ "์บก์ฒ"ํ๋ ๊ฒ์ ๋งํด.
; ํด๋ก์ ์์
(defn make-adder [n]
(fn [x] (+ x n))) ; n์ ์บก์ฒํ๋ ํด๋ก์
(def add5 (make-adder 5))
(def add10 (make-adder 10))
(add5 3) ; => 8
(add10 3) ; => 13
; ๋ ์ค์ฉ์ ์ธ ์์
(defn make-counter []
(let [count (atom 0)] ; atom์ ๋ณ๊ฒฝ ๊ฐ๋ฅํ ์ฐธ์กฐ (๋์ค์ ์ค๋ช
!)
{:increment #(swap! count inc)
:decrement #(swap! count dec)
:value #(deref count)}))
(def counter (make-counter))
((:increment counter))
((:increment counter))
((:increment counter))
((:value counter)) ; => 3
; partial - ํจ์์ ์ผ๋ถ ์ธ์๋ฅผ ๋ฏธ๋ฆฌ ์ ์ฉ
(def multiply-by-3 (partial * 3))
(multiply-by-3 5) ; => 15
(multiply-by-3 10) ; => 30
; ์ค์ฉ์ ์ธ ์
(def add-tax (partial * 1.1))
(add-tax 1000) ; => 1100.0
(add-tax 5000) ; => 5500.0
; comp - ํจ์ ํฉ์ฑ
(def process-name
(comp clojure.string/upper-case
clojure.string/trim))
(process-name " hello ") ; => "HELLO"
; juxt - ์ฌ๋ฌ ํจ์๋ฅผ ๋์์ ์ ์ฉ
(def stats (juxt min max #(/ (reduce + %) (count %))))
(stats [3 1 4 1 5 9 2 6])
; => [1 9 31/8] (์ต์๊ฐ, ์ต๋๊ฐ, ํ๊ท )
Clojure๋ ๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด 4๊ฐ์ง ์ฐธ์กฐ ํ์
์ ์ ๊ณตํด.
๊ฐ๊ฐ ๋ค๋ฅธ ์ํฉ์์ ์ฌ์ฉํ๋ ๊ฑฐ์ผ. ์ด๊ฒ ์ง์ง ์ ์ค๊ณ๋ ๋ถ๋ถ์ ๐
๋ถ๋ณ ๋ฐ์ดํฐ + ์ด 4๊ฐ์ง ์ฐธ์กฐ ํ์
= ๋์์ฑ ๋ฒ๊ทธ ์๋ ์ฝ๋!
; Atom ์์ฑ
(def counter (atom 0))
(def user-db (atom {}))
; ๊ฐ ์ฝ๊ธฐ
@counter ; => 0 (deref์ ๋์ผ)
(deref counter) ; => 0
; ๊ฐ ๋ณ๊ฒฝ (swap! - ํจ์๋ฅผ ์ ์ฉํด์ ๋ณ๊ฒฝ)
(swap! counter inc) ; => 1
(swap! counter + 5) ; => 6
(swap! counter * 2) ; => 12
; ๊ฐ ๊ต์ฒด (reset! - ์ ๊ฐ์ผ๋ก ์ง์ ๊ต์ฒด)
(reset! counter 0) ; => 0
; ์ค์ฉ์ ์ธ ์: ์ฌ์ฉ์ DB ๊ด๋ฆฌ
(swap! user-db assoc :user1 {:name "๊นํด๋ก์ " :age 30})
(swap! user-db assoc :user2 {:name "์ดํจ์ํ" :age 25})
(swap! user-db update-in [:user1 :age] inc)
@user-db
; => {:user1 {:name "๊นํด๋ก์ ", :age 31},
; :user2 {:name "์ดํจ์ํ", :age 25}}
; ๋ฉํฐ์ค๋ ๋ ํ๊ฒฝ์์๋ ์์ !
(def safe-counter (atom 0))
(doall
(pmap (fn [_] (swap! safe-counter inc))
(range 1000)))
@safe-counter ; => 1000 (ํญ์ ์ ํํ ๊ฐ!)
; ์ํ ๊ณ์ข ์์ (STM์ผ๋ก ์์ ํ๊ฒ!)
(def account-a (ref 1000))
(def account-b (ref 500))
; ์ด์ฒด ํจ์
(defn transfer! [from to amount]
(dosync ; ํธ๋์ญ์
์์
(when (>= @from amount)
(alter from - amount)
(alter to + amount)
true)))
; ์ด์ฒด ์คํ
(transfer! account-a account-b 300)
; => true
@account-a ; => 700
@account-b ; => 800
; ๋์์ ์ฌ๋ฌ ์ด์ฒด๊ฐ ์ผ์ด๋๋ ์์ !
; STM์ด ์ถฉ๋์ ๊ฐ์งํ๊ณ ์๋์ผ๋ก ์ฌ์๋ํจ
(future (transfer! account-a account-b 100))
(future (transfer! account-b account-a 50))
; ์ ์ ํ...
(+ @account-a @account-b) ; => 1500 (ํญ์ ์ดํฉ ๋ณด์กด!)
๋งคํฌ๋ก๋ Clojure(LISP)์ ๊ฐ์ฅ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ ์ค ํ๋์ผ.
์ฝ๋๋ฅผ ์์ฑํ๋ ์ฝ๋๋ผ๊ณ ์๊ฐํ๋ฉด ๋ผ.
์ผ๋ฐ ํจ์๋ ๋ฐํ์์ ์คํ๋์ง๋ง, ๋งคํฌ๋ก๋ ์ปดํ์ผ ํ์์ ์ฝ๋๋ฅผ ๋ณํํด.
์ด๊ฒ ๊ฐ๋ฅํ ์ด์ ๊ฐ ๋ฐ๋ก ํธ๋ชจ์ด์ฝ๋์ฑ(Homoiconicity)์ด์ผ.
Clojure์์ ์ฝ๋ ์์ฒด๊ฐ ๋ฐ์ดํฐ(๋ฆฌ์คํธ)์ด๊ธฐ ๋๋ฌธ์, ์ฝ๋๋ฅผ ๋ฐ์ดํฐ์ฒ๋ผ ์กฐ์ํ ์ ์์ด.
์ด๊ฒ ์ ๊ฐ๋ ฅํ๋๋ฉด... ์ธ์ด์ ์๋ ๊ธฐ๋ฅ์ ์ง์ ์ถ๊ฐํ ์ ์๊ฑฐ๋ !
์๋ฅผ ๋ค์ด if, when, and, or ๊ฐ์ ๊ฒ๋ค์ด ์ฌ์ค Clojure์์ ๋งคํฌ๋ก๋ก ๊ตฌํ๋์ด ์์ด ใทใท
; ๋งคํฌ๋ก ์ ์ ์์
; when ๋งคํฌ๋ก ์ง์ ๊ตฌํํด๋ณด๊ธฐ
(defmacro my-when [condition & body]
`(if ~condition
(do ~@body)
nil))
; ์ฌ์ฉ
(my-when (> 10 5)
(println "10์ด ๋ ํฌ๋ค!")
(println "๋ง์!"))
; => 10์ด ๋ ํฌ๋ค!
; => ๋ง์!
; ๋งคํฌ๋ก ํ์ฅ ํ์ธ (macroexpand)
(macroexpand '(my-when (> 10 5) (println "hello")))
; => (if (> 10 5) (do (println "hello")) nil)
; ์ค์ฉ์ ์ธ ๋งคํฌ๋ก ์์ : ํ์ด๋ฐ ์ธก์
(defmacro time-it [& body]
`(let [start# (System/nanoTime)
result# (do ~@body)
end# (System/nanoTime)]
(println (str "์คํ ์๊ฐ: "
(/ (- end# start#) 1000000.0)
"ms"))
result#))
(time-it
(Thread/sleep 100)
(+ 1 2 3))
; => ์คํ ์๊ฐ: 100.xxx ms
; => 6
; ๋ก๊น
๋งคํฌ๋ก
(defmacro log-call [fn-name & args]
`(do
(println (str "[LOG] " ~fn-name " ํธ์ถ: " ~@args))
(~(symbol fn-name) ~@args)))
Clojure์ ์ง์ง ์ฐ์ํ ๋ถ๋ถ ์ค ํ๋๊ฐ ์ํ์ค ์ถ์ํ(Sequence Abstraction)์ผ.
๋ฒกํฐ๋ , ๋ฆฌ์คํธ๋ , ๋งต์ด๋ , ์
์ด๋ , ์ฌ์ง์ด ํ์ผ์ด๋ ๋คํธ์ํฌ ์คํธ๋ฆผ์ด๋ ...
๋ชจ๋ ๋์ผํ ์ํ์ค ์ธํฐํ์ด์ค๋ก ๋ค๋ฃฐ ์ ์์ด.
first, rest, next, cons ๊ฐ์ ํจ์๋ค์ด ๋ชจ๋ ์ํ์ค์ ๋์ํจ.
๊ทธ๋ฆฌ๊ณ map, filter, reduce ๊ฐ์ ๊ณ ์ฐจ ํจ์๋ค๋ ๋ง์ฐฌ๊ฐ์ง์ผ.
์ด๊ฒ ์ ์ข๋๋ฉด... ์๋ก์ด ๋ฐ์ดํฐ ํ์
์ ๋ง๋ค์ด๋ ์ํ์ค ํ๋กํ ์ฝ๋ง ๊ตฌํํ๋ฉด
๊ธฐ์กด์ ๋ชจ๋ ํจ์๋ค์ ๊ทธ๋๋ก ์ฌ์ฉํ ์ ์์ด!
; ๋ค์ํ ์ปฌ๋ ์
์ ๋์ผํ ํจ์ ์ ์ฉ
(first [1 2 3]) ; => 1 (๋ฒกํฐ)
(first '(1 2 3)) ; => 1 (๋ฆฌ์คํธ)
(first #{1 2 3}) ; => 1 (์
, ์์ ๋ถํ์ )
(first {:a 1 :b 2}) ; => [:a 1] (๋งต์ ํค-๊ฐ ์)
; ์ง์ฐ ์ํ์ค (Lazy Sequence)
; ๋ฌดํ ์ํ์ค๋ ๋ง๋ค ์ ์์ด!
(def natural-numbers (range)) ; 0๋ถํฐ ๋ฌดํ๋๊น์ง
(take 10 natural-numbers)
; => (0 1 2 3 4 5 6 7 8 9)
; ํผ๋ณด๋์น ์์ด (์ง์ฐ ์ํ์ค)
(def fibs
(lazy-cat [0 1]
(map + fibs (rest fibs))))
(take 10 fibs)
; => (0 1 1 2 3 5 8 13 21 34)
; ๋ฌดํ ์ํ์ค์์ ํ์ํ ๋งํผ๋ง ๊ฐ์ ธ์ค๊ธฐ
(->> (range)
(filter even?)
(map #(* % %))
(take 5))
; => (0 4 16 36 64)
; iterate - ํจ์๋ฅผ ๋ฐ๋ณต ์ ์ฉ
(take 8 (iterate #(* 2 %) 1))
; => (1 2 4 8 16 32 64 128)
; cycle - ๋ฌดํ ๋ฐ๋ณต
(take 7 (cycle [1 2 3]))
; => (1 2 3 1 2 3 1)
; repeat - ๊ฐ ๋ฐ๋ณต
(take 5 (repeat "hello"))
; => ("hello" "hello" "hello" "hello" "hello")
ํธ๋์ค๋์๋ Clojure 1.7์์ ๋์
๋ ๊ฐ๋
์ผ๋ก, Rich Hickey๊ฐ ์ง์ ์ค๊ณํ ๊ฑฐ์ผ.
๊ธฐ์กด์ map, filter ๊ฐ์ ํจ์๋ค์ ์ค๊ฐ ์ปฌ๋ ์
์ ์์ฑํด.
์๋ฅผ ๋ค์ด (map f (filter g coll))์ filter ๊ฒฐ๊ณผ๋ฅผ ๋ด๋ ์ค๊ฐ ์ปฌ๋ ์
์ด ์๊น.
ํธ๋์ค๋์๋ ์ด ์ค๊ฐ ์ปฌ๋ ์
์์ด ๋ณํ์ ํฉ์ฑํ ์ ์์ด.
โ ๋ฉ๋ชจ๋ฆฌ ํจ์จ ํฅ์
โ ์ฑ๋ฅ ํฅ์
โ ์ปฌ๋ ์
, ์ฑ๋, ์คํธ๋ฆผ ๋ฑ ์ด๋์๋ ์ฌ์ฌ์ฉ ๊ฐ๋ฅ
; ์ผ๋ฐ ๋ฐฉ์ (์ค๊ฐ ์ปฌ๋ ์
์์ฑ)
(->> [1 2 3 4 5 6 7 8 9 10]
(filter even?)
(map #(* % %))
(into []))
; ์ค๊ฐ์ (2 4 6 8 10) ์ปฌ๋ ์
์ด ์์ฑ๋จ
; ํธ๋์ค๋์ ๋ฐฉ์ (์ค๊ฐ ์ปฌ๋ ์
์์!)
(def xf
(comp
(filter even?)
(map #(* % %))))
(into [] xf [1 2 3 4 5 6 7 8 9 10])
; => [4 16 36 64 100]
; transduce๋ก ๋ฐ๋ก reduce
(transduce xf + [1 2 3 4 5 6 7 8 9 10])
; => 220
; ์ฑ๋์๋ ์ ์ฉ ๊ฐ๋ฅ (core.async)
; (chan 10 xf) ; ์ฑ๋์ ํธ๋์ค๋์ ์ ์ฉ
Clojure๋ก ์น ๊ฐ๋ฐ๋ ์ถฉ๋ถํ ํ ์ ์์ด!
Ring์ Clojure์ HTTP ์ถ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ณ ,
Compojure๋ ๋ผ์ฐํ
๋ผ์ด๋ธ๋ฌ๋ฆฌ์ผ.
Python์ WSGI + Flask ์กฐํฉ์ด๋ ๋น์ทํ ๋๋์ด๋ผ๊ณ ๋ณด๋ฉด ๋จ.
; project.clj (Leiningen ํ๋ก์ ํธ ์ค์ )
(defproject my-web-app "0.1.0"
:dependencies [[org.clojure/clojure "1.11.1"]
[ring/ring-core "1.9.6"]
[ring/ring-jetty-adapter "1.9.6"]
[compojure "1.7.0"]
[ring/ring-json "0.5.1"]])
; src/my_web_app/core.clj
(ns my-web-app.core
(:require [compojure.core :refer :all]
[compojure.route :as route]
[ring.adapter.jetty :as jetty]
[ring.middleware.json :as json]
[ring.util.response :as response]))
; ๋ฐ์ดํฐ ์ ์ฅ์ (Atom ์ฌ์ฉ!)
(def users (atom [{:id 1 :name "๊นํด๋ก์ " :age 30}
{:id 2 :name "์ดํจ์ํ" :age 25}]))
; ๋ผ์ฐํธ ์ ์
(defroutes app-routes
(GET "/" []
(response/response "Clojure ์น ์๋ฒ์ ์ค์ ๊ฑธ ํ์ํฉ๋๋ค!"))
(GET "/users" []
(response/response @users))
(GET "/users/:id" [id]
(let [user-id (Integer/parseInt id)
user (first (filter #(= (:id %) user-id) @users))]
(if user
(response/response user)
(response/not-found "์ฌ์ฉ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค"))))
(POST "/users" {body :body}
(let [new-user (assoc body :id (inc (count @users)))]
(swap! users conj new-user)
(response/created "/users" new-user)))
(route/not-found "ํ์ด์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค"))
; ๋ฏธ๋ค์จ์ด ์ ์ฉ
(def app
(-> app-routes
json/wrap-json-response
json/wrap-json-body))
; ์๋ฒ ์์
(defn -main []
(jetty/run-jetty app {:port 3000 :join? false})
(println "์๋ฒ ์์! http://localhost:3000"))
; CSV ๋ฐ์ดํฐ ์ฒ๋ฆฌ ์์
(require '[clojure.string :as str])
(require '[clojure.java.io :as io])
; ์ํ ๋ฐ์ดํฐ
(def raw-data
"์ด๋ฆ,๋์ด,์ ์,๋ถ์
๊นํด๋ก์ ,30,95,๊ฐ๋ฐ
์ดํจ์ํ,25,88,๊ฐ๋ฐ
๋ฐ๋ถ๋ณ,35,92,๊ธฐํ
์ต๋์์ฑ,28,78,๊ฐ๋ฐ
์ ๋งคํฌ๋ก,32,85,๊ธฐํ")
; ๋ฐ์ดํฐ ํ์ฑ ํจ์
(defn parse-csv [csv-str]
(let [lines (str/split-lines (str/trim csv-str))
headers (map keyword (str/split (first lines) #","))
rows (rest lines)]
(map (fn [row]
(zipmap headers
(str/split (str/trim row) #",")))
rows)))
(def employees (parse-csv raw-data))
; ๋ฐ์ดํฐ ๋ถ์
; ๊ฐ๋ฐํ ํ๊ท ์ ์
(->> employees
(filter #(= (:๋ถ์ %) "๊ฐ๋ฐ"))
(map #(Integer/parseInt (:์ ์ %)))
(fn [scores] (/ (reduce + scores) (count scores))))
; => 87 (๊ฐ๋ฐํ ํ๊ท )
; ๋ถ์๋ณ ๊ทธ๋ฃนํ
(group-by :๋ถ์ employees)
; => {"๊ฐ๋ฐ" [...], "๊ธฐํ" [...]}
; ์ ์ ๊ธฐ์ค ์ ๋ ฌ
(sort-by #(Integer/parseInt (:์ ์ %)) > employees)
; ์ ์ ๋์ ์์ผ๋ก ์ ๋ ฌ
; core.async ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ
(require '[clojure.core.async :as async
:refer [chan go go-loop >! <! >!! <!! close!]])
; ์ฑ๋ ์์ฑ
(def ch (chan 10))
; ์์ฐ์ (Producer)
(go
(doseq [i (range 5)]
(>! ch (* i i))
(println (str "์ ์ก: " (* i i))))
(close! ch))
; ์๋น์ (Consumer)
(go-loop []
(when-let [val (<! ch)]
(println (str "์์ : " val))
(recur)))
; ํ์ดํ๋ผ์ธ ์ฒ๋ฆฌ
(def input-ch (chan 10))
(def output-ch (chan 10))
; ๋ณํ ํ์ดํ๋ผ์ธ
(async/pipeline
4 ; ๋ณ๋ ฌ ์ฒ๋ฆฌ ์
output-ch
(comp (filter even?) (map #(* % %)))
input-ch)
; ๋ฐ์ดํฐ ์ ์ก
(go
(doseq [i (range 10)]
(>! input-ch i))
(close! input-ch))
; ๊ฒฐ๊ณผ ์์ง
(go-loop [results []]
(if-let [val (<! output-ch)]
(recur (conj results val))
(println "๊ฒฐ๊ณผ:" results)))
Clojure ์์ํ๋ ๋ฐฉ๋ฒ์ ํฌ๊ฒ ๋ ๊ฐ์ง์ผ:
Leiningen (์ ํต์ ์ธ ๋ฐฉ๋ฒ) ๋๋ Clojure CLI + deps.edn (์ต์ ๋ฐฉ๋ฒ)
์์ฆ์ Clojure CLI ๋ฐฉ์์ ๋ ๋ง์ด ์ถ์ฒํ๋ ์ถ์ธ์ผ.
; 1. Java ์ค์น (JDK 11 ์ด์ ๊ถ์ฅ)
; https://adoptium.net/ ์์ ๋ค์ด๋ก๋
; 2. Clojure CLI ์ค์น (macOS)
; brew install clojure/tools/clojure
; 3. Leiningen ์ค์น (์ ํ)
; brew install leiningen
; 4. ์ ํ๋ก์ ํธ ์์ฑ
; lein new app my-clojure-app
; ๋๋
; clojure -Tnew app :name myname/myapp
; 5. REPL ์์ (์ง์ง ์ค์! Clojure ๊ฐ๋ฐ์ ํต์ฌ)
; lein repl
; ๋๋
; clj
; 6. ์ฝ๋ ์คํ
; lein run
; ๋๋
; clj -M -m my-app.core
| ์๋ํฐ | ํ๋ฌ๊ทธ์ธ | ํน์ง |
|---|---|---|
| VS Code | Calva | ๊ฐ์ฅ ์ฝ๊ฒ ์์ ๊ฐ๋ฅ, REPL ํตํฉ |
| IntelliJ IDEA | Cursive | ๊ฐ์ฅ ๊ฐ๋ ฅํ IDE ์ง์, ์ ๋ฃ |
| Emacs | CIDER | ์ ํต์ ์ธ Clojure ๊ฐ๋ฐ ํ๊ฒฝ |
| Vim/Neovim | Conjure | vim ์ฌ์ฉ์๋ฅผ ์ํ ์ ํ |
| Sublime Text | SublimeREPL | ๊ฐ๋ฒผ์ด ์๋ํฐ ์ ํธ์ |
RingCompojure
PedestalLuminus
ReititBidi
next.jdbcHoneySQL
DatomicCrux
MongerCarmine
clojure.testMidje
Expectationstest.check
Kaocha
core.asyncManifold
Incantertech.ml
Tablecloth
| ํน์ง | Clojure | Haskell | Scala | Elixir |
|---|---|---|---|---|
| ํ์ ์์คํ | ๋์ ํ์ | ์ ์ ํ์ | ์ ์ ํ์ | ๋์ ํ์ |
| ์คํ ํ๊ฒฝ | JVM | ๋ค์ดํฐ๋ธ | JVM | BEAM VM |
| ๋ถ๋ณ์ฑ | ๊ธฐ๋ณธ๊ฐ | ๊ธฐ๋ณธ๊ฐ | ์ ํ์ | ๊ธฐ๋ณธ๊ฐ |
| ํ์ต ๊ณก์ | ์ค๊ฐ | ๋งค์ฐ ๋์ | ๋์ | ์ค๊ฐ |
| Java ํธํ | ์์ ํธํ | ์์ | ์์ ํธํ | ์์ |
| ๋์์ฑ | STM, Atom | STM | Akka | Actor ๋ชจ๋ธ |
| ๋งคํฌ๋ก | ๊ฐ๋ ฅํจ | ์ ํ์ | ์ ํ์ | ์์ |
| ์ํ๊ณ | Java ํ์ฉ | ๋ ์์ | Java ํ์ฉ | Erlang ํ์ฉ |
Clojure๊ฐ ํนํ ๋น๋๋ ์ํฉ๋ค:
1 ๋ฐ์ดํฐ ๋ณํ ํ์ดํ๋ผ์ธ - ETL, ๋ฐ์ดํฐ ์ฒ๋ฆฌ, ๋ถ์
2 ๋์์ฑ์ด ์ค์ํ ์๋ฒ - ๋์ ์ฒ๋ฆฌ๋, ์ค์๊ฐ ์์คํ
3 ๊ธฐ์กด Java ์์คํ
๊ณผ ํตํฉ - Java ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ทธ๋๋ก ํ์ฉ
4 ๋๋ฉ์ธ ํนํ ์ธ์ด(DSL) ๊ฐ๋ฐ - ๋งคํฌ๋ก๋ก ์ธ์ด ํ์ฅ
5 REPL ๊ธฐ๋ฐ ํ์์ ๊ฐ๋ฐ - ๋ฐ์ดํฐ ๊ณผํ, ํ๋กํ ํ์ดํ
6 ๋ง์ดํฌ๋ก์๋น์ค - ์๊ณ ๋
๋ฆฝ์ ์ธ ์๋น์ค ๊ตฌ์ฑ
๋ฐ๋ฉด Clojure๊ฐ ๋ ์ ํฉํ ์ํฉ:
โข ํ ์ ์ฒด๊ฐ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ต์ํ์ง ์์ ๋
โข ๊ฐํ ํ์
์์ ์ฑ์ด ํ์ํ ๋ (Haskell์ด๋ Scala ๊ณ ๋ ค)
โข ๋ชจ๋ฐ์ผ ์ฑ ๊ฐ๋ฐ (ClojureScript๋ก React Native๋ ๊ฐ๋ฅํ์ง๋ง ๋ณต์กํจ)
โข ๊ฒ์ ๊ฐ๋ฐ (์ฑ๋ฅ ์ต์ ํ๊ฐ ์ด๋ ค์ธ ์ ์์)
๐ ์ฑ
โข Clojure for the Brave and True - Daniel Higginbotham (๋ฌด๋ฃ ์จ๋ผ์ธ, ์ด๋ณด์ ์ต๊ณ )
โข Programming Clojure - Alex Miller, Stuart Halloway
โข The Joy of Clojure - Michael Fogus, Chris Houser (์ค๊ธ~๊ณ ๊ธ)
โข Clojure Applied - Ben Vandgrift, Alex Miller (์ค์ ํจํด)
๐ ์จ๋ผ์ธ ์๋ฃ
โข clojure.org - ๊ณต์ ๋ฌธ์ (์ง์ง ์ ๋์ด์์)
โข clojuredocs.org - ํจ์๋ณ ์์ ํ๋ถ
โข 4clojure.com - ๋ฌธ์ ํ๊ธฐ ์ฐ์ต
โข exercism.io/tracks/clojure - ์ฒด๊ณ์ ์ธ ์ฐ์ต ๋ฌธ์
๐ฅ ์์
โข Rich Hickey์ ๊ฐ์ฐ๋ค (Simple Made Easy, The Value of Values ๋ฑ) - ์ ํ๋ธ์์ ๋ฌด๋ฃ
โข ClojureTV ์ ํ๋ธ ์ฑ๋
๐ฌ ์ปค๋ฎค๋ํฐ
โข Clojurians Slack - ๊ฐ์ฅ ํ๋ฐํ ์ปค๋ฎค๋ํฐ
โข r/Clojure ์๋ธ๋ ๋ง
โข ์ฌ๋ฅ๋ท์์๋ Clojure ๊ด๋ จ ์ฌ๋ฅ์ ์ฐพ์๋ณผ ์ ์์ด! ๋ฉํ ๋ง์ด๋ ์ฝ๋ ๋ฆฌ๋ทฐ ์๋น์ค๋ฅผ ํ์ฉํ๋ฉด ํ์ต ์๋๊ฐ ํจ์ฌ ๋นจ๋ผ์ง ๐
Rich Hickey์ ๊ฐ์ฅ ์ ๋ช
ํ ๊ฐ์ฐ ์ ๋ชฉ์ด "Simple Made Easy"์ผ.
์ฌ๊ธฐ์ ๊ทธ๋ ๋จ์ํจ(Simple)๊ณผ ์ฌ์(Easy)์ ๊ตฌ๋ถํด.
Easy(์ฌ์): ์ฐ๋ฆฌ์๊ฒ ์น์ํ๊ณ , ๊ฐ๊น์ด ์๊ณ , ๋ฐฐ์ฐ๊ธฐ ์ฌ์ด ๊ฒ
Simple(๋จ์ํจ): ๋ณต์กํ๊ฒ ์ฝํ์์ง ์์ ๊ฒ, ํ๋์ ์ญํ ๋ง ํ๋ ๊ฒ
์๋ฅผ ๋ค์ด Java์ ๊ฐ๋ณ ๊ฐ์ฒด๋ ์ฝ๊ฒ ์ฌ์ฉํ ์ ์์ง๋ง, ๋จ์ํ์ง ์์.
์๋ํ๋ฉด ๊ฐ์ฒด์ ์ํ๊ฐ ์ธ์ ์ด๋์ ๋ฐ๋์ง ๋ชจ๋ฅด๊ธฐ ๋๋ฌธ์ ๋ณต์ก์ฑ์ด ์ฆ๊ฐํ๊ฑฐ๋ .
๋ฐ๋ฉด Clojure์ ๋ถ๋ณ ๋ฐ์ดํฐ๋ ์ฒ์์ ์ด์ํ๊ฒ ๋๊ปด์ง์ง๋ง, ๋จ์ํด.
๋ฐ์ดํฐ๊ฐ ์ ๋ ๋ฐ๋์ง ์์ผ๋๊น ์ถ๋ก ํ๊ธฐ ์ฝ๊ณ , ๋ฒ๊ทธ๊ฐ ์ค์ด๋ค์ด.
์ด ์ฒ ํ์ด Clojure ์ ์ฒด ์ค๊ณ์ ๋
น์์์ด. ๊ทธ๋์ ์ฒ์์ ์ด์ํด๋ ์ต์ํด์ง๋ฉด ์ง์ง ํธํจ ใ
ใ
Clojure๊ฐ ์ค์ ๋ก ์ด๋์ ์ฐ์ด๋๊ณ ? ์๊ฐ๋ณด๋ค ๋ง์ ๊ณณ์์ ์ฐ์ฌ!
๐ฆ ๊ธ์ต/ํํ
ํฌ
โข Nubank (๋ธ๋ผ์ง ์ต๋ ๋์งํธ ๋ฑ
ํฌ) - Clojure๋ฅผ ์ฃผ๋ ฅ ์ธ์ด๋ก ์ฌ์ฉ
โข Funding Circle - ๋์ถ ํ๋ซํผ
โข Stripe - ์ผ๋ถ ๋ฐฑ์๋ ์๋น์ค
๐ฎ ๊ฒ์/๋ฏธ๋์ด
โข Puppet Labs - ์ธํ๋ผ ์๋ํ
โข Netflix - ์ผ๋ถ ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ
๐ฌ ๋ฐ์ดํฐ/AI
โข Cognitect (Datomic ๊ฐ๋ฐ์ฌ) - Rich Hickey์ ํ์ฌ
โข ๋ค์ํ ๋ฐ์ดํฐ ๊ณผํ ํ๋ก์ ํธ๋ค
ํนํ Nubank์ ์ฌ๋ก๊ฐ ์ ๋ช
ํ๋ฐ, ์๋ฐฑ๋ง ๋ช
์ ์ฌ์ฉ์๋ฅผ ์ฒ๋ฆฌํ๋ ๊ธ์ต ์์คํ
์
Clojure๋ก ๊ตฌ์ถํด์ ์์ ์ฑ๊ณผ ๊ฐ๋ฐ ์์ฐ์ฑ ๋ชจ๋ ๋์๋ค๊ณ ํด. ๋ ์ ๋ ในใ
์์งํ ๋งํ ๊ฒ. Clojure๊ฐ ๋น์ฅ ์ทจ์
์ ๊ฐ์ฅ ์ ๋ฆฌํ ์ธ์ด๋ ์๋์ผ.
Python, JavaScript, Java์ ๋นํ๋ฉด ์ฑ์ฉ ๊ณต๊ณ ์๊ฐ ์ ์ ๊ฑด ์ฌ์ค์ด์ผ.
๊ทผ๋ฐ Clojure๋ฅผ ๋ฐฐ์ฐ๋ฉด ์ป๋ ๊ฒ ์์ด:
1 ํจ์ํ ์ฌ๊ณ ๋ฐฉ์ - ์ด๊ฒ ๋ค๋ฅธ ์ธ์ด์๋ ์ ์ฉ๋จ. JavaScript์์ ํจ์ํ ํจํด ์ธ ๋, Python์์ map/filter ์ธ ๋ ํจ์ฌ ์ ์ดํดํ๊ฒ ๋จ
2 ๋์์ฑ์ ๋ํ ๊น์ ์ดํด - ๋ฉํฐ์ฝ์ด ์๋์ ํ์์ ์ธ ์ง์
3 ๋ถ๋ณ์ฑ์ ๊ฐ์น - React์ ์ํ ๊ด๋ฆฌ, Redux ํจํด ๋ฑ์ด ๋ค Clojure์ ์ํฅ์ ๋ฐ์
4 REPL ์ฃผ๋ ๊ฐ๋ฐ - ํ์์ ํ๋ก๊ทธ๋๋ฐ ๋ฅ๋ ฅ ํฅ์
5 ์ธ์ด ์ค๊ณ์ ๋ํ ํต์ฐฐ - ์ ์ธ์ด๊ฐ ์ด๋ ๊ฒ ์ค๊ณ๋์๋์ง ์ดํดํ๊ฒ ๋จ
์ฌ๋ฅ๋ท์์๋ Clojure๋ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ๊ด๋ จ ์ฌ๋ฅ์ ๊ณต์ ํ๊ฑฐ๋ ๋ฐฐ์ธ ์ ์๋ ๊ธฐํ๊ฐ ์์ผ๋, ๊ด์ฌ ์์ผ๋ฉด ์ฐพ์๋ด! ๐ฏ
Clojure๋ "์์ฐ์ฑ์ ๋์ด๋ ์ธ์ด"๋ผ๊ธฐ๋ณด๋ค "๋ ๋์ ํ๋ก๊ทธ๋๋จธ๊ฐ ๋๊ฒ ํด์ฃผ๋ ์ธ์ด"์ผ.
ํ๋ฒ ์ ๋๋ก ๋ฐฐ์๋๋ฉด ํ์ ์จ๋จน์ ์ ์๋ ์ฌ๊ณ ๋ฐฉ์์ ์ป๊ฒ ๋จ ใ
ใ
brew install clojure (macOS) ๋๋ ๊ณต์ ์ฌ์ดํธ์์ ์ค์น2.
clj ๋ช
๋ น์ด๋ก REPL ์์3.
(println "Hello, Clojure!") ์
๋ ฅ4. ์ด ๊ธ์์ ๋ฐฐ์ด ์์ ๋ค ์ง์ ์คํํด๋ณด๊ธฐ
5. "Clojure for the Brave and True" ๋ฌด๋ฃ ์จ๋ผ์ธ ์ฑ ์ฝ๊ธฐ
์์์ด ๋ฐ์ด์ผ! ์ง๊ธ ๋ฐ๋ก ํด๋ด ๐
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.