본문 바로가기

Study/ETC

Clojure로 LISP 톺아보기 (1) - 데이터 구조

서론

함수형 자바스크립트에 관심이 생겨 공부하던 중 명령형 코드를 함수형 코드로 변환하는 방법에 대해 궁금증이 생겨 대표적인 함수형 언어인 Clojure 에 대해 공부해 보려고 한다. 공부를 통해 내가 얻고자 하는 것을 얻을 수 있으면 좋겠지만 그렇지 않다고 해도 도움이 될 것 같다.

Clojure 란?

범용 함수형 언어

자세한 내용은 해당 링크에서 확인 http://clojure.or.kr/wiki/doku.php?id=lecture:clojure:why_clojure

기초 데이터 구조

단순값

Boolean

참, 거짓을 나타내는 데이터 구조

user=> true true user=> false false

nil

값이 없음을 의미

user=> nil nil

keyword

주로 map 데이터 구조의 키값으로 자주 이용

user=> :key :key

number

숫자를 표현하는 데이터 구조

  • 정수
  • 실수
  • 분수 (Ratio)
user=> 1 1 user=> 0.5 0.5 user=> 1/3 1/3

character

문자 하나를 표현하는 데이터 구조

기존 string 을 이용해 표현 "p" 를 이용해 표현이 가능 하지만 부하를 줄이고 표현의 간결함을 위해 해당 키워드를 사용

user=> \p \p

string

문자열을 표현하는 데이터 구조

user=> "papico" "papico"

컬렉션

list

순서가 있는 데이터 집합, 생성하는 방법은 두가지가 있다.

  • ' 를 이용한 생성 방법
user=> '(1 2 3 4) (1 2 3 4)
  • list 함수를 이용한 생성 방법
user=> (list 1 2 3 4) (1 2 3 4)

각각의 요소들 사이에 쉼표를 넣어 구분할 수 있지만 관용적이지 않다. clojure 에서 쉼표는 무시되기 때문에 요소들 사이는 공백으로 구분하자.

  • list 컬렉션에서 사용할 수 있는 여러가지 함수
user=> (first '(1 2 3 4 5)) 1 user=> (rest '(1 2 3 4 5)) (2 3 4 5) user=> (cons 5 '(1 2 3 4)) (5 1 2 3 4) user=> (last '(1 2 3 4 5)) 5 user=> (second '(1 2 3 4 5)) 2 user=> (count '(1 2 3 4 5)) 5

vector

리스트와 비슷하지만 vector 는 인덱스를 통해 배열 요소에 빠르게 접근이 가능하다.

user=> [1 2 3 4 5] [1 2 3 4 5]
  • vector 컬렉션에서 사용할 수 있는 여러가지 함수
user=> (first [1 2 3 4 5]) 1 user=> (second [1 2 3 4 5]) 2 user=> (rest [1 2 3 4 5]) (2 3 4 5) user=> (nth [1 2 3 4 5] 0) 1 user=> (last [1 2 3 4 5]) 5

list와 vector 모두 nth 함수를 가지고 있다. 하지만 두 컬렉션의 접근 방식의 차이점이 존재한다. list 는 nth 로 접근할때 처음부터 원하는 요소까지 순회하면서 해당 요소를 찾는다. 반면에 vector는 전체를 거치지 않고 바로 요소에 접근하게 된다.

  • conj 함수의 차이

list와 vector 모두 conj 라는 함수를 가지고 있는데 둘에서의 동작이 다르다. 우선 conj 함수는 해당 컬렉션에 새로운 요소를 추가시킨 컬렉션을 리턴을 하는데 해당 데이터 구조에서 가장 효율적인 방법으로 요소를 추가시킨다.

user=> (conj '(1 2 3 4) 5) (5 1 2 3 4) user=> (conj [1 2 3 4] 5) [1 2 3 4 5]

map

key 와 value 를 가지는 데이터 구조로 순서가 보장되지 않는다.

user=> {:name "apple" :price "19"} {:name "apple", :price "19"}

key를 아용해 map에 접근이 가능하다

user=> (:name {:name "apple" :price "19"}) "apple"
  • map 컬렉션에서 사용할 수 있는 여러가지 함수
user=> (get {:name "apple" :price "19"} :name) ;; key 로 value 가져오기 "apple" user=> (keys {:name "apple" :price "19"}) ;; key 들을 list로 반한 (:name :price) user=> (vals {:name "apple" :price "19"}) ;; value 들을 list로 반환 ("apple" "19") user=> (assoc {:name "apple" :price "19"} :taste "sweet") ;; map에 새로운 요소 추가 {:name "apple", :price "19", :taste "sweet"} user=> (dissoc {:name "apple" :price "19"} :price) ;; map에 해당 요소 제거 {:name "apple"} user=> (merge {:name "apple" :price "19"} {:taste "sweet" :origin "korea"}) ;; 여러 map 컬랙션을 합친 새로운 map 을 반환 {:name "apple", :price "19", :taste "sweet", :origin "korea"}

set

집합을 표현하는 컬렉션 데이터 구조로 중복이 없는 컬렉션을 만들때 유용하다. set 또한 요소의 순서를 보장하지 않는다.

#{:green :red :blue :black} user=> #{:red :blue :red} Syntax error reading source at (REPL:5:19). Duplicate key: :red

set은 생성할때 중복을 허용하지 않는다.

여러가지 집합 연산을 사용할 수 있다. 우선 require() 함수를 이용하여 closure.set 라이브러리를 로드해야 한다.

user=> (require '[clojure.set]) nil user=> (clojure.set/union #{:a :b :c} #{:c :d :e}) ;; 합집합 #{:e :c :b :d :a} user=> (clojure.set/difference #{:a :b :c} #{:c :d :e}) ;; 차집합 #{:b :a} user=> (clojure.set/intersection #{:a :b :c} #{:c :d :e}) ;; 교집합 #{:c}
  • set 컬렉션에서 사용할 수 있는 여러가지 함수
user=> (set [:a :b :c :d]) ;; 다른 컬렉션을 set으로 변환해서 반환 #{:c :b :d :a} user=> (set {:a 1 :b 2 :c 3}) #{[:c 3] [:b 2] [:a 1]} user=> (get #{:a :b :c} :a) ;; 요소 반환 :a user=> (:a #{:a :b :c}) ;; 위와 동일 :a user=> (contains? #{:a :b :c} :a) ;; 포함 여부를 boolean 으로 반환 true user=> (contains? #{:a :b :c} :d) false user=> (conj #{:a :b :c} :d) ;; set 내부에 새로운 요소를 추가시킨 set 컬렉션 반환 #{:c :b :d :a} user=> (disj #{:a :b :c} :a) ;; set 내부에 해당 요소를 제거한 set 컬렉션 반환 #{:c :b}

컬렉션들의 공통점

모든 컬렉션은 불변(immutable) 이고 존속적(persistent)이다. 컬렉션을 변형시키는 함수는 원본 컬렉션은 변형하지 않고 새로운 컬렉션을 만들어 리턴한다.

마무리

이번 포스팅에서는 clojure의 데이터 구조에 대해서 알아보았다.

이번 포스팅을 요약한 내용이다.

  • 단순값은 number, string, character, bull, nil, keyword 가 있다.
  • 단순값 데이터 구조 각각의 표현 방법이 다르다
  • 컬렉션 데이터 구조는 list, vector, map, set 이 있다.
  • 각각의 컬렉션마다 제공하는 함수들이 있다.
  • 함수로 인해 컬렉션이 변하지 않고 새로운 컬렉션이 만들어져 리턴한다.
  • 모든 컬렉션은 불변(immutable) 이고 존속적(persistent)

다음 포스팅에서는 clojure의 함수 관련된 내용에 대해 다뤄볼 것이다.