조건문, 재귀문, 구조분해
별거없다
empty?로 컬렉션이 비었는지 검사한다seq로 컬렉션이 비지 않았는지 검사한다every?로 요소 전체에 대한 검사가 참인지 검사한다not-any?로 요소 전체에 대한 검사가 거짓인지 검사한다some으로 요소 중 일부에 대한 검사가 참인지 검사한다
흐름 제어(if, when)
If문
(if [조건] (참일때 결과) (거짓일때 결과)) 의 꼴을 가진다.

여기서 let 과 if를 결합시킬수있다. 예를 들어
user=> (let [need-to-grow-small (> 5 3)]
#_=> (if need-to-grow-small "drink bottle" "don't drink bottle"))
"drink bottle"
user=> (if-let [need-to-grow-small (> 5 3)] "drink bottle" "don't drink bottle")
"drink bottle"let 내부에서 심볼을 바인딩하고 이를 if 문에서 평가를 하게 작성할수있는데, 이를 if-let으로 간략하게 표현할수있다.
그리고 if문을 사용하기 위해서는 clojure에선 반드시 참인경우와 거짓인 경우 둘다 명시해줘야하는데, 참인 경우에 대해서만 작성하고 싶을땐 when을 사용하면된다
user=> (defn drink [need-to-grow-small]
#_=> (when need-to-grow-small "drink-bottle"))
#'user/drink
user=> (drink true)
"drink-bottle"
user=> (drink false)
nilwhen 또한 let을 결합하여 when-let으로 간략하게 표현할수 있다
Cond, case
cond는 쉽게 말해 if, else if 와 같고, case는 switch문과 같다
user=> (let [x 5]
#_=> (cond
#_=> (> x 6) "bigger than 6"
#_=> (> x 3) "bigger than 3"
#_=> :else "default")
#_=> )
"bigger than 3"위와 같이 cond를 사용하여 if, else if, else 문을 구현할 수있다.
clojure에서 else 문은 따로 방법이:else 처럼 존재하는 것이 아니고 그냥 마지막 요소에 논리적으로 참으로 평가되는 값을 쓰고 디폴트값을 써줌으로써 else로 사용할수 있다.
; 2 나오는 경우
user=> (let [number 2]
#_=> (case number
#_=> 1 "number one"
#_=> 2 "number two"
#_=> 3 "number three"
#_=> "default"))
"number two"
; default 나오는 경우
user=> (let [number 5]
#_=> (case number
#_=> 1 "number one"
#_=> 2 "number two"
#_=> 3 "number three"
#_=> "default"))
"default"위와 같이 case를 사용하여 switch 문을 구현할 수 있다.
따로 default값은 마지막 요소에 참으로 평가되는 값을 써주면 해당 값이 반환된다.
title: 커링 및 함수 합성
clojure에는 커링을 가능케하는 `partial` 함수
그리고 함수 함수를 가능케하는 `comp` 함수가 존재한다.
partial의 경우는 **다중인자를 받는 함수를 단일 함수들로 연결**하게 해주고 이는 어떠한 함수에 고정적인 인수가 들어가야하는 경우 partial함수를 통해 새로운 함수를 만들어 줄수 있다.
comp의 경우 함수 내부에 다른함수의 평가값이 필요하고 그런게 잦아들게 되면 두 함수를 합성하여 새로운 함수를 만들어 줄수 있다.구조 분해
별거 없고 let내부에서 혹은 let과 유사한 구조에서 키와 쌍 구조를 띄면 해당 값들은 전부 바인딩 된다
user=> (let [[x y] [1 2]] (str x "-" y))
"1-2"
user=> (let [{x :x y :y} {:x "flower1" :y "flower2"}] (str x "-" y))
"flower1-flower2"
; :or을 사용하여 값을 없을시 기본값 세팅을 할 수있고 :as를 사용하여 전체 값을 변수에 담을 수 있다.
user=> (let [{x :x y :y :or {x "missing1" y "missing2"} :as original}
#_=> {:x "flower1"}] (str x "-" y " " original))
"flower1-missing2 {:x \"flower1\"}"
; keys를 사용한 바인딩 방식을 가장 많이 사용함
user=> (let [{:keys [flower1 flower2]} {:flower1 "blue" :flower2 "red"}]
#_=> (str flower1 " and " flower2))
"blue and red"
; keys를 사용하여 더 직관적이고 편하게 함수에서 바인딩을 형성할 수 있다.
user=> (defn binding-test [{:keys [flower1 flower2]}]
#_=> (str flower1 " - " flower2))
#'user/binding-test
user=> (binding-test {:flower1 "red" :flower2 "blue"})
"red - blue"
title: 지연 평가
**지연 평가는 무한 리스트를 다룰 수 있게 해준다**
지연 시퀀스를 반환하는 함수들이 있다
예를 들어 `range`, `repeat`, `repeatedly`, `cycle`, `rest`, `map` 등등 많은 함수들이 존재하고
해당 함수들을 그대로 사용하면 무한시퀀스가 발생하여 REPL혹은 프로그램이 멈춘다.
따라서 `take` 함수로 지연시퀀스를 안전하게 처리할 수있다.
```clojure
user=> (take 10 (range))
(0 1 2 3 4 5 6 7 8 9)
user=> (take 5 (repeat "rabbit"))
("rabbit" "rabbit" "rabbit" "rabbit" "rabbit")
user=> (take 5 (repeatedly #(rand-int 10)))
(9 5 6 7 6)
user=> (take 3 (cycle ["big" "small"]))
("big" "small" "big")
user=> (take 3 (rest (cycle ["big" "small"])))
("small" "big" "small")
```재귀문
일단 우리가 아는 방식대로 재귀문을 만들어보자
user=> (defn alice-is [in out]
#_=> (if (empty? in)
#_=> out ; in에 더이상 요소가 남지 않으면 out 반환
#_=> (alice-is (rest in) ;in의 나머지 부분
#_=> (conj out (str "Alice is " (first in))))))
#'user/alice-is
user=> (alice-is ["normal" "too small" "too big"] [])
["Alice is normal" "Alice is too small" "Alice is too big"]뭔가 꺼림직하지만 재귀문이 만들어졌다. 어느 부분이 꺼림직할까.. 바로 out이 따로 존재한다는점, 따로 반환 되야할 컬렉션을 명시하는 부분이 우리가 알고 있던것과 비교했을때 어색하다..
하지만 이는 함수의 순수성을 지키기 위해 존재하는 것으로 어색할 수 밖에 없다. 이를 완화할 방법은 뒤에 loop를 사용하면서 나온다.
하지만 이 방법의 치명적인 단점은 따로 존재한다.
user=> (defn countdown [n]
#_=> (if (= n 0) n (countdown (- n 1))))
#'user/countdown
; Stack Over Flow 에러
user=> (countdown 100000)
Execution error (StackOverflowError) at user/countdown (REPL:2).
null치명적인 단점은 바로 위와 같은 방식으로 재귀문을 작성하면 함수가 스택에 계속 적재되어 스택 오버플로우가 발생한다.
따라서 재귀문을 사용할땐 loop와 recur를 사용해야한다. loop 와 recur를 사용하면 매번 호출할 때 하나의 스택만 사용된다.
; loop 없이 recur만 사용하면 함수 자체가 loop로 인식된다
user=> (defn countdown-refine [n]
#_=> (if (= n 0) n
#_=> (recur (- n 1)))
#_=> )
#'user/countdown-refine
user=> (countdown-refine 100000)
0여기에 이어 아까 in out에서 따로 정의해줘야하는 것이 불편했던것도 loop를 사용하면서 완화할수있다.
loop를 사용하면 함수 중간에 loop시작할 지점을 지정해줄수있고, loop시 마다 사용되고 유지될 변수를 지정할수있다.
; loop 사용하여 재귀에서 사용할 변수 지정 가능 (let 처럼 사용)
user=> (defn alice-is-refine [input]
#_=> (loop [in input out []]
#_=> (if (empty? in) out
#_=> (recur (rest in) (conj out (str "Alice is " (first in)))))))
#'user/alice-is-refine
user=> (alice-is-refine ["normal" "too small" "too big"])
["Alice is normal" "Alice is too small" "Alice is too big"]title: map 과 reduce
**map은 지연시퀀스를 반환**한다 따라서 무한 시퀀스를 다룰 수있다.
```clojure
user=> (map #(str %) ["asd" "dsa" "qwe"])
("asd" "dsa" "qwe")
user=> (take 3 (map #(str %) (range)))
("0" "1" "2")
```
map 은 또한 한개 이상의 컬렉션을 인자로 받을 수 있는데, 이때 함수의 인자로 각각의 컬렉션 요소가 사용된다.
```clojure
user=> (def animals ["mouse" "duck" "rabbit" "dog"])
#'user/animals
user=> (def colors ["blue" "red" "pink" "black"])
#'user/colors
user=> (defn animal-color [animal color]
#_=> (str animal " - " color))
user=> (map animal-color animals colors)
("mouse - blue" "duck - red" "rabbit - pink" "dog - black")
; 1대1 매칭이 아니라 더 작은 컬렉션
user=> (def two-colors ["black" "pink"])
#'user/two-colors
; 작은 컬렉션을 기준으로 map이 평가된다
user=> (map animal-color animals two-colors)
("mouse - black" "duck - pink")
```
**reduce는 무한 시퀀스르 다룰수 없다 (함수의 입력컬렉션이 모두 없어질때까지 실행하기 때문)**
```clojure
; 초기값이 없을시 컬렉션의 첫번째 값이 초기값이 된다.
user=> (reduce + [1 2 3 4 5])
15
; 2 + 3 * 3 + 4 * 4
user=> (reduce (fn [r x] (+ r (* x x))) [2 3 4])
27
; 0 + 2 * 2 + 3 * 3 + 4 * 4
user=> (reduce (fn [r x] (+ r (* x x))) 0 [2 3 4])
29
; 배열도 마찬가지 방법으로 적용 가능 (단 초기값을 잘 써줘야함)
user=> (reduce #(conj %1 (* %2 %2)) [] [1 2 3 4 5])
[1 4 9 16 25]
```그 밖에도
많은 유용한 함수들이 있다.
진위함수의 역을 반환하는 complement
filter, remove, flatten, partition, split-with 등등 많은 함수들이 있지만
이러한 것들은 필요시에 검색하여 더 알아보는 것으로 하고 가장 기본이 되는 for 에 대해서만 간단하게 짚어보고 끝내려한다
(for [순환할 요소들] 평가부) 로 구성되어있다.
For문
; animal의 각 요소에 따라 for문을 돔
user=> (for [animal ["dog" "cat" "duck"]]
#_=> (str animal " is cute"))
("dog is cute" "cat is cute" "duck is cute")
; name 함수를 사용하면 키워드를 문자열로 평가하여 반환함
user=> (for [animal [:dog :cat :duck]]
#_=> (str (name animal) " is cute"))
("dog is cute" "cat is cute" "duck is cute")
; 이중 포문
user=> (for [animal [:dog :cat :duck]
#_=> color [:blue :red]]
#_=> (str (name animal) " is " (name color)))
("dog is blue" "dog is red" "cat is blue" "cat is red" "duck is blue" "duck is red")
; 포문 내 :let 수정자 (내부에서 효율적인 관리)
; :let을 사용해 for문을 돌 값 이외의 정보를 캐싱 및 관리해 줄 수있다
user=> (for [
#_=> animal [:dog :cat :duck]
#_=> color [:blue :red]
#_=> :let [animal-color (str (name animal) " is " (name color))]
#_=> ]
#_=> (str animal-color "-" animal-color ))
("dog is blue-dog is blue" "dog is red-dog is red" "cat is blue-cat is blue" "cat is red-cat is red" "duck is blue-duck is blue" "duck is red-duck is red")