User Tools

Site Tools


lecture:4clojure:level-elementary-easy

Elementary/Easy 레벨 문제풀이를 위한 Clojure 문법

키워드 자료형

키워드(Keyword)는 Java의 Enum 과 같은 개념으로 생각하면 된다. 즉 임의의 상수이다. :으로 시작하는 임의의 문자열은 키워드이다.

:a 
:1 
:@

Boolean과 Equality

Clojure에서 참/거짓을 나타내는 논리형 데이타는 true와 false 이다.

(= 1 1) ;==> true
(= 1 2) ;==> false
 
(if true 1 2) ;==> 1
(if false 1 2) ;==> 2
(= true (not false)) ;==> true

false 과 nil 이외의 모든 값은 참이다.

(if false 1 2) ;==> 2
(if nil 1 2) ;==> 2
 
(if 0 1 2) ;==> 1    0은 숫자
(if 1 1 2) ;==> 1    1은 숫자
(if "" 1 2) ;==> 1   "" empty string
(if \A 1 2) ;==> 1   \A 는 문자(Character)
(if [] 1 2) ;==> 1   [] empty vector

변환 (int/float)

(int 1)   ;=> 1
(int 1M)  ;=> 1
(int 1.2) ;=> 1
(int \1)  ;=> 49
(int \a)  ;=> 97
(float 1) ;=> 1.0

Java Interop

Clojure는 Java 코드를 그대로 가져다 사용할 수 있는 방식을 제공하는데, 이것을 Java Interop이라고 한다.
Clojure의 문자는 Java의 Character 인스턴스에, 스트링은 Java의 String 인스턴스이다.
Java 오브젝트의 메소드를 다음과 같이 호출할 수 있다.

(.toUpperCase "abc") ;==> "ABC"

클래스의 정적 메소드는 다음과 같이 호출할 수 있다.

(Character/isUpperCase \A) ;==> true

함수 호출

Clojure는 Lisp이기 때문에 괄호를 사용한다.
괄호의 첫 요소는 함수이고, 나머지는 파라미터이다.

(str "Hello " "World!") ;==> "Hello World!"

따라서 수치연산 함수는 전위연산자이다.

(+ 1 1) ; => 2
(- 2 1) ; => 1
(* 1 2) ; => 2
(/ 2 1) ; => 2
 
(+ 1 (- 3 2)) ; = 1 + (3 - 2) => 2

익명 함수

(fn [s] (println "hello, " s))
#(println "hello, " %)
 
(#(* % %) 5) ;==> 25
((fn [n] (* n n)) 5) ;==> 25
 
(#(* %1 %2) 5 4) ;==> 20
((fn [x y] (* x y)) 5 4) ;==> 20

자료 구조

Clojure에는 4개의 자료 구조가 있다 : List, Vector, Set, Map

(1 2 3)  ; 리스트는 ()로 묶는다. 순서가 있으며 index를 통한 임의 접근이 않된다.
[1 2 3]  ; 벡터는 []로 묶는다. 순서가 있으며 index를 통한 임의 접근이 된다.
#{1 2 3} ; 집합은 #{}로 묶는다. 순서가 없으며 value를 통한 접근이 된다. value는 중복값을 가질 수 없다.
{:a 1 :b 2 :c 3}  ; 맵은 {}로 묶는다. 순서가 없으며 key를 통한 접근이 된다. key는 중복값을 가질 수 없다.

리스트를 제외한 나머지 자료구조는 인덱스나 키로 요소를 가져올 수 있다.

; 벡터는 인덱스로 값을 가져온다.
([1 2 3] 0) ;=> 1 ; 벡터의 첫 요소는 0는 인덱스 번호 0이다.
([1 2 3] 3) ;=> nil ; 인덱스 번호 3은 네번째 요소로 존재하지 않는다.
 
; 맵은 키로 값을 가져온다.
({:a 1 :b 2 :c 3} :a) ;=> 1   ;=> :a키에 해당하는 값은 1이다.
({:a 1 :b 2 :c 3} :d) ;=> nil ;=> :d라는 키는 없기 때문에 nil을 리턴
({:a 1 :b 2 :c 3} :d 4) ;=> 4 ;=> 맵에 해당키가 없는 경우 디폴트 값을 지정할 수 있다.
 
; 집합은 요소 자신을 통해 값을 가져온다. (결국 존재여부 확인)
(#{1 2 3} 1) ;=> 1  ; 집합은 값과 키가 같은 맵이라고 생각하면 된다. 앞의 집합은 {1 1 2 2 3 3} 맵으로 생각할 수 있다.
(#{1 2 3} 0) ;=> nil ; 앞의 집합에서 1은 있으나 0은 없다.

get 함수를 통해 명시적으로 요소를 가져올 수 있다.

(get [1 2 3] 0) ;=> 1 ; 인덱스는 0부터 시작한다.
 
(get {:a 1 :b 2 :c 3} :a) ;=> 1   
(get {:a 1 :b 2 :c 3} :d) ;=> nil 
(get {:a 1 :b 2 :c 3} :d 4) ;=> 4 ; 4는 디폴트 값.
 
(get #{1 2 3} 1) ;=> 1  
(get #{1 2 3} 0) ;=> nil

get 함수를 통하면 벡터와 집합에 해당 요소가 없을 경우 디폴트 값을 지정할 수 있다.

(get [1 2 3] 3) ;=> nil
(get [1 2 3] 3 100) ;=> 100
 
(get #{1 2 3} 0) ;=> nil
(get #{1 2 3} 0 100) ;=> 100

맵에서 해당키의 값이 nil일 경우, contains?로 키의 존재 여부를 확인할 수 있다.

({:a nil} :b) => nil
({:a nil} :a) => nil
(contains? {:a nil} :a) ;=> true

벡터는 nth로도 값을 가져올 수 있다.

(nth [1 2 3] 0) ;=> 1
(nth [1 2 3] 3) ;=> 예외(IndexOutOfBoundsException) 발생
(nth [1 2 3] 3 3) ;=> 3

생성 함수(list/vector/hash-set/hash-map)

(list 1 2 3)             ;=> (1 2 3)
(vector 1 2 3)           ;=> [1 2 3]
(hash-set 1 1 2 2 2 3 3) ;=> #{1 2 3}
(hash-map :a 1 :b 2)     ;=> {:a 1 :b 2}

vec/vector

vec
리스트/맵/집합을 벡터로 바꾼다.

(vec '(1 2 3)) ;=> [1 2 3]
(vec #{1 2 3}) ;=> [1 2 3]
(vec {:a 1 :b 2 :c 3}) ;=> [[:c 3] [:b 2] [:a 1]]

vector
인자들로 구성된 벡터를 만든다.

(vector 1 2 3) ;=> [1 2 3]

자료구조에 추가/삭제 : cons/conj/pop/disj/assoc/dissoc/assoc-in

리스트에 추가

(cons 0 '(1 2 3)) ;=> (0 1 2 3)
(conj '(1 2 3) 4) ;=> (4 1 2 3)
(pop '(1 2 3)) ;=> (2 3)

Clojure는 코딩시 (1 2 3) 하면 위에서 함수를 설명한 것처럼 괄호()의 첫 요소인 1을 함수로 평가하게 되는데, 1은 함수가 아니기 때문에 예외가 발생한다. Clojure 컴파일러가 괄호()를 평가하지 않고 그냥 리스트로 인식하게 하기 위해서는 '(1 2 3) 처럼 하면 된다.

cons는 새로운 리스트를 만든다. 새로운 리스트의 첫 요소는 첫번째 파라미터이고, 나머지는 두번째 파라미터의 요소들이다.
conj(oin)는 리스트에 새 요소를 추가하는데, 리스트에 추가하는 가장 빠른 방법은 앞에 추가하는 것이다.

벡터에 추가

(cons 0 [1 2 3]) ;==> (0 1 2 3)
(conj [1 2 3] 4) ;==> [1 2 3 4]
(pop [1 2 3]) ;=> [1 2]

cons는 그 대상이 벡터여도 결과는 같다. 즉 맨 앞에 추가한 리스트를 리턴한다.
conj는 벡터일 때는 맨 뒤에 추가한다. 벡터는 인덱스 접근이 가능하기 때문이다.

집합에 추가/삭제

(conj #{1 2 3} 4) ;=> #{1 2 3 4} ; 추가
(disj #{1 2 3} 3) ;=> #{1 2}     ; 삭제

맵에 추가/삭제

(assoc {:a 1} :b 2) ;=> {:a 1 :b 2} ; 추가
(assoc {:a 1} :b 2 :c 3 :d 4) ;=> {:a 1 :b 2 :c 3} ; 추가
(dissoc {:a 1 :b 2} :b) ;=> {:a 1}  ; 삭제

assoc-in / update-in

(assoc-in {:a {:x 1}} [:a :x] 100) ;=> {:a {:x 100}}
(update-in {:a {:x 1}} [:a :x] inc) ;=> {:a {:x 2}}

자료 구조에 접근하는 함수 : first/second/last/rest

(first '(1 2 3 4 5)) ;=> 1
(second '(1 2 3 4 5)) ;=> 2
(last '(1 2 3 4 5)) ;=> 5
(rest '(1 2 3 4 5)) ;=> (2 3 4 5)

ffirst/next/butlast/fnext/nfirst

(ffirst [[1] [2]]) ;=> 1 ;(first (first x)) 와 같다
(next [1 2 3 4 5]) ;=> (2 3 4 5) ;rest와 같다. 마지막 요소가 없으면 nil을 리턴
(next []) ;=> nil
(butlast [1 2 3 4 5]) ;=> (1 2 3 4) ;마지막을 제외한 리스트
(fnext [1 2 3]) ;=> 2 ;(first (next x)) 와 같다.
(nfirst [[1 2] 3]) ;=> 2 ;(next (first x)) 와 같다.

rest와 next의 차이

(rest [1 2 3]) ;=> (2 3)
(next [1 2 3]) ;=> (2 3)
 
(rest []) ;=> ()
(next []) ;=> nil

seq / empty?

(empty? coll)
coll이 비었으면 true, 아니면 false를 반환한다.

(empty? ())   ;=> true
(empty? '(1)) ;=> false
(empty? nil)  ;=> true

(seq coll)
coll의 seq를 반환한다.

(seq '(1))  ;=> (1)
(seq [1 2]) ;=> (1 2)
(seq "abc") ;=> (\a \b \c)
(seq nil)   ;=> nil
(seq '())   ;=> nil
(seq "")    ;=> nil

coll이 비었거나 nil은 경우를 확인하기 위해서는 seq를 쓰는 것이 좋다.

contains?

(contains? coll key)
컬렉션 coll에 key가 있는지 여부 판단.

(contains? {:a 1} :a)    ;=> true
(contains? {:a nil} :a)  ;=> true
(contains? {:a 1} :b)    ;=> false
(contains? [:a :b :c] :b)  ;=> false
(contains? [:a :b :c] 2)   ;=> true
(contains? #{"a" "b" "c"} "a") ;=> true
(contains? #{"a" "b" "c"} "z") ;=> false  

range

(range)(range end)(range start end)(range start end step)
[start end) 사이의 정수로 된 지연 시퀀스를 반환한다. setp만큼 건너뛴다.

(range 10)
;=> (0 1 2 3 4 5 6 7 8 9)

0부터 시작해서 인수만큼의 정수의 리스트를 만든다.

(range -5 5)
;=> (-5 -4 -3 -2 -1 0 1 2 3 4)

(range -0 100 10)
;=> (0 10 20 30 40 50 60 70 80 90)

(range 100 0 -10)
;=> (100 90 80 70 60 50 40 30 20 10)

시작과 끝, 그리고 step을 지정할 수 있다.

(range) 
;=> (0 1 2 3 4 5 6 7 8 9 10 ... 12770 12771 12772 12773 ... n

(take 10 (range))
;=> (0 1 2 3 4 5 6 7 8 9)

(range)는 0을 포함한 자연수의 집합이라는 무한 지연 시퀀스를 만든다.
이것을 실행하면 무한 반복되어 그 스레드는 먹통이 된다.
그래서 take로 잡아주어야 한다.

if / when

if

(if true 1 2)  ;=> 1
(if false 1 2) ;=> 2
(if fasel 1)   ;=> nil

when

(when (= 1 1) true)    => true
(when (not= 1 1) true) => nil

cond / case / condp

cond
여러 조건절에 따라 분기

(let [grade 85]
  (cond
    (>= grade 90) "A"
    (>= grade 80) "B"
    (>= grade 70) "C"
    (>= grade 60) "D"
    else "F"))
 ;=> "B"

case
경우에 따라 분기

(let [x 2]
   (case x
     1 10
     2 20
     3 30
     "none"))
;=> 20

condp
pred 평가에 따른 분기

(let [x 2]
 (condp = x
      1 "one"
      2 "two"
      3 "three"
      (str "unexpected value, \"" x \")))
      

reverse

(reverse coll)
역전된 coll을 반환한다.

(reverse '(1 2 3)) ;=> (3 2 1)

take/drop

(take n coll)
coll의 처음 n개의 요소로 된 lazy-seq를 리턴한다.

(take 3 '(1 2 3 4 5 6)) ;=> (1 2 3) ; 리스트를 받아 lazy-seq를 리턴
(take 3 [1 2 3 4 5 6) ;=> (1 2 3)   ; 벡터를 받아 lazy-seq를 리턴
(take 3 [1 2]) ;=> (1 2)  

(drop n coll)
coll의 처음 n개의 요소를 제외한 나머지로 된 lazy-seq를 리턴한다.

(drop 3 '(1 2 3 4 5 6)) ;=> (4 5 6)
(drop 3 [1 2 3 4 5 6]) ;=> (4 5 6)
(drop 3 [1 2]) ;=> ()  

take-while/drop-while

(take-while pred coll)
(pred el)이 참인 동안의 요소들로 된 lazy-seq를 리턴한다. 즉 처음 (pred el)이 거짓일 때까지의 요소로 된 lazy-seq.

(take-while neg? [-2 -1 0 1 2 3]) ;=> (-2 -1)
(take-while neg? [-2 -1 0 -1 -2 3]) ;=> (-2 -1)

(drop-while pred coll)
(pred el)이 참인 동안의 요소들을 제외한 나머지 요소로 된 lazy-seq를 리턴한다. 즉 처음 (pred el)이 거짓일 때까지의 요소 제외한 lazy-seq.

(drop-while neg? [-1 -2 -6 -7 1 2 3 4 -5 -6 0 1]) ;=> (1 2 3 4 -5 -6 0 1)

map

(map f coll)
map 함수는 다음과 같이 수학에서 한 집합에서 다른 집합으로 바꾸는 작업을 한다. f는 coll의 각 요소에 적용된다.

   X                 Y   
 +----+   (f x1)   +----+
 | x1 |  ------->  | y1 |
 |    |   (f x2)   |    |
 | x2 |  ------->  | y2 |
 |    |   (f x3)   |    |
 | x3 |  ------->  | y3 |
 |    |   (f x3)   |    |
 | x4 |  ------->  | y4 |
 +----+            +----+
 

f가 inc이고 coll [1 2 3 4]인 경우 coll의 모든 요소에 inc가 적용된 lazy-seq가 리턴된다.

(map inc [1 2 3 4])  ;=> (2 3 4 5)
   X                Y   
 +---+  (inc 1)   +---+
 | 1 | -------->  | 2 |
 |   |  (inc 2)   |   |
 | 2 | -------->  | 3 |
 |   |  (inc 3)   |   |
 | 3 | -------->  | 4 |
 |   |  (inc 4)   |   |
 | 4 | -------->  | 5 |
 +---+            +---+

map 함수는 clojure programming에서 가장 많이 사용하는 함수이고 기능도 아주 다양하다. clojuredocs.org에는 map함수를 활용하는 다양한 예제 샘플들이 아주 많다.

filter/remove

(filter pred coll)
filter 함수는 coll의 요소 el에 대해 (pred el)이 참인 el들로 된 lazy-seq를 리턴한다.

   X                              Y    
 +----+    (pred x1) = true     +----+
 | x1 |------------------------>| x1 |
 |    |    (pred x2) = false    |    |
 | x2 |-----------------------x |    |
 |    |    (pred x3) = true     |    |
 | x3 |------------------------>| x2 |
 |    |    (pred x4) = false    |    |
 | x4 |-----------------------x |    |
 +----+                         +----+
 
(filter even? (range 10)) ;=> (0 2 4 6 8)

remove는 반대이다. 즉 (pred el)이 참이면 제거한다.

(remove even? (range 10)) ;=> (1 3 5 7 9)

keep

(keep f coll)
keep 함수는 coll의 요소 el에 대해 (f el)이 nil이 아닌 리턴값으로 된 lazy-seq를 리턴한다.

   X                         Y    
 +----+    (f x1) = y1     +----+
 | x1 |------------------->| y1 |
 |    |    (f x2) = nil    |    |
 | x2 |------------------x |    |
 |    |    (f x3) = y3     |    |
 | x3 |------------------->| y3 |
 |    |    (f x4) = nil    |    |
 | x4 |------------------x |    |
 +----+                    +----+
(keep #(if (odd? %) %) (range 10)) ;=> (1 3 5 7 9)

reduce

(reduce f initial coll)
reduce 함수는 coll의 요소 el에 대해 2개의 인수를 받는 함수 (f acc el)의 결과를 다음 el들의 f의 acc에 적용하여 누적한 최종값(acc)을 리턴한다.

 
 (f initial x0) ;=> acc1
     |---------------|
     v
 (f acc1 x1)    ;=> acc2
     |---------------|
     v   
 (f acc2 x2)    ;=> acc3
     |---------------|
     v
 (f accn xn)    ;=> acc (최종값)
 
 

initial이 주어지지 않으면 최초 f 호출시 첫요소 x0가 acc로, x1은 el로 해서 호출된다.

(reduce + [1 2 3 4 5]) ;=> 15

위 reduce가 수행되는 과정은 다음과 같다.

initial이 주어지지 않아서 처음 acc는 1이고, 2는 두번째 파라미터가 된다.

 (+ 1 2)   ;=> 3
    |----------|
    v
 (+ 3 3)   ;=> 6
    |----------|
    v   
 (+ 6 4)   ;=> 10
    |-----------|
    v
 (f 10 5)  ;=> 15 (최종값)

reduce는 매우 강력한 함수이다. 이것만 잘 사용하면 왠만한 제어구조는 거의 소화할 수 있다.

(reduce에서 reduce를 사용하는 매우 복잡한 일도 가능하게 되는데, 가급적 사용하지 않는 것이 코드의 가독성을 키우는데 좋다.)

into

(into to from)
from 컬렉션의 요소들을 to 컬렉션에 넣는다.

(into [1 2 3] '(4 5 6))   ;=> [1 2 3 4 5 6]
(into [] {:a 1 :b 4})     ;=> [[:a 1] [:b 2]]
(into {} [[:a 1] [:b 2]]) ;=> {:a 1 :b 2}

merge / merge-with

(merge & maps)
맵들을 합친다. 키가 겹치면 나중의 것을 취한다.

(merge {:a 1 :b 2 :c 3} {:b 9 :d 4}) ;=> {:d 4, :a 1, :b 9, :c 3}

(merge-with f & maps)
맵들을 합친다. 키가 겹치면 그 값들을 함수 f에 적용한 값을 취한다.

(merge-with + {:a 1  :b 2} {:a 10  :b 20 :c 30}) ;=> {:a 11 :b 22 :c 30}

let 문구

로컬 바인딩

(let [x 1] 
  x)
;=> 1
(let [a 1 b 2] 
  (+ a b))
;=> 3
(let [x 10
      y (range 10)] 
  (apply + y))
;=> 45

loop recur

loop은 바인딩을 제공하고 recur는 loop으로 돌아가서 재바인딩한다.

(loop [x 0]         ; 1)
  (if (< x 10)      ; 2)
    (recur (inc x)) ; 3)
    x))             ; 4)
  1. 최초 x는 0이다.
  2. loop을 빠져나오는 조건을 만든다.
  3. x를 1증가시킨 후, 그 값을 다시 loop의 x에 바인딩한다.
  4. x가 리턴된다.
(loop [sum 0 cnt 10]                  ; 1)
    (if (= cnt 0)                     ; 2)
      sum                             ; 3)
      (recur (+ cnt sum) (dec cnt)))) ; 4)
  1. 최초 sum은 0, cnt는 10이다.
  2. loop을 빠져나오는 조건을 만든다.
  3. sum이 리턴된다.
  4. sum과 cnt의 합이 sum으로 바인딩되고, cnt를 하나 줄여서 cnt에 바인딩된다.

for

(for [n (range 5)]
 n)
;=> (0 1 2 3 4)
(for [n (range 5)]
 (+ n n)
;=> (0 2 4 6 8)
(for [x ['a 'b 'c] 
      y [1 2 3]]
  [x y])
;=> ([a 1] [a 2] [a 3] [b 1] [b 2] [b 3] [c 1] [c 2] [c 3])

clojure에서 for는 제어구조가 아니라 매크로다. 그리고 리스트를 리턴한다. 이것이 다른 언어와 다른 점이다.

for는 매우 막강한 기능을 갖는다. clojuredoc.org의 for 예제 샘플은 for의 막강한 기능을 잘 보여준다.

(참고: http://programming-puzzler.blogspot.kr/2013/03/logic-programming-is-overrated.html)

다음은 그중 일부이다.

(for [x [0 1 2 3 4 5]
      :let [y (* x 3)]
      :when (even? y)]
  y)
;=> (0 6 12)
(for [x (range 1 6) 
      :let [y (* x x) 
            z (* x x x)]] 
   [x y z])          
;=> ([1 1 1] [2 4 8] [3 9 27] [4 16 64] [5 25 125])

iterate /repeat

(iterate f x)
x, (f x), (f (f x))의 지연 시퀀스를 만든다.

(take 5 (iterate inc 5)) ;=> (5 6 7 8 9)

(repeat x) (repeat n x)
주어진 값을 반복하는 지연 시퀀스를 만든다.

(take 5 (repeat "x")) ;=> ("x" "x" "x" "x" "x")
(repeat 5 "x")        ;=> ("x" "x" "x" "x" "x")

apply

(apply f args)
함수 f를 args에 적용한다.

(+ 1 2 3)          ;=> 6
(apply + '(1 2 3)) ;=> 6
(max 1 2 3)          ;=> 3
(apply max '(1 2 3)) ;=> 3

partial

(partial f)(partial f arg1)(partial f arg1 arg2) …
함수 f와 f의 일부 인수를 받고 함수를 반환하는데, 이 반환함수는 f의 나머지 인수를 받아서 f 함수를 수행할 수 있다.

(def add5 (partial + 5))
(add5 1) ;=> 6
(add5 1 2) ;=> 8
(def my-hello (partial str "Hello" ", "))
(my-hello "john")
(my-hello "john" " and " "sujan")

comp

(comp f g)
합성함수 f(g)를 만든다.

((comp str +) 8 8 8)   ;=> "24"
(filter (comp not zero?) [0 1 0 2 0 3 0 4]) ;=> (1 2 3 4)

concat / mapcat

(concat x y)(concat x y & zs)
컬렉션들을 합친다.

(concat [1 2] [3 4]) ;=> (1 2 3 4)
(concat "abc" "def") ;=> (\a \b \c \d \e \f)
(apply concat '(([1 2]) ([3 4] [5 6]) ([7 8])))
;=> ([1 2] [3 4] [5 6] [7 8])

(mapcat f & colls)
일반적인 map을 수행한 후, concat을 적용한다. f는 컬렉션을 반환해야 한다.

(mapcat reverse [[3 2 1 0] [6 5 4] [9 8 7]])
;=> (0 1 2 3 4 5 6 7 8 9)

some / every?

(some pred coll)
coll의 요소 x에 대해 (f x)가 논리적으로 참일 때의 반환값을 반환한다.

(some even? '(1 2 3 4))         ;=> true
(some true? [false true false]) ;=> true  
(some #(= 5 %) [1 2 3 4 5])     ;=> true
(some #(when (even? %) %) '(1 2 3 4)) ;=> 2
(some #{2 4 6} [0 1 2 3 4 5])         ;=> 2

(every? pred coll)
coll의 모든 요소 x에 대해 (pred x)가 참이면 true, 아니면 false를 반환한다.

(every? even? '(2 4 6)) ;=> true
(every? even? '(1 2 3)) ;=> false
(every? #{1 2} [1 2 3]) ;=> false
(every? #{1 2} [1 2])   ;=> true

-> / ->> threading form

thread-first form

(first (.split (.replace (.toUpperCase "a b c d") "A" "X") " "))
;=> "X"

코드상 함수의 위치 순서와 실행 순서가 반대라서 코드를 이해하기 어렵다.

(-> "a b c d" 
    .toUpperCase 
    (.replace "A" "X") 
    (.split " ") 
    first)
;=> "X"

코드상 함수가 그 실행 순서대로 배치되어 있어 이해하기가 쉽다.

thread-last form

(reduce + (take 10 (filter even? (map #(* % %) (range)))))

(->> (range)
     (map #(* % %))
     (filter even?)
     (take 10)
     (reduce +))
;=> 1140

mod / quot

(mod 10 6)  ;=> 4, 나머지
(quot 10 3) ;=> 3, 몫

identity

(identity x)
받은 인수를 그래로 반환한다.

(identity 4) ;=> 4

interleave / interpose

(interleave c1 c2)
각 컬렉션의 요소들을 순서대로 섞는다.

(interleave [:a :b :c] [1 2 3]) ;=> (:a 1 :b 2 :c 3)

(interpose sep coll)
coll의 요소 사이에 sep을 넣는다.

(interpose 0 [1 2 3]) ;=> (1 0 2 0 3)

partition / partition-by

(partition n coll) (partition n step coll) (partition n step pad coll)
아이템을 n개씩 묶는다. step만큼 건너뛰면서.

(partition 2 (range 10))
;=> ((0 1) (2 3) (4 5) (6 7) (8 9))
(partition 4 (range 11))
;=> ((0 1) (2 3) (4 5) (6 7) (8 9))
(partition 2 1 (range 10))
;=> ((0 1) (1 2) (2 3) (3 4) (4 5) (5 6) (6 7) (7 8) (8 9))
(partition 4 6 ["a" "b" "c" "d"] (range 20))
;=> ((0 1 2 3) (6 7 8 9) (12 13 14 15) (18 19 "a" "b"))  

(partition-by f coll)
f함수의 반환값에 따라 아이템을 묶는다.

(partition-by #(= 3 %) [1 2 3 4 5]) 
;=> ((1 2) (3) (4 5))
(partition-by odd? [1 1 1 2 2 3 3])
;=> ((1 1 1) (2 2) (3 3))
(partition-by identity "ABBA")
;=> ((\A) (\B \B) (\A))

group-by

(group-by f coll)
함수 f의 반환값이 같은 것끼리 묶는다.

(group-by odd? (range 10))
;=> {false [0 2 4 6 8], true [1 3 5 7 9]}
(group-by count ["a" "as" "asd" "aa" "asdf" "qwer"])
;=> {1 ["a"], 2 ["as" "aa"], 3 ["asd"], 4 ["asdf" "qwer"]}

map-indexed / keep-indexed

(map-indexed f coll)
map 과 같은데, 함수 f의 첫 인수로 인덱스이고, 두번째 인수가 coll의 요소(item)이다.

(map-indexed vector (range 10))
;=> ([0 0] [1 1] [2 2] [3 3] [4 4] [5 5] [6 6] [7 7] [8 8] [9 9])
(map-indexed (fn [idx itm] [idx itm]) "foobar")
;=> ([0 \f] [1 \o] [2 \o] [3 \b] [4 \a] [5 \r])

(keep-indexed f coll)
keep 과 같은데, 함수 f의 첫 인수로 인덱스이고, 두번째 인수가 coll의 요소(item)이다.

(keep-indexed #(if (odd? %1) %2) [:a :b :c :d :e])
;=> (:b :d)

lazy-seq

지연 시퀀스를 만든다.

(defn fib [a b] (lazy-seq (cons a (fib b (+ b a)))))
(take 5 (fib 1 1))  ;=> (1 1 2 3 5)

destructuring

lecture/4clojure/level-elementary-easy.txt · Last modified: 2019/02/04 14:26 (external edit)