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.


2015년 2월 15일 일요일

Clojure 연습 2






;; 2015-02-16 14:20:05 (월요일)
;; 클로저를 배워보자.

;; 참조:
;; http://www.moxleystratton.com/blog/2008/05/01
;; /clojure-tutorial-for-the-non-lisp-programmer/


(comment

클로저 표현식은 두가지가 있다.
atoms, lists.


atoms 는 다른언어의 primitive 타입과 같다.

클로저의 atoms 에는 다음과 같은것들이 포함된다.

Number
Booleans
Nil
String
Symbols
Keywords

)

;; Number
5

;; Booleans
true

;; Nil
nil

;; String
"Hello, world!"

;; Symbols
;;
;; 심볼은 값의 이름이다.
;; 다른 언어에서 상수라고 부르는것과 유사하다.
;; 클로저는 "변수"라는것이 없다.
;; 클로저에서는 심볼과 심볼의 값을 명확히 구분한다.
;;

;; 다음과 같은것이 심볼의 예가 되겠다.
*file*
*compile-path*
*clojure-version*
*command-line-args*
+

;; 위에서 나열된 심볼은 클로저의 내장심볼이다. (built-in symbols)
;; 관례적으로, 데이터에 바인드된 심볼은 
;; 앞뒤에 * 를 붙여준다.
;; 마지막에 있는 + 는 내장함수이다.

;;
;; 클로저에서는
;; 심볼이름에 다음과 같은 문자를 포함할수있다.
;; *,+,!,-,_,?
;; 위 문자는 C++ 변수에 쓰면 대부분 에러가 나는것들이다.
;; C++ 보다 클로저에서 더 자유롭게 이름을 지을수있다.
;; 클로저 심볼역시, 숫자로 시작해서는 안된다.
;;


;; Keywords
;; 키워드는 값에 바인드되지 않는다는 점을 빼면, 심볼과 같다.
;; 키워드는 항상 자기자신에게 바인드된다.
;; 키워드는 : 으로 시작한다.

:a
:_123
:KEY


;;Lists
;; 리스트는 다음과 같이 생겼다.
()
(list 1 2 3)
(+ 3 3 3)

;; 괄호()로 감싸진것은 리스트다.
;; 리스트는 두가지 용도로 사용된다.
;; 데이터구조, 함수호출
;;
;; 즉, 클로저에서는
;; 리스트 자료구조도 리스트 형태로 표현하고,
;; 함수호출도 리스트 형태로 표현한다.
;;
;; 두가지 기능을 같은 문법 형태로 표현한다.
;;

;; 리스트문법으로 데이터를 표현하면 다음과 같다.
(list 1 2 3)

;; 위 문장과 같은 뜻으로, 다음과 같이 쓸수있다.
'(1 2 3)

;; 둘다 리스트 데이터를 표현한다.


;; 리스트문법으로 함수호출을 표현하면 다음과 같다.
(+ 1 2)

;; 맨앞에있는것이 함수, 그 뒤에오는것이 인자(arguments)다.


;; 리스트 안에 들어가는 요소의 타입이 같을 필요는없다.
;; 여러가지 다른 다입을 리스트에 넣을수있다.
(list :keyword1 (list 1 2 3) [4 5 6])

;;
;; C++ 의 배열이나 리스트는 한가지 타입만 가능하다.
;; 클로져는 리스트에 여러가지 타입을 넣을수있다.
;; 언어의 표현력. 언어의 자유도가 높다는것을 알수있다.
;;

;; 리스트데이터를 표현할때 quote 를 쓰는 다음과 같은 표현은
'(1 2 3)

;; 식을 평가하지 않는 효과를 가진다.
;; 이 기능은 매크로를 만들때 사용된다.
;; 지금은 그냥 리스트를 표현하는 문법만 알아두자.


;; Vectors
;; 벡터는 이렇게 생겼다.
[1 2 3]

;; 벡터역시 여러가지 타입을 요소로 넣을수있다.
[ 1 2 3 :keyword1 (list 1 2 3) [4 5 6]]

;; 리스트와는 달리
;; 첫번째 요소에 list 를 쓴다거나
;; 맨앞에 ' 를 쓴다거나 하는 제약조건이 없다.
;; 이건 그냥 데이터를 표현한다.
;; 반면,
;; 리스트 문법은, 데이터와 함수호출 두가지를 표현하기때문에,
;; 그 둘을 구분하기위해 list, ' 등이 필요한것이다.
;; 그런것이 없으면, 함수호출로 해석하고,
;; 그런것이 있으면, 데이터 리스트로 해석하는것이다.
;;

;; 배열처럼 index 로 특정요소를 가져온다.
([1 2 3] 1)
(first [1 2 3])
(last [1 2 3])
(rest [1 2 3])


;; Maps and Sets
;; 맵은 키-값 쌍을 정의한다.
{"a" 1, "b" 2, "c" 3}

;; 중간에 콤마(,) 를 써도되고 안써도 된다.
;; 대부분 클로저는 콤마(,)를 공백으로 해석한다.
{"a" 1 "b" 2 "c" 3}
{"a", 1, "b", 2, "c", 3}


;; 키를 써서, 값을 가져오는 기능.
(get {"a" 1 "b" 2 "c" 3} "a")
(get {"a" 1 "b" 2 "c" 3} "b")
(get {"a" 1 "b" 2 "c" 3} "b")

;; 위코드의 단축형으로, 다음과 같이 쓸수있다. 같은 뜻이다.
( {"a" 1 "b" 2 "c" 3} "a")
( {"a" 1 "b" 2 "c" 3} "b")
( {"a" 1 "b" 2 "c" 3} "b")

(comment
;; 이건 안되는구나.
( "a" {"a" 1 "b" 2 "c" 3} )
( "b" {"a" 1 "b" 2 "c" 3} )
( "c" {"a" 1 "b" 2 "c" 3} )
)

;; 키를 써서, 값을 가져오는 기능.
(get {:a 1 :b 2 :c 3} :a)
(get {:a 1 :b 2 :c 3} :b)
(get {:a 1 :b 2 :c 3} :c)

;; 단축형(1). 같은뜻.
( {:a 1 :b 2 :c 3} :a)
( {:a 1 :b 2 :c 3} :b)
( {:a 1 :b 2 :c 3} :c)

;; 단축형(2). 같은뜻.
( :a {:a 1 :b 2 :c 3} )
( :b {:a 1 :b 2 :c 3} )
( :c {:a 1 :b 2 :c 3} )


;; 위, 단축형(1) 과 단축형(2) 는 자주쓰이는 문법이다.
;; 기억해두자.
;;
;; 맵도 넣을수있는 요소의 타입에는 제약이 없다.




;; 정리하자.

;; 1. list
()

;; 2. vector
[]

;; 3. map
{}

;; 4. set
#{}


(println (type ()))
(println (type []))
(println (type {}))
(println (type #{}))






;; def
;; 심볼을 값에 연결한다.
(def x 5)
(println x)

(def val1 (+ 5 x))
(println val1)

(def my-list '(1 2 3))
(println my-list)
(println (last my-list))

;; 새로운 심볼이 생성되고,
;; 여기에 주어진 값을 연결하는 작업이 수행된다.
;; C++ 에서 이렇게 하던것이었는데.
;;
;; x = 5;
;; val1 = 5 + x
;;


;; defn
;; 함수를 만든다.

(defn election-year? [year]
  (zero? (rem year 4)))

(println (election-year? 2007))
(println (election-year? 2008))


;; 모든 심볼에 ? 문자를 쓸수있다.
;; 함수이름에 ? 문자를 쓸수있다.
;; true/false 를 리턴하는함수를 predicate 라 한다.
;; 관례적으로 predicate 함수명에 ? 문자를 붙여준다.
;;



;; fn
;; 무명의 함수.

(fn [x] (+ x 1))
(println ((fn [x] (+ x 1)) 9))

;; 이름없는 함수. 
;; 무명함수.
;; 그 유명한 람다(lambda).
;; 또는, 그 유명한 클로저(closure).
;;
;; 이름이 없으므로 호출할수없다.
;; 호출할수있는 유일한 기회는, 그 함수가 정의되는 곳 뿐.
;; 

;; 물론 이름을 붙여줄수도 있다.
(def plus-one (fn [x] (+ x 1)))
(println (plus-one 9))

;; 위에서 사용한 def, fn 조합의 단축형이 defn 이다.
(defn plus-one [x] (+ x 1))


;; 무명함수가 함수 외부값을 쓸수있는가? 
;; 쓸수있다면, 
;; 이것은 람다(lambda)가 아니라 클로저(closure)다.
(def xxx 7)
(defn xxxfn [x] (+ x xxx))
(println (xxxfn 1))


;; 의문이 생긴다.
;; 이름이 없어서 나중에 호출할수없는 함수라니.
;; 호출할수없는 함수가 필요한가?
;; 이것을 어디에 쓴단말인가?



;; doc
;; repl 에서만 된다.
;;(doc first)



;; str
(str "Hello," "world!")

;; if
(if true "yes")

;; do
(do (println "Hello.") (+ 2 2))

;; when
(when true "yes")

;; let
(let [x 2] (+ x 8))

;; Looping and Iterating
(loop [i 0]
  (when (< i 5)
    (println i)
    (recur (inc i))))


(dorun (for [i (range 0 5)]
         (println i)))


(doseq [i (range 0 5)]
  (println i))


;; Sequences
(seq [1 2 3])
(println (seq [1 2 3]))

(def s (seq [1 2 3]))
(println (type s))
(println (.getClass s))

(first s)
(second s)
(last s)
(rest s)


(println (first s))
(println (second s))
(println (last s))
(println (rest s))

(cons 1 [ 2 3])
(println (cons 1 [ 2 3]))


;; Java Integration
(new java.util.Date)

(new StringBuffer "this is..")

(StringBuffer. "this is..")

(.toString (new java.util.Date))

(.toString (java.util.Date.))

(Integer/MAX_VALUE)
(Character/TYPE)
(Boolean/valueOf "true")

(println (Integer/MAX_VALUE))
(println (Character/TYPE))
(println (Boolean/valueOf "true"))




2015년 2월 13일 금요일

Clojure 연습 1




;; 참조:
;; http://pupeno.com/2011/08/16/why-i-love-lisp/


; 2015-02-13 23:44:49 (금요일)
; 2015-02-14 14:31:10 (토요일)

; 클로저를 배워보자.

; 이것이 주석이다.
;; 이것이 주석이다.


( comment 

  이것이 여러줄 주석이다.

  Hello World.

  한글좀 써보자.

)



;; 헬로월드다.
( println "Hello World." )


;; 괄호위치가 심상치 않다.
;; 대부분 언어에서 괄호위치는 함수명 뒤에 오지않나?
;; println( "Hello World.")


;; 한글좀 써보자.
(println "안녕하신가.")


;; 1 더하기 2.
(+ 1 2)

;; 아. 연산자의 위치가 앞에 나와있는데.
;; 대부분 언어에서 연산자 위치는 중간에 있지않나?
;; (1 + 2)



( comment

  용어 정의 한번 하고 가자.
  클로저에서 "쿠오트", "백틱"이 자주쓰인다.
  자주쓰이면서, 유의미하게 쓰인다.

;;      ' : quote
;;      ` : back-tick


)


;; symbol
'aaaa
'한글

;; vector
[1 2 3 4]

;; list
'( a b c d )

;; nested list
'( a ( b c ) d )


;;
;; 심볼을 쓸때 ' 를 해주지 않으면 에러가난다.
;; 심볼의 값을 평가하기때문이다.
;; ' 를 써주면, 값을 평가하지 말라는 뜻이다.
;;


;; 변수정의
( def hello-world "Hello World!!!" )

;; 변수사용
( println hello-world )


;; 변수정의할때, def 를 쓴다는점.
;; 변수정의할때, - 도 사용가능하다는점.


;; 함수
(fn [n] (* n 2))

;; 이 함수는 이름이 없다.

;; 이름없는 함수에 이름을 붙여주자.

(def twice (fn [n] (* n 2)))

;; 함수를 불러보자.
(println (twice 10))


;; 한글변수를 시험해보자.
( def 한글변수 "안녕하쇼~")
( println 한글변수 )

;; 한글함수를 시험해보자.
( def 따블 (fn [숫자] (* 숫자 2)))
( println (따블 20))
 
;; 이름없는 함수를 정의하고, 이름을 붙이는데, 
;; 두개의 키워드(def, fn) 가 사용된다.
;; 이것을 하나의 키워드로 편하게하자.
(defn twice [n] (* 2 n))
(println (twice 32))


;; if 문을 써보자.
(defn twice [n] (if (> n 50) 100 (* n 2)))
(println (twice 45))


;;
;; 괄호가 전체 문장을 감싸고 있어서, 
;; 문장이 마치, list 또는 nested list 처럼 보인다는 점.
;; 연산자가 맨앞에 나와있다는점.
;; if 문에서도 비교연사자가 맨앞에 나와있다!
;;


;; 함수를 줄바꿈으로 이쁘게 써보자.

(defn twice [n]
  (if (> n 50)
    100
    (* n 2)))

(println (twice 33))



;; 새로운 문법을 만든다고 해보자.
;; 연산자가 뒤에 나오는 문법을 만든다고해보자.
;; 현재 클로저에서 다음과 같이 쓰면,
;; 에러가 난다. 

;;(4 5 +)
;; java.lang.ClassCastException: java.lang.Long cannot be cast to clojure.lang.IFn



;; 위 에러를 해결하기위해
;; 다음과 같은 함수 하나를 고안한다.

(defn psil [exp]
  (reverse exp))

;;(psil (4 5 +))
;; 이것도 에러가난다. 원인은,
;; psil 이 실행되기전에 (4 5 +) 를 평가하기 때문이다.
;; (4 5 +) 를 list 로 만들어주면, 평가하지 않는다.



(psil '(4 5 +))

(println (psil '(4 5 +)))
;; ( + 5 4)

;; 이것은 실행은 되지만,
;; 내가 원하던값 9 는 아니다.
;; 값이 평가되지 않았다.
;; 평가를 하도록해주자.

(eval (psil '(4 5 +)))

(println (eval (psil '(4 5 +))))

;; 이것은 잘 실행되고, 평가도 된다. 
;; 원하는 값 9 가 나왔다.


(eval (psil '(4 5 +)))

;; 코드를 다시보자.
;; 모양이 nested list 다.
;; "중첩된 리스트" 모양이다.
;; '(4 5 +) 
;; 이것은 데이터이다. 리스트다.
;; 위코드는 리스트를 실행시키는 코드다.
;; 위코드는 데이터를 실행시키는 코드다.
;; 데이터가 코드가 되는 순간이다.
;;
;; 그토록 바라던것.
;; 데이터가 코드가되고, 코드가 데이터가 되는것.
;;
;; 마치, 
;; 에너지와 질량이 상호 변환가능(E=mc^2)한 것처럼.
;; 코드와 데이터가 상호 변환가능하다는 사실(Code <=> Data) 이 
;; 관찰되는 순간이다.
;; 코드는 데이터가 되고, 데이터는 코드가 된다.
;;
;  나는 이것을
;; "데이터로, 실행되는 프로그램을 만들수있다"는 의미로 해석한다.
;; 

;; 평가되지않는 인자를 받아서,
;; 평가된 결과를 내는 기능을 매크로(Macro)라고 한다.

(defmacro psil [exp]
  (reverse exp))

(psil (4 5 +))

(println (psil (4 5 +)))


;; (println (psil (4 5 +)))
;; (println (eval (psil '(4 5 +))))
;;
;; 위 코드를 비교해보자.
;; 매크로를 쓰면, eval 을 쓰지 않아도 된다.
;; 매크로를 쓰면, quote (') 를 쓰지 않아도 된다.
;; 어마어마한, 도약이다.
;;
;; Clojure 문법에 맞지않는 문장 "(4 5 +)" 이 어째서 에러가 나지않는가.
;; 그것은 psil 이 실행되기 전까지는 데이터가 평가되지 않기때문이다.
;; 데이터를 요리해서 다른 프로그램을 만들어낼수있는것이다.
;; 이 기능은 나만의 프로그래밍 언어를 만드는것을 가능하게 해준다.
;; Lisp 언어에서 내가 필요한 어떤 문법이라도 쓸수있게 해준다.
;; 이것이, 개발자가 원하는 궁극의 그무엇이 아닌가.
;; 새로운 문법을 만들수 있다면,
;; 이것이 바로 요즘 유행하는 DSL 이 아닌가.


;; 매크로를 평가하지는 않고, 데이터가 처리된 결과만을
;; 돌려주는 연산자가 있다. macroexpand 이다.
;; 이것으로 평가하려는 코드를 볼수있다.
(macroexpand '(psil (4 3 +)))
(println (macroexpand '(psil (4 3 +))))


;; 매크로는 컴파일타임에 실행되는 함수로 생각하면된다.
;; Lisp 에서는 컴파일타임과 런타임이 섞여있다.
;; 이 둘사이를 계속 왔다갔다 할수있다.


;; do 연산자를 한번보자.

(do (println "Hello") (println "World"))

;; do 는 평가식을 하나씩, 순차적으로 실행한다.
;; do 에 넘겨진 여러개 평가식은 
;; 전체가 하나의 평가식으로 그룹지어진다.

;; 매크로를 만들때, do 를 써보자.

(defmacro psil [exp]
  (println "compile time")
  `(do (println "run time")
       ~(reverse exp)))



;; back-tick(`) 은 quote(') 와 기능이 같다.
;; 값을 평가하지 말라는 뜻이다.
;; back-tick 에서 tilde (~) 가 나오면
;; tilde 가 사용된 곳에서는 다시 평가하라는 뜻이다.
;; 즉, 평가식중에서 특정 일부분을 
;; 평가할것인지 말것인지를 켰다 껐다 할수있는 기능이다.
;; tilde(~) 는 quote(') 에서 쓰면 에러가 난다.
;; tilde(~) 는 back-tick(`) 과 같이 써야, 에러가 안난다.

(psil (4 5 +))
(println (psil (4 5 +)))
(println (macroexpand '(psil (4 5 +))))


(comment



;; cond 를 한번 보자.

(cond (= x 0) "영이다."
      (= x 1) "일이다."
      :else "다른거다.")


;; cond 는 다른언어에서 
;; switch 문 이나 case 문 처럼 생겼다.
;;



)



;; 다음 예제는 if 문도 만들수 있다는것을 
;; 보여주는 예제인데, 예제가 깔끔하지 않다.
;; 의미를 명확하게 보여주지 못한다.
;; 
;; 언어의 일부분인 if 문까지도 매크로를 통해서 
;; 만들수 있다는 것을 보여주려는 의도이다.
;; 그런데 사용한 cond 예제코드가 
;; 최적의 선택은 아닌것으로 생각된다.


;; my-if 를 함수로 만들면 부작용이 발생한다.
(defn my-if [predicate if-true if-false]
  (cond predicate if-true
        :else if-false))

(my-if (= 0 0) "equals" "not-equals")
(println (my-if (= 0 0) "equals" "not-equals"))

(my-if (= 0 1) "equals" "not-equals")
(println (my-if (= 0 1) "equals" "not-equals"))

(println "-------------------")
(my-if (= 0 0) (println "equals") (println "not-equals"))


;; 이 부작용을 매크로가 해결해준다.

(defmacro my-if [predicate if-true if-false]
  `(cond ~predicate ~if-true
        :else ~if-false))


(println "#-------------------")
(my-if (= 0 0) (println "equals") (println "not-equals"))
(my-if (= 0 1) (println "equals") (println "not-equals"))


(println (= 0 0))
(println (= 0 1))
(println (macroexpand '(my-if (= 0 0) (println "equals") (println "not-equals"))))
(println (macroexpand '(my-if (= 0 1) (println "equals") (println "not-equals"))))


Firefly Algorithms

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