지금까지는 데이터 하나하나만을 다뤘습니다: 숫자, 문자열, 값. 프로그래밍시에는 데이터들의 묶음, 즉
콜렉션
을 가지고 작업하는 경우가 더 많습니다.
클로저는 이런 collections와 함께 작업할 수 있는 훌륭한 기능들을 갖추고 있습니다. 클로저는 네 가지의 collections을 제공할 뿐만 아니라, 이 모든 collection들을 함께 사용하는 일관된 방법을 제공합니다.
벡터는 순서가 있는 값들의 모음입니다. 벡터는 비어있을 수 있으며, 서로 다른 타입의 값들을 담을 수 있습니다. 벡터의 값들은 순서를 매길 때에 0부터 세고, 이 숫자를 인덱스라고 합니다. 인덱스는 값들을 참조할 때 사용합니다.
벡터를 상상하기 위해서는 일정한 크기의 칸으로 나누어진 박스를 상상해 보세요. 각각의 칸은 수를 가지고 있습니다. 각각의 칸에는 데이터들을 넣을 수 있고 번호가 있기 때문에 어디서 그 데이터를 찾아야 할 지 알 수 있습니다.
그 숫자는 0부터 시작되한다는 것을 주의하세요. 이상하게 보일 수도 있지만 프로그래밍 할 때는 0부터 계산해야 합니다.
벡터는 원하는 개수의 값들을 [] 안에 공백으로 구분하여 써서 사용합니다. 다음은 벡터에 관한 예제들 입니다:
[1 2 3 4 5]
[56.9 60.2 61.8 63.1 54.3 66.4 66.5 68.1 70.2 69.2 63.1 57.1]
[]
한 쌍의 거북이들이 있을때,
(turtle-name)
명령은 벡터의 형태로 거북이의 이름을 반환합니다.
(turtle-names)
;=> [:trinity :neo :oracle :cypher]
다음 두 함수는 새로운 벡터를 만들 때 사용합니다.
vector
함수는 임의의 개수의 인수들을 받고, 그것들을 모두 담은 벡터를 반환합니다.conj
는 모든 콜렉션 자료형에 사용할 수 있는 흥미로운 함수입니다. 첫번쨰 인수가 벡터인 경우에는, 그 벡터의 맨 끝에 두번째 인수로 주어진 값을 추가한 새로운 벡터를 반환합니다.conj
는 왜 이름이conj
일까요?conj
는 join or combine을 의미하는 conjoin을 짧게 쓴 것입니다. 이것이 바로 우리가 하는 일입니다: 벡터에 새로운 값을 추가합니다.
(vector 5 10 15)
;=> [5 10 15]
(conj [5 10] 15)
;=> [5 10 15]
다음 네개의 함수를 살펴봅시다.
count
는 벡터 안에 있는 값의 개수를 알려줍니다.nth
는 벡터안의 n번째 값을 알려줍니다. 이때 0부터 세야한다는 것을 주의하세요. 따라서 이 경우에는 1과 함께 사용된nth
는 두번째 값(인덱스는 0부터 시작한다는 점에 주의!)을 알려줍니다.first
는 벡터에서 첫번째 값을 반환해줍니다.rest
는 첫번째 값을 제외한 나머지 모든 값들을 반환해 줍니다. 혼란스러울 수 있으므로nth
와 동시에 생각하지 않도록 하세요.
(count [5 10 15])
;=> 3
(nth [5 10 15] 1)
;=> 10
(first [5 10 15])
;=> 5
(rest [5 10 15])
;=> (10 15)
walk.clj
파일로 이동하세요.walk.clj
파일의 맨 끝에 (add-turtle :neo)
를 추가하세요(add-turtle :oracle)
을 하단 REPL창에 입력하세요.(turtle-names)
를 입력하고 결과를 확인하세요.myproject
의 core.clj
로 이동해서 InstaREPL을 실행하세요.nth
함수를 사용하세요.맵은 키 값과 그에 관련된 value 값의 집합이 들어있습니다. 이는 사전을 생각하면 됩니다: 우리는 단어(keyworld)를 사용해서 뜻(value)을 찾아봅니다. 만약 다른 언어를 이용해서 프로그래밍한다면 dictionary, hash, associative array와 같은 이름으로 맵과 유사한 것을 볼 수 있을 것입니다.
맵은 중괄호 안에 키와 값을 교대로 써서 만듭니다.
맵은 우리가 일반적으로 생각하는 방식으로 데이터를 저장할 수 있기 때문에 유용합니다. Sally Brown을 예로 들어봅시다. 맵에 그녀의 성과 이름, 주소, 좋아하는 음식 등의 정보를 저장할 수 있습니다. 이는 해당 데이터를 모으고 보기 편하게 만드는 간단한 방법입니다. 마지막 예시는 비어있는 맵입니다. 이는 값을 저장할 준비는 되어있지만 아직 아무것도 없는 맵입니다.
{:first "Sally" :last "Brown"}
{:a 1 :b "two"}
{}
거북이가
forward
나right
같은 명령을 받으면 맵의 맵 형태로 결과를 반환해 줍니다.
(forward 40)
;=> {:trinity {:length 40}}
(right 90)
;=> {:trinity {:angle 90}}
assoc
과dissoc
은 한 쌍의 함수입니다: 이것들은 맵에서 항목들을 연결하거나 연결을 끊습니다.assoc
을 이용해서 어떻게 성 “Brown”을 추가하고,dissoc
을 이용해서 그것을 삭제하는지 알아봅시다.merge
는 두 개의 맵을 합친 새로운 맵을 반환합니다.
(assoc {:first "Sally"} :last "Brown")
;=> {:first "Sally", :last "Brown"}
(dissoc {:first "Sally" :last "Brown"} :last)
;=> {:first "Sally"}
(merge {:first "Sally"} {:last "Brown"})
;=> {:first "Sally", :last "Brown"}
count
는 모든 collection에서 사용할 수 있는 함수입니다. 왜 답이 2라고 생각하나요?count
는 키-값 쌍의 개수를 반환해 주기 때문입니다.
맵은 키-값의 쌍으로 이루어져있기 때문에, 키는 맵에서 값을 얻기 위해서 사용이 됩니다. 다음은 클로저에서 종종 사용되는 방법입니다. 맵에서 값을 찾기 위해서 키는 함수처럼 사용될 수 있습니다. 마지막 예에선,
:MISS
라는 키를 제공하고 있습니다. 이는 우리가 찾는 키값이 맵에 존재하지 않을 때 반환됩니다.
(count {:first "Sally" :last "Brown"})
;=> 2
(get {:first "Sally" :last "Brown"} :first)
;=> "Sally"
(get {:first "Sally"} :last)
;=> nil
(get {:first "Sally"} :last :MISS)
;=> :MISS
keys
와vals
라는 간단한 함수들이 있습니다: 맵에서 키들과 값들을 반환합니다. 순서는 보장할 수 없기 때문에(:first :last)
나(:last :first)
의 형태로 값이 반환될 것입니다.
(keys {:first "Sally" :last "Brown"})
;=> (:first :last)
(vals {:first "Sally" :last "Brown"})
;=> ("Sally" "Brown")
맵을 생성한 후, 키에 새로운 값을 연결시키기를 원할 수 있습니다.
assoc
함수는 기존의 키에 새로운 값을 추가할 때 사용될 수 있습니다. 또,update
라는 편리한 함수도 있습니다.update
는 맵과 키를 함께 사용합니다. 지정된 키가 함수의 첫번 째 인자가 됩니다.update-in
함수는update
처럼 작동하지만 중첩된 맵의 경로에 서 업데이트하기 위해 키의 벡터가 인수로 필요합니다.
(def hello {:count 1 :words "hello"})
(update hello :count inc)
;=> {:count 2, :words "hello"}
(update hello :words str ", world")
;=> {:count 1, :words "hello, world"}
(def mine {:pet {:age 5 :name "able"}})
(update-in mine [:pet :age] - 3)
;=> {:pet {:age 2, :name "able"}}
수, 키워드, 문자열과 같은 값들만이 콜렉션에 넣을 수 있는 것은 아닙니다. 다른 콜렉션들도 콜렉션에 넣을 수 있으므로 맵의 벡터, 벡터의 리스트와 같이 데이터에 맞는 조합을 가질 수 있습니다.
(state-all)
;=> [{:trinity {:x -1.7484556000744965E-6, :y 39.99999999999996, :angle 90, :color [106 40 126]}}
{:neo {:x 21.213202971967114, :y 21.213203899225725, :angle 45, :color [0 64 0]}}
{:oracle {:x -49.99999999999981, :y -4.3711390001862375E-6, :angle 180, :color [43 101 236]}}]
(def states (state-all))
;=> #'clojurebridge-turtle.walk/states
(first states)
;=> {:trinity {:x -1.7484556000744965E-6, :y 39.99999999999996,
:angle 90, :color [106 40 126]}}
(def st (first states))
;=> #'clojurebridge-turtle.walk/st
st
;=> {:trinity {:x -1.7484556000744965E-6, :y 39.99999999999996,
;=> :angle 90, :color [30 30 30]}}
(get st :trinity)
;=> {:x -1.7484556000744965E-6, :y 39.99999999999996,
;=> :angle 90, :color [30 30 30]}
(get-in st [:trinity :angle])
;=> 90
walk.clj
파일로 이동하세요.REPL에서 코드를 입력할 때 __enter__를 잊지 마세요.
(state-all)
(def states (state-all))
(first states)
(def st (first states))
st
(get st :trinity)
(get-in st [:trinity :angle])
첫번째 슬라이드로 돌아가거나 curriculum outline으로 가세요.