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...