User Tools

Site Tools


Sidebar

  • Learn about Wiki
  • Lectures
  • Study
  • Tips
    • 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)