2015년 2월 18일 수요일

Clojure 연습 3





;; 참조.
;; http://www.creativeapplications.net/
;; tutorials/introduction-to-clojure-part-1/



;; 2015-02-17 22:01:17 (화요일)

;; 클로저를 배워보자.
;;
;; 갈길이 멀다.
;;


(#_(

클로저가 처음 만들어진것은 2007 년 이라고한다.
만든 사람은 Rich Hickey.

ClojureLisp 이다.
ClojureJVM 에서 돌아가는 Lisp 일 뿐이다.

Lisp 이 처음 만들어진것은 1958 년 이라고 한다.
만든 사람은 John McCarthy.

C++11 에 와서야 람다가 추가되었다.
Java8 에 와서야 람다가 추가되었다.

"1958년"에 만들어진 Lisp 을 다른언어들이 따라가고있는 모양새다.



클로저 코드에는 괄호"()"가 많다.
그래서 낯설다.
그래서 거부감이 들수도 있다.



그러나 클로저에는 없는것이 더 많다.
- 세미콜론";" 이 없다.
- 연산자 오버로딩이 없다.
- 범위"scope"를 지정할때 사용하는 중괄호{} 가 없다.
- 콤마"," 조차 안써도 되며, 써도 공백으로 처리된다.



그 결과. 코드는 간결하다.
그 결과. 코드는 읽기쉽다.





다른 언어들의 코드 생김새를 한번보자.

"
//C++
void greetings(const char *fname, const char *lname) {
    std::cout << "hello " << fname << " " << lname << std::endl;
}

// Java
public void greetings(String fname, String lname) {
   System.out.println("hello " + name);
}
"


클로저 코드 생김새를 보자.

; Clojure
(defn greetings [fname lname]
    (println "hello " fname lname))


이 함수를 호출하는 코드의 생김새를 보자.

"
// C++, Java
greetings("Doctor", "Evil");
"

; Clojure
(greetings "Doctor" "Evil")


클로저 문법은 S-expression ( symbolic expression ) 이다.


))


;; 여기서도, 다시 인사해보자.
(println "Hello World")

;; 이것이 문법의 전부다.
(+ 1 2)
(+ (+ 1 2) (+ 3 4))
(+ 1 2 3 4)


;; 이것이 문법의 전부다.
;; (함수 인자1 인자2 ... 인자N)

;; 괄호는 S-expression 의 범위를 말해준다.
;; 괄호는 리스트 의 범위를 말해준다.


;; 피타고라스 정리를 다음과 같이 코딩할수있다.
;; (c^2 = a^2 + b^2)
(+ (* 4 4) (* 8 8))



;; 심볼(symbols).
;; 심볼은 재사용할수있다는 점과,
;; 데이터에대한 이름을 제공한다는 점에서,
;; 다른언어의 변수와 비슷하다.
;; 그러나, 변수는 클로저에는 없다.

;; 다른언어의 변수와 클로저의 심볼이 다른점은 이것이다.
;; 심볼은 한번 데이터 값에 이어지면 (,연결되면, bind) 
;; 이것을 바꿀수 없다는 점이다.
;; 이 개념을 불변성, 불연성, immutablity 라고 한다.


;; 심볼의 값을 바꿀수없다는 사실은,
;; C++ 프로그래머인 나로서는 좀 낯설다.

;; 불변 데이터는 한번 정의되면 읽기전용이다.
;; 일견, 불편해보이는 이 읽기전용 특성은 
;; 참으로 안전한 멀티쓰레드 애플리케이션을 만드는데 필요한 필수 기능이다.

;; 의문이 생긴다.
;; 심볼이 불변성인데, 내부상태가 변하는 객체를 표현할수 있을까.
;; 현재로서는 의문은 접어두고, 갈길을 가자.



;; let 폼은, 심볼에 값을 잇는 (연결하는, 바인딩하는) 용도다.
;; 여기서 정의된 심볼은 범위가 로컬로 제한된다. (local scope)
;; 다음 코드에서,
;; 심볼 a, b 가 정의된 scope 은 let 괄호 영역 범위이다.

(let [a 4
      b 8]
  (+ (* a a) (* b b)))


;; 이것이, 
;; 로컬범위에서 심볼을 정의하고 사용하는방법이다.


;; namespace 는 module 을 관리하고, 이름충돌을 방지하는 수단이다.
;; C++ 의 namespace, Java 의 package, Python 의 module 과 그 개념이 같다.


;; def 는 Var 를 정의하는데 사용되는 form 이다.

(def a 4)
(def b 8)
(+ (* a a) (* b b))

;; (var a)
;; #'a
;; 'a
;; a


;; 함수를 정의할때, defn 를 쓴다.

(defn hypot [a b]
  (let [a (* a a)
        b (* b b)
        c (+ a b)]
    (Math/sqrt c)))


(defn hypot [a b] (Math/sqrt (+ (* a a) (* b b))))

;; 함수의 리턴값은 함수의 마지막 표현식이다.
;; 함수의 마지막 표현식은, 그함수의 리턴값이된다.

(hypot 9 12)
(println (hypot 9 12))


;; anonymous function
;; 무명함수
;; 람다, 클로저. lambda, closure.
;; fn 을 쓴다.

(fn [name] ( println "Hello " name))


;; Guards
;; 가드는 맵으로 표현한다.
;; 함수의 입력(인자)을 점검할때는 :pre 를 쓰고,
;; 함수의 출력(리턴값)을 점검할때는 :post 를 쓴다.
;; 가드는 입출력의 제약조건을 기술할때 쓴다.
(defn make-greetings [hello]
  {:pre [(string? hello)(< (count hello) 10)]
   :post [(string? %)]}
  (fn [name]
    (str hello " " name "!")))


;; Metadata
;; documentation, type hint.
;;
;; 함수에 문서를 달고, 타입힌트를 쓸수있다.
;; 여기에, 가드까지 달아주면,
;; 함수가 점점더 완전해진다.
;;
(defn make-greetings 
  "Takes a single argument and returns a fn."
  [^String greeting]
  {:pre [(string? greeting)(< (count greeting) 10)]
   :post [(string? %)]}
  (fn [^String name]
    (str greeting " " name "!")))


(println
 (meta (var make-greetings))
)

;; if
;; when
;; do


;; Lisp : List Processing


;; eval : data -> code
;; '    : code -> data


;; map 은 key-value pairs 를 표현한다.
;; java 의 HashMap 과 유사하다.
;; key-value 는 어떤 타입이나 가능하다.
;; 그러나 key 로 자주쓰이는 타입은 keyword 이다.

;; keyword 는 평가된 결과가 자기자신인 symbol 이다.
;; 즉, 다른값이 이어지지 (연결되지, 바인드되지) 않은 symbol 이다.
;; 하나의 namespace 에서 keyword 는 중복될수 없다.
;; keyword 는 : 이나 함수 keyword 를 써서 표현한다.
;; keyword 에는 어떤문자라도 사용할수있으나, 공백은 안된다.

:my-key
(keyword (str "my-key"))
(println (keyword (str "my-key")))

;; map 은 {} 나 hash-map 함수로 표현한다.

;; map 의 key 는 중복을 허락치 않는다.
;; (def m { :a 21 :a 23 :b [1 2 3] :c {"name" "toxi" "age" 38}})
(def m { :a 23 :b [1 2 3] :c {"name" "toxi" "age" 38}})
(println m)
(println (type m))
(println (meta m))

(def m2 (hash-map  :a 23 :b [1 2 3] :c (hash-map "name" "toxi" "age" 38)))
(println m2)
(println (type m2))
(println (meta m2))


(println (m :a))
(println (:b m))
(println (get m :c))
(println (:missing-key m))
(println (get m :missing-key "nada"))

(println ((:b m) 2))
(println ((:c m) "name"))


(println (get-in m [:b 2]))
(println (get-in m [:c "name"]))
(println (get-in m [:missing-key :missing-location] "nada"))

(println (select-keys m [:a :b :missing-key]))



;; set 은 중복되지않은 유일한값들을 표현하는데쓴다.
;; set 은 순서가 없다.

#{1 2 3 4}

;; set 의 key 는 중복을 허락치 않는다.
; #{1 1 2 3 4} ; 중복된 요소는 허락치 않는다.


;; 이런 표현으로 중복을 입력해도, 결과는 유일한 요소다.
(hash-set 1 1 2 3 4)
(println (hash-set 1 1 2 3 4))


(println (type {:a 1 :b 2 :c [1 2 3]}))
(println (type #{:a :b}))
(println (type (hash-map :a 1 :b 2 :c [1 2 3])))
(println (type (hash-set :a :b)))


(def lucky-numbers [1 2 3 4 4 2 1 3])
(println lucky-numbers)
(println (set lucky-numbers))
(println (into #{} lucky-numbers))
(println lucky-numbers)


;; set 은 map 의 일종이라고 생각할수있다.
;; key 는 있으나, value 가 없고
;; key 는 자기자신으로 평가되는 일종의 map 으로 생각할수있다.
;;
;; set 과 map 은 본질적으로 같은것이다.
;; #{} 과 {}  은 본질적으로 같은것이다.
;; map 을 key 로 참조하듯,
;; set 도 key 로 참조한다.
;; 순서가 없으므로, index 가 아니라 key 로 참조한다.
;;


(println (get #{1 2 3 4} 3)) ; index 가 아니라, 3 이라는 key 를 리턴한다.
(println (#{1 2 3 4} 5))     ; 찾는 key 가 없으면, nil 을 리턴한다.
(println (get #{1 2 3 4} 5 :nope)) ; 찾는 key 가 없으면, nil 대신 리턴할 기본값 지정.



(def my-set #{:key1 :key2 :key3 "key4" #{:a :b} })
(println (get my-set :key1))
(println (get my-set :key2))
(println (get my-set :key3))
(println (get my-set "key4"))
(println (get my-set #{:a :b}))
(println (get my-set #{:b :a})) ; 순서는 개의치 않는다.





(def g
  #{#{:toxi :ricardo}
    #{:mia :toxi}
    #{:filip :toxi}
    #{:filip :ricardo}})

(defn knows?
  "Takes a graph and two node names, returns true if the graph contains
  a relationship between the nodes (ignoring direction)"
  [graph a b]
  (not (nil? (graph #{a b}))))

(println (knows? g :toxi :filip))
(println (g #{:toxi :filip}))
(println (g #{:filip :toxi})) ; 순서는 개의치 않는다.


;; Adding elements
;; conjoin
;; conj

(conj [1 2 3] 4 5 6)
(println (conj [1 2 3] 4 5 6))

(println (conj '(1 2 3) 4 5 6))

(println (conj {:a 1 :b 2} [:c 3] [:d 4]))

(println (conj #{1 2 3} 1 2 4 5))

;; associative collections
;; map 과 vector 는 associative collections 이다.
;; map 은    key   - value 를 잇는다.(연결한다.)
;; vector 는 index - value 를 잇는다.(연결한다.)
;;

(println (assoc {:a 23} :b 42 :a 88))
(println (assoc [1 2 3] 0 10, 3 40))

;; ()
;; []
;; {}
;; #{}
;; list, vector, map, set

;;Nested data manipulations
;; assoc-in, update-in


;; Removing elements
;; dissociate, disjoin
;; dissoc, disj

(println (dissoc {:a 1 :b 42} :b))

(println (disj #{10 20 30} 20))

;;(println (dissoc [10 20 30] 1))

;; list 와 vector 는 random item 삭제기능을 제공하지않는다.
;; 오직 head tail 에서만 삭제가 가능하다.
;; list 는 head 에서만 삭제한다.
;; vector 는 tail 에서만 삭제한다.

(println (pop '(1 2 3)))

(println (pop [1 2 3]))

;; 데이터를 넣을때(conj)나 삭제할때(pop).
;; list 는 모두 head 에서 작업한다.
;; vector 는 모두 tail 에서 작업한다.
;; map, set 은 순서에 상관하지 않으므로,
;; 위치는 개의치 않는다.


;; Sequence
;; first, next, rest
;; cons, seq

(println (cons 1 nil))
(println (type (cons 1 nil)))
(println (cons 2 (cons 1 nil)))
(println (cons \c "ab"))


;; loop
;; tail recursion
;; loop, recur
;; doseq
;; dotimes

(println
(loop [result nil, coll [1 2 3 4]]
  (if (seq coll)
    (let [x (first coll)]
      (recur (cons x result) (rest coll)))
    result))
)

(println
(doseq [p [{:name "Ben" :age 42} {:name "Alex"} {:name "Boris" :age 26}]]
  (let [name   (:name p)
        suffix (if (#{\s \x} (last name)) "'" "'s")
        age    (or (:age p) "rather not say")]
    (println (str name suffix " age: " age))))
    
)

(dotimes [i 3] (println i))

;; pure function
;; memoize
(#_(
(defn slow-fn [x] (Thread/sleep (* x 1000)) x)

(def not-so-slow-fn (memoize slow-fn))

(println (not-so-slow-fn 3))

(println (not-so-slow-fn 3))
))


;; map-reduce
;; transformation function


;; map
;; map 의 transformation function 인자는 최소 1개.
(println (map inc [1 2 3 4 5]))

(println (map (fn [x] (* x x)) [1 2 3 4 5]))

(println (map #(* % %) [1 2 3 4 5]))

;; reader macro #(...) : anon fn
;; % : first argument
;; %2, %3, ...

(println (#(* % %2) 10 2))


(println
(map
 (fn [p c] {:pos p :color c})         ; transformation fn
 [[100 0] [0 -100] [0 100] [200 100]] ; points
 [:red :green :blue])                 ; colors
)




;; reduce
;; reduce 의 transformation function 의 인자는 최소 2개.
;; 하나는 결과값, 하나는 요소.
(println
(reduce + 0 [1 2 3 4 5 6 7 8 9 10])
)

(println
(reduce + (range 10))
)

(println
(reductions + (range 1 10))
)

(println (range 10))
(println (range 1 10))
(println (range 1 10 2))
(println (range 5 10))

;; filter , predicate
;; 관례적으로 filter 는 ? 가 붙는다.
;; true/false 를 리턴하는 predicate 이기 때문이다.
(println (filter even? (range 10)))

;; filter 의 transformation function 이,
;; true/false 를 리턴하는것이 강제조건은 아니다.
;; 따라서 set 에 존재하는가를 서술하는 용도로,
;; set 을 사용하는것이 가능하다.
(println (filter #{1 2 4 8 16 32} (range 10)))

;; 데이터를 함수처럼 쓰는 모습이다.
;; using data as code.
;; #{1 2 4 8 16 32} 가 있는 위치는 transformation function 이 
;; 와야 하는 자리다. set 을 함수처럼 쓰고있다.
;; 이것이 "data as code" 이다.


;; take / drop
(println
(take 3 '(a b c d e f))
)

(println
(drop 3 '(a b c d e f))
)

;; take-last, drop-last, butlast, take-nth, 
;; take-while, drop-while

(println (take-last 2 '(a b c d e f)))
(println (drop-last 2 '(a b c d e f)))
(println (butlast     '(a b c d e f)))
(println (take-nth  2 '(a b c d e f)))
(println (take-while #(< % 5) (range 10)))
(println (drop-while #(< % 5) (range 10)))

;; concat & mapcat
;; concatenate
 
(println 
(concat [1 2 3] '(a b c) {:a "aa" :b "bb"})
)

(defn rotate-left
  [n coll]
  (concat (drop n coll)(take n coll)))

(println
(rotate-left 3 '(a b c d e f g h i))
)


(def g2
  #{#{:ricardo :toxi}
    #{:filip :edu}
    #{:filip :toxi}
    #{:filip :ricardo}
    #{:filip :marija}
    #{:toxi :marija}
    #{:marija :edu}
    #{:edu :toxi}})

(println g2)
(println (seq g2))
(println (map seq g2))
(println (mapcat seq g2))
(println (identity g2))
(println (mapcat identity g2))
(println (set (mapcat identity g2)))
(println (frequencies (mapcat identity g2)))


;; Infinite sequences
;; range, cycle, repeat, repeatedly, iterate


;; sequence combinator
;; interleave, interpose, zipmap

(println
(interleave [:colojure :lisp :scheme] [2007 1958 1970])
)

(println
(interpose "," #{"cyan" "black" "yello" "magenta"})
)

(println (take 10 (iterate #(* 10 %) 1)))
(println
(take 5 (map (fn [x] [x (* x 5)]) (iterate #(* 10 %) 1)))
)

(println
(take 5 (mapcat (fn [x] [x (* x 5)]) (iterate #(* 10 %) 1)))
)

(println
(zipmap
 [:I :V :X :L :C :D :M]
 (mapcat (fn [x] [x (* x 5)]) (iterate #(* 10 %) 1)))
)


;; for

(println
(for [i (range 4)] {:i i :even (even? i)})
)

(println
(into {} (for [i (range 4)] [i (even? i)]))
)

;; for 는 list 를 이해한다.
;; list 값을 로컬심볼 i 에 잇는다.
(println
(for [i (list 1 2 3 4)] [i])
)

(println
(for [i (list :a :b :c :d)] [i])
)

;; for 는 vector 를 이해한다.
;; vector 값을 로컬심볼 i 에 잇는다.
(println
(for [i [11 12 13 14]] [i])
)

;; for 는 set 을 이해한다.
;; vector 값을 로컬심볼 i 에 잇는다.
(println
(for [i #{21 22 23 24}] [i])
)

;; for 는 map 을 이해한다.
;; map 값을 로컬심볼 i 에 잇는다.
(println
(for [i {:a 21 :b 22 :c 23 :d 24}] [i])
)

;; for 는 모든 collection 을 이해한다.
;; 이런 collection 에 대하여, for,

;; 리스트 요소를 인덱스로 참조하지 않는다.


;; nested seqs.
(println
(for [y (range 2)
      x (range 4)]
  [x y])
)

;; let 과 when 을 써서,
;; 더 지능적인 for 문을 만들수있다.
(println
(for [y (range 4)
      x (range 4)
      :let [border? (or (= 0 x) (= 3 x) (= 0 y) (= 3 y))]
      :when border?]
  [x y])
)


;; every?, some

(println
(every? :name [{:name "nardove"} {:name "toxi"} {:name 88}])
)

(println
(every? #(zero? (rem % 3)) [666 42 99 12])
)

(println
(some #{3 6 9 12} [1 2 3 4 5 6])
)

(println
(some #(> (count %) 4) ["mia" "nardove" "toxi"])
)

(println
(some #(if (> (count %) 3) %) ["mia" "nardove" "toxi"])
)


;; destructuring
;; 심볼을 값에 이어주는 방법.

(def nested-data [10 20 [30 40] {:x 1 :y 2}])
(prn nested-data)

(let [a (nested-data 0)
      b (nested-data 1)
      c (nested-data 2)]
  (prn :a a :b b :c c))

(let [[a b c] nested-data] (prn :a a :b b :c c))

(let [[_ b _ d] nested-data] (prn :b b :d d))

(let [[_ _ [c d :as third]] nested-data]
  (prn third "contains:" c d))

(let [[a b & more] nested-data]
  (println (count more) "more elements:" more))

(let [{a :x b :y} (nested-data 3)]
  (prn :a a :b b))

(let [{:keys [x y] :as v} (nested-data 3)]
  (prn :x x :y y :v v))

;; destructuring and fucntion arity.


Firefly Algorithms

firefly algorithm 001 Firefly Algorithms ¶ 반딧불 알고리즘 번역 요약 ¶ References [1] X. S. Y...