Skip to content

Living Clojure 01

Kyunggook Kim edited this page Aug 15, 2016 · 1 revision

0장. 클로저 REPL 환경설정

  1. 자바 SDK 설치
  2. 클로저 REPL 준비

1장. 클로저의 구조

42
;=> 42
  1. REPL에 숫자 42를 입력하고 엔터키를 누른다.
  2. 이 식의 결과는 자기 자신이다(즉, 자기 자신으로 평가되어 42가 되었음을 의미한다).
  3. 그 식의 결과가 출력된다. 이 책에서 출력 결과는 ;=>를 사용한다.

단순값으로 처음 시작하기

;; 실수값 입력
12.43
;=> 12.43

;; 분수가 평가될 때 실수로 변환 안됨
1/3
;=> 1/3

;; 분수에서 분모와 분자 간에 약분 가능
4/2
;=> 2

;; 분수의 분모와 분자에는 정수만 사용 가능
4.0/2
;=> NumberFomatException Invalid number: 4.0/2

;; 1을 3으로 나눔, 결과 또한 분수
(/ 1 3)
;=> 1/3

;; 인수 중 하나가 실수이기 때문에 결과 또한 실수
(/ 1 3.0)
;=> 0.3333333333333333
  • 클로저에서는 함수나 연산자가 먼저 오고, 필요한 인수가 뒤에 온다.
;; 클로저의 문자열은 큰따옴표로 둘러쌈
"jam"
;=> "jam"

;; 클로저에서 키워드는 콜론으로 시작하는 식별자
:jam
;=> :jam

;; 하나의 문자만 사용하는 경우 역슬래시 뒤에 문자를 붙임
\j
;=> \j

;; 자기 자신으로 평가되는 단순값. 불린(boolean)
true
;=> true
false
;=> false

;; 클로저에서 값이 없음을 표현
nil
;=> nil
(+ 1 1)
;=> 2

(+ 1 (+ 8 3))
;=> 12
  • 식 내부에 식이 포함될 수 있고, 내부의 식이 먼저 평가되고, 그 다음 식이 평가된다.

클로저 데이터를 컬렉션에 담기

  • 리스트 컬렉션 사용하기
    • 리스트의 특징은 요소들이 순서를 가진다.
    • 리스트의 생성은 인용기호(')를 괄호 앞에 붙이고 데이터를 괄호 안에 넣으면 된다.
;; 클로저에서는 문자열, 정수, 키워드 같은 여러 종류의 값을 컬렉션 안에 혼합가능
'(1 2 "jam" :marmalade-jar)
;=> (1 2 "jam" :marmalade-jar)

;; 요소들 사이에 쉼표가 있어도 쉼표는 공백 문자처럼 취급되어 무시
'(1, 2, "jam", :bee)
;=> (1 2 "jam" :bee)
  • 리스트로 무엇을 할 수 있을까?
    • first 함수는 리스트의 첫 요소를 반환
    • rest 함수는 첫 요소를 제외한 나머지 모든 요소들의 리스트를 반환
    • cons 함수는 두 개의 인수, 첫째는 추가하고자 하는 요소, 둘째는 그 요소가 추가될 리스트
    • 인용기호(')와 list 함수로도 리스트 생성 가능
(first '(:rabbit :pocket-watch :marmalade-jar :door))
;=> :rabbit

(rest '(:rabbit :pocket-watch :marmalade-jar :door))
;=> (:pocket-watch :marmalade-jar :door)

(first (rest '(:rabbit :pocket-watch :marmalade-jar :door)))
;=> :pocket-watch

(first (rest (rest '(:rabbit :pocket-watch :marmalade-jar :door))))
;=> :marmalade-jar

(first (rest (rest (rest '(:rabbit :pocket-watch :marmalade-jar :door)))))
;=> :door

(first (rest (rest (rest (rest '(:rabbit :pocket-watch :marmalade-jar :door))))))
;=> nil

(cons 5 '())
;=> (5)

;; nil로 리스트 만들기
(cons 5 nil)
;=> (5)

(cons 4 (cons 5 nil))
;=> (4 5)

(cons 3 (cons 4 (cons 5 nil)))
;=> (3 4 5)

(cons 2 (cons 3 (cons 4 (cons 5 nil))))
;=> (2 3 4 5)

'(1 2 3 4 5)
;=> (1 2 3 4 5)

(list 1 2 3 4 5)
;=> (1 2 3 4 5)
  • 벡터를 사용해 인덱스로 데이터 접근하기
    • 대괄호로 둘러싸서 만든다.
    • first, rest 함수는 리스트와 동일하게 동작한다.
    • nth 함수를 통해 해당 인덱스에 있는 벡터의 요소에 접근할 수 있다.
    • 인덱스는 0번부터 시작한다.
    • last 함수는 벡터의 마지막 요소를 반환한다.
    • 리스트는 처음 시작부터 원하는 요소까지 탐색하기 때문에 벡터의 nthlast가 성능이 더 좋다.
    • 따라서 인덱스로 컬렉션의 요소에 접근할 필요가 있다면 벡터를 쓴다.
[:jar1 1 2 3 :jar2]
;=> [:jar1 1 2 3 :jar2]

(first [:jar1 1 2 3 :jar2])
;=> :jar1

(rest [:jar1 1 2 3 :jar2])
;=> [1 2 3 :jar2]

(nth [:jar1 1 2 3 :jar2] 0)
;=> :jar1

(nth [:jar1 1 2 3 :jar2] 2)
;=> :jar2

;; 벡터에 last 적용
(last [:rabbit :pocket-watch :marmalade])
;=> :marmalade

;; 리스트에 last 적용
(last '(:rabbit :pocket-watch :marmalade))
;=> :marmalade
  • 컬렉션의 공통점들
    • 모든 컬렉션은 불변(immutable)이고 존속적(persistent)이다.
    • 불변은 컬렉션의 값이 변하지 않는다는 것을 의미한다.
    • 요소를 컬렉션에 추가하면 새로 생성된 컬렉션이 반환된다.
    • 존속이란 구조 공유(structual sharing) 기법을 통해 효율적으로 만들어진다는 것을 의미한다.
;; count 함수는 컬렉션의 크기를 반환
(count [1 2 3 4])
;=> 4

;;`conj` 함수는 해당 데이터 구조에 가장 효율적인 방식으로 컬렉션에 요소를 추가한다.
;; conj는 벡터의 맨 뒤에 요소를 추가하여 새로운 벡터를 반환한다.
(conj [:toast :butter] :jam)
;=> [:toast :butter :jam]

;; 여러 개의 요소를 벡터의 맨 뒤에 추가하여 새로운 벡터를 반환한다.
(conj [:toast :butter] :jam :honey)
;=> [:toast :butter :jam :honey]

;; conj는 리스트의 맨 앞에 요소를 추가하여 새로운 리스트를 반환한다.
(conj '(:toast :butter) :jam)
;=> (:jam :toast :butter)

;; 여러 개의 요소를 리스트의 맨 앞에 추가하여 새로운 리스트를 반환한다.
(conj '(:toast :butter) :jam :honey)
;=> [:honey :jam :toast :butter)
  • 맵을 사용해 키-값 쌍의 데이터를 저장하기
    • 맵은 중괄호로 둘러싼다.
    • 가독성을 위해 맵에서 쉼표를 사용할 수 있다.
    • get 함수로 맵에서 값을 가져올 수 있다.
    • 맵의 키가 키워드라면 get을 사용하지 않고 키 자체를 함수로 사용할 수 있다.
    • keys 함수는 맵의 키만을, vals 함수는 맵의 값만을 반환한다.
    • assoc 함수는 맵에 새로운 키-값 쌍을 결합하여 새로운 맵을 반환한다.
    • dissoc 함수는 맵과 키를 인수로 받아 그 키-값 쌍이 제거된 새로운 맵을 반환한다.
    • merge 함수는 여러 맵의 키-값 쌍을 합쳐서 새로운 맵을 반환한다.
{:jam1 "strawberry" :jam2 "blackberry"}
;=> {:jam1 "strawberry", :jam2 "blackberry"}

{:jam1 "strawberry", :jam2 "blackberry"}
;=> {:jam1 "strawberry", :jam2 "blackberry"}

;; get을 명시적으로 사용한 예
(get {:jam1 "strawberry", :jam2 "blackberry"} :jam2)
;=> "blackberry"

;; 키가 없는 경우 디폴트 값
(get {:jam1 "strawberry", :jam2 "blackberry"} :jam3 "not found")
;=> "not found"

;; 키를 함수로 사용해 값 가져오기
(:jam2 {:jam1 "strawberry", :jam2 "blackberry", :jam3 "marmalade"})
;=> "blackberry"

;; keys 함수
(keys {:jam1 "strawberry", :jam2 "blackberry", :jam3 "marmalade"})
;=> (:jam1 :jam2 :jam3)

;; vals 함수
(vals {:jam1 "strawberry", :jam2 "blackberry", :jam3 "marmalade"})
;=> ("strawberry", "blackberry", "marmalade")

;; assoc 함수
(assoc {:jam1 "red", :jam2 "black"} :jam3 "orange")
;=> {:jam1 "red", :jam2 "black", :jam3 "orange"}

;; dissoc 함수
(dissoc {:jam1 "strawberry", :jam2 "blackberry"} :jam1)
;=> {:jam2 "blackberry"}

;; merge 함수
(merge {:jam1 "red" :jam2 "black"}
       {:jam1 "orange" :jam3 "red"}
       {:jam4 "blue"})
;=> {:jam1 "orange", :jam2 "black", :jam3 "red", :jam4 "blue"}
  • 집합을 사용해 유일한 데이터의 컬렉션 표현하기
    • 집합은 요소의 중복이 없는 컬렉션을 만들 때 유용하다.
    • 집합은 #{}로 둘러싼다.
    • 합집합, 차집합, 교집합 등의 집합 연산이 가능하다.
    • set함수를 이용해 다른 종류의 컬렉션을 집합으로 바꿀 수 있다.
#{:red :blue :white :pink}
;=> #{:white :red :blue :pink}

;; 집합을 생성할 때 중복은 허용되지 않는다.
#{:red :blue :white :pink :pink}
;=> IllegalArgumentException Duplicate key: :pink

;; 합집합
(clojure.set/union #{:r :b :w} #{:w :p :y})
;=> #{:y :r :w :b :p}

;; 차집합
(clojure.set/difference #{:r :b :w} #{:w :p :y})
;=> #{:r :b}

;; 교집합
(clojure.set/intersection #{:r :b :w} #{:w :p :y})
;=> #{:w}

;; 벡터를 집합으로 바꾸기(중복 제거됨)
(set [:rabbit :rabbit :watch :door])
;=> #{:door :watch :rabbit}

;; 맵을 집합으로 바꾸기
(set {:a 1 :b 2 :c 3})
;=> #{[:c 3] [:b 2] [:a 1]}

;; get 함수를 사용해서 요소 찾기
(get #{:rabbit :door :watch} :rabbit)
;=> :rabbit

(get #{:rabbit :door :watch} :jar)
;=> :nil

;; 키워드를 사용해서 요소 찾기
(:rabbit #{:rabbit :door :watch})
;=> :rabbit

;; 집합 자체를 함수로 사용해서 요소 찾기
(#{:rabbit :door :watch} :rabbit)
;=> :rabbit

;; contains?를 사용해서 요소가 있는지 확인
(contains? #{:rabbit :door :watch} :rabbit)
;=> true

(contains? #{:rabbit :door :watch} :jam)
;=> false

;; conj 함수를 사용해 집합에 요소 추가
(conj #{:rabbit :door} :jam)
;=> #{:door :rabbit :jam}

;; disj 함수는 집합에서 요소를 제거할 때 사용
(disj #{:rabbit :door} :door)
;=> #{:rabbit}
  • 단순값과 컬렉션에 대한 요약
    • 문자열, 정수, 분수, 실수, 키워드, 문자, 불린은 함수나 식에서 사용될 수 있다.
    • 클로저의 식에서는 연산자나 함수가 먼저 오고, 인수가 뒤따라온다.
    • 리스트는 맨 앞에서부터 접근할 수 있는 데이터 컬렉션이다.
    • 벡터는 임의의 위치로 접근할 수 있는 데이터 컬렉션이다.
    • 맵은 키-값 쌍들로, 데이터를 구성하고 쉽게 접근하는 데 좋다.
    • 집합은 유일한 요소들의 컬렉션으로, 집합 연산이 가능하다.
Clone this wiki locally