;; 참조.
;; http://www.creativeapplications.net/
;; tutorials/introduction-to-clojure-part-1/
;; 2015-02-17 22:01:17 (화요일)
;; 클로저를 배워보자.
;;
;; 갈길이 멀다.
;;
(#_(
클로저가 처음 만들어진것은 2007 년 이라고한다.
만든 사람은 Rich Hickey.
Clojure 는 Lisp 이다.
Clojure 는 JVM 에서 돌아가는 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.
|
---|
2015년 2월 18일 수요일
Clojure 연습 3
Firefly Algorithms
firefly algorithm 001 Firefly Algorithms ¶ 반딧불 알고리즘 번역 요약 ¶ References [1] X. S. Y...
-
// 그루비를 배워보자. /* 주석이 되나. */ // 그래. 이것이 그루비 코드다. // 2015-02-12 11:34:45 (목요일) println( "Hello world" ) printl...
-
firefly algorithm 001 Firefly Algorithms ¶ 반딧불 알고리즘 번역 요약 ¶ References [1] X. S. Y...
-
;; 참조: ;; http://pupeno.com/2011/08/16/why-i-love-lisp/ ; 2015-02-13 23:44:49 (금요일) ; 2015-02-14 14:31:10 (토요일) ; 클로저를 배워보자...