레이블이 clojuredocs인 게시물을 표시합니다. 모든 게시물 표시
레이블이 clojuredocs인 게시물을 표시합니다. 모든 게시물 표시

2018년 1월 26일 금요일

[clojure] 클로저로 mx-record 구하기 (mx-record in clojure)

예전에 MX-RECORD 받아와야 할 때, 자바로 된 것을 클로저로 바꾸면 어떨까 해서 한 번 짜봤던 것이 있다.

시작은 이렇게 하자. nslookup 을 사용할 것이다.
(use '[clojure.java.shell :only [sh]])

(sh "notepad" "a.txt")
(sh "notepad" "/a.txt")
(sh "pwd")
(sh "nslookup")
(sh "nslookup" "-query=mx" "google.com")

뭔가 보이는 것 같다.
(defn find-mx [hostname]
  (letfn [(nslookup [hostname] (sh "nslookup" "-query=mx" hostname))]
    (nslookup hostname)))

(find-mx "google.com")
자세히 보아하니 뭔가 엄청 나오는 거 같다.
좀 더 정리해보자. 보아하니 내가 찾으려는 내용에는 mail이라는 거와 exchanger라는게 붙어 있는 것 같다.
일단 해보자.
(defn find-mx [hostname]
  (letfn [(nslookup [hostname] (sh "nslookup" "-query=mx" hostname))]
    (remove nil? (map #(re-matches #"(.+)mail\sexchanger(.+)" %) 
                       (clojure.string/split (second (second (nslookup hostname))) #"\r\n")))))

(find-mx "google.com")

(["google.com\tMX preference = 10, mail exchanger = aspmx.l.google.com" "google.com\tMX
preference =
10, " " = aspmx.l.google.com"] ["google.com\tMX preference = 30, mail exchanger = alt2.aspmx.l.goog
le.com" "google.com\tMX preference = 30, " " = alt2.aspmx.l.google.com"] ["google.com\tMX
preference
= 20, mail exchanger = alt1.aspmx.l.google.com" "google.com\tMX preference = 20, " " =
alt1.aspmx.l
.google.com"] ["google.com\tMX preference = 50, mail exchanger = alt4.aspmx.l.google.com"
"google.co
m\tMX preference = 50, " " = alt4.aspmx.l.google.com"] ["google.com\tMX preference = 40, mail exchan
ger = alt3.aspmx.l.google.com" "google.com\tMX preference = 40, " " =
alt3.aspmx.l.google.com"])

나온 거 같다. 다른 테스트도 해보자.
user=> (count (find-mx "google.com"))
5
user=> (count (find-mx "google33.com"))
0
user=> (count (find-mx "naver.com"))
3

MX레코드가 있는지 없는지만 확인하고 싶다면
(count (find-mx hostname)) 0)
  true
  false)

(mx-exist? "google.com")
true
(mx-exist? "google3134.com")
false

2017년 1월 26일 목요일

[clojure] 클로저 소개글

사내 블로그를 적어야 할 일이 있어서(할당량...) 무엇을 적을 까 하다가
클로저에 대해 한 번 써보았다.
좀 약파는 글이기는 한데 재미있었다.
http://blog.naver.com/andwise/220920969200

2017년 1월 22일 일요일

[clojure]nil 체크

nil 과 false를 제외한 모든 것이 반복문이나 조건문에서 true가 된다.

user=> (if nil true false)
false
user=> (if false true false)
false
user=> (if -1 true false)
true
user=> (if 0 true false)
true
user=> (if "false" true false)
true
하지만 비어있는 컬렉션컬렉션은?
user=> (if [] true false)
true
이럴수가. 하지만 이렇게 하면 된다.
user=> (if (not (empty? [])) true false)
false
이게 뭔고생인가. Joy Of Clojure에서는 다른 방법을 제시한다. 바로 (seq s)이다.
user=> (defn print-seq [s]
  #_=>   (when (seq s)
  #_=>     (prn (first s))
  #_=>     (recur (rest s))))
#'user/print-seq
user=> (print-seq [])
nil

여기서는 rest를 쓰고 next를 쓰지 말라고 하는데 왜인지 한번 보자.
user=> (defn my-seq [s]
  #_=>   (when (seq s)
  #_=>     (prn (first s))
  #_=>     (recur (next s))))
#'user/my-seq
user=> (my-seq [])
nil

음... 이게 문제는 아닌 것같다.
user=> (next [])
nil
user=> (rest [])
()
이게 문제인 것 같다.
rest는 시퀀스가 비어있는지 아니던지 nil를 리턴하지는 않는다.
next는 바로 nil을 리턴한다.
우리는 각 반복 시점마다 (seq s)를 쓴다 그러니 시퀀스를 다루는 rest를 넣는게 더 조합이 잘되있다고 보는 것이다. (nil이 나오지 않게 하려 하는 점은 좋은 생각이다.)

** 여담으로 컬렉션을 순회할 때는 재귀보다는 doseq를 사용하는 편이낫다고 한다. 다만 doseq를 사용하면 위철머 살펴 보는 것은 힘들다. 클로저에서 do로 시작하는 구문 (doseq, dotimes, do 등)은 부수 효과를 일으키기 위해 사용하기 때문에 보통 결과로 nil을 리턴한다.

[clojure][macro] 매크로사용하기 00

Code is Data 라는 말은 코드 자체가 프로그래밍 언어의 자료구조가 된다는 말이다. 클로저 매타프로그래밍(macro)를 하게 되면 우리는 expression level에서 생각하게 된다 rather than at the textual level.

코드 자체가 데이타(자료구조)이기 때문에 이것을 맘대로 바꿀 수 있다.

어쨋든 이게 뭐라는 거야
제대로 알아보자. (REPL을 예로들자)

REPL : Read-Eval-Print loop

Read : 문자단위표시방식 (character-based representation, typically an input stream) 을 받아서 클로저 자료구조로 변환한다. 그래서 Read phase(읽기상태)의 아웃풋은 data(데이타)이다. 이 데이터는 두번째 상태에서 평가된다. (데이터로!)

EVEL : 어떻게 EVAL에서 code-as-data에서 data-as-code로 변할까 (자료구조안에 있는 코드들이 코드로 만들어진 데이터)

read는 표현식(매크로가 잘 작동하는)을 작성 할 때(building the expression) 편한다. 다음의 인풋과 아웃풋을 보자. (어떻게 이게 동작하는지 잘보자.)

첫 매크로는 바로 머리에 때려박아주기 적절한 예시가 될것(ll give you enough to dive headfirst into your first macro.)

실제 클로저의 read 함수는 stream을 사용한다.(consume stream) 이런 행위는 셋업하는데 많은 짓을 해야 할지도 모른다.(verbose to set up.)

이 예제의 목적에 따라. read의 형제격인 read-string을 받겠다. (stream 대신 strings를 받는 것) 
 =================================================
(read-string "(+ 1 2 3 4 5)") 
;==> (+ 1 2 3 4 5)
(class (read-string "(+ 1 2 3 4 5)"))
;=> clojure.lang.PersistentList

 =================================================

여기 이 리스트가 우리가 지금까지 이야기 했던 데이터의 조각이다.
이건 클로저 리스트다. 그리고 아직까지는 클로저 코드다.  (Code as Data)
readstring에서 돌아온 값은 평가될 준비가 되어있다.
이 리스트를 평가할 때, 그 표현식의 값을 알 수 있을 것이라 기대한다.

=================================================
(eval (read-string "(+ 1 2 3 4 5)"))
;=> 15
(class (eval (read-string "(+ 1 2 3 4 5)")))
;=> java.lang.Long
(+ 1 2 3 4 5)
;=> 15
=================================================
이게 REPL에서 실제로 일어나고 있는 일이다. 아니면 완전체 클로저(full-blown clojure project)를 실행할때에도 실제 일어난다. 코드는 자료구조(data structure)로 읽어지고(be read into, 저장되고) 그러고 나서 평가된다(eval). 이 두 스텝(read, evaluate)을 놓고 보았을 때,
read와 eval 사이에 코드자신이 평가되기 위해 자기자신을 끼워넣는 것(꼽사리로 들어가는 것)을 어렵지 않을 것이다.


예를들어 we could replace addition with multiplication:
=================================================


(let [expression (read-string "(+ 1 2 3 4 5)")
  (cons (read-string "*")  ;; 꼽사리로 들어가는 현장
        (rest expression)))
;=> (* 1 2 3 4 5)

(eval *1) ;; *1 holds the result of the previous REPL evaluation
;=> 120
=================================================
이게 무슨 뜻이나면 read-string에다가 일단 곱하기(*)를 넣고 그 다음에는 더하기(+)를 제외한 값(rest)를 가져와서 합친것이다.

물론, 만약 새롱누 리스트를 평가(eval)한다면, 원래 결과와는 전혀 다른 내용이 나올 것이다. 이전에 나온 예시도 나쁘지 않지만 단순히 문자열값을 읽는 것보다는 제대로 표현식을 지어보자.
냅다 이런 "(+ 1 2 3 4 5)" 스트링을 만드는 것은 이상하단 말이야.
하지만 이런 형태의 리스트를 생성하고 싶다면 단순히 (+ 1 2 3 4 5) 이렇게 타입한다고 될 일이 아니다. 왜냐하면 이것 실제로 표현식으로 평가될 것이기 때문이다.
=================================================
(let [expression (+ 1 2 3 4 5)] ;; expression is bound to 15
  (cons
    (read-string "*") ;; *
      (rest expression))) ;; (rest 15)
; IllegalArgumentException Don't know how to create ISeq from: java.lang.Long
; clojure.lang.RT.seqFrom (RT.java:505)
=================================================
보아라 (+ 1 2 3 4 5)가 바로 그냥 15로 평가 되는 것이다. 왜냐하면 (실제로 표현식으로 평가되기 때문에!)

그래서 다른 솔루션이 필요하다. 하나는 실행을 억제하는 거다. 운좋게도 클로저에는 quote라는 동사(verb, 이 verb에 유념하라 function이 아니다)가 똑같은 일을 한다.
=================================================
(let [expression (quote (+ 1 2 3 4 5))]
  (cons (quote *)
        (rest expression)))
;=> (* 1 2 3 4 5)

(eval (let [expression (quote (+ 1 2 3 4 5))]
  (cons (quote *)
        (rest expression))))
;= 120
=================================================

quote verb는 사실 클로저의 특별한 형태(Clojure special form)이며 함수가 아니다.  우리는 이 verbs를 (공식적인 의미는 아님) 함수,매크로 그리고 special forms의 결합체라고 생각하면 된다.

리스트(Lists) 또한 verb 위치로 보여질 수 있는데 평가(eval)될 때는 함수위치로 축소되어야 한다.

함수(function)만이 아규먼트(여기서 아규먼트란 평가될 모~든 데이터를 말하는 듯 하다)를 제대로 평가한다. (컨트롤흐름을 코드(구현한 verbs)로 패스하기전에)
  * Only functions uniformly evaluate their arguments before passing control to the code that implements the verb.


=================================================
(defn print-with-asterisks [printable-argument]
  (print "*****")
  (print printable-argument)
  (println "*****"))

(print-with-asterisks "hi")
; *****hi*****
;= nil
=================================================
verb - 어떻게 정의하나? - 언제 아규먼트가 평가되나?
Functions - defn - Before executing the body
Macro -defmacro - Depends on macro; possibly multiple times or never.
Special form - We can’t! They’re only defined by the language. - Depends on special form; possibly multiple times or never.

자 위에 나온 print-with-asterisks같은 걸 만들 때는 굳이 언제 아규먼트가 평가(timing of the argument evaluation)되는지 알필요가 없다.

하지만 만약! 표현식을 아규먼트로 쓴다면! 그 해당 함수(print-with-asterisks)가 평가되기 전에 평가된다. (???)
 =================================================
(print-with-asterisks
  (do (println "in argument expression")
  "hi"))
=================================================
잘보라 사용하기 전에 이미 김이 다 빠져나간거다.
Macros and speical forms, 는 이 룰에서 자유롭다. 그리고 어떤 순서에서 정의한 거든 아규먼트를 평가할 것이다. (아니면 이상하게 작동해서 망하던가...)  이게 함수와 매크로의 큰 차이란다.

quote expression (quote ...)안에 있는 코드는 평가되지 않는다. 그리고 아주 단순한 토큰들도 가능하다. (심볼, 리스트, 벡터.. 모두)
=================================================
(quote 1)
;= 1
(quote "hello")
;= "hello"
(quote :kthx)
;= :kthx
(quote kthx)
;= kthx
=================================================
근데 귀찮게 항상 QUOTE을써야 하나? 아니다 더 좋은게 있다. 
Reader macro라는 것이다. 이건 우리가 만들 수 있는게 아니다. 언어딴에서 만들어진거라 한다.
=================================================

'(+ 1 2 3 4 5)
;= (+ 1 2 3 4 5)
'map
;= map
user.core=> map
#object[clojure.core$map 0x589a6fa "clojure.core$map@589a6fa"]
user.core= 'map
map
=================================================
오... 확실히 먼가 다르다.
=================================================
(let [expression '(+ 1 2 3 4 5)]
  (cons '* (rest expression)))
;= (* 1 2 3 4 5)
=================================================

2016년 12월 10일 토요일

[clojure] ref 사용하기



ref

이전 시간에 atom을 공부했다. 그런데 왜 ref라는게 필요할까? 값을 보호하는데 atom 하나만 있으면 되는거 아닌가? 하지만 여러개의 행위를 트랜잭션처럼 묶고싶다면? ref를 쓰는 것이 좋다.
user=> (def items
  #_=>   #{"backpack" "phone" "card" "water" "dairy"})
#'user/items
user=> (defn item-count [item count]
  #_=>   {:name item :count count})
#'user/item-count


user=> (def shelves (ref {:name "YH Market" :items 
         (set (map #(item-count % (rand-int 10)) items))}))
#'user/shelves
user=> shelves
#object[clojure.lang.Ref 0x7b5ff2de 
{:status :ready, :val {:name "YH Market", :items #{{:name "backpack", :count 0} 
{:name "card", :count 7} {:name "phone", :count 7} {:name "dairy", :count 8} 
{:name "water", :count 6}}}}]
user=> (:name shelves)
nil
user=> (:name @shelves)
"YH Market"
user=> (:items @shelves)
#{{:name "backpack", :count 0} {:name "card", :count 7} 
{:name "phone", :count 7} {:name "dairy", :count 8} {:name "water", :count 6}}

user=> (defn buy [shelves] (dosync
  #_=>   (when-let [goods (some #(if (> (:count %) 0) %) (:items @shelves))]
  #_=>     (let [updated-goods (item-count (:name goods) (dec (:count goods)))]
  #_=>       (alter shelves update-in [:items] disj goods)
  #_=>       (alter shelves update-in [:items] conj updated-goods)))))
#'user/buy
user=> (buy shelves)
{:name "YH Market", :items #{{:name "phone", :count 2} {:name "dairy", :count 8} 
{:name "card", :count 5} {:name "water", :count 6} {:name "backpack", :count 2}}}
user=> (buy shelves)
{:name "YH Market", :items #{{:name "phone", :count 1} {:name "dairy", :count 8} 
{:name "card", :count 5} {:name "water", :count 6} {:name "backpack", :count 2}}}
진열대를 만들고 거기에 item를 넣는다. 그리고 그 갯수는 임의로(0부터 10까지) 정했다. (buy shelves)로 하나를 사면? 하나가 사라진다. (문제는 대충 만들어서 물건을 고를 수 없음 ㅎㅎ..)

[clojure] atom



왜 아톰이라는 것이 존재 하는가.
user=> (def a 1)
#'user/a

user=> (inc a)
2

user=> a
1

user=> (def b (inc a))
#'user/b

user=> b
2
느껴지는가? a는 한번 정의되면 바뀌지 않는다. 대신 새로 정의해야한다. 옛 철학자들은 수를 신처럼 받아들이기도 했다. 값은 변하지 않는다. 이건 불변의 진리다. int a = 0; a++; 이걸 사용하면서 값이 변한다고 믿어왔는가? 값이 변했다고 생각하지만 값은 변한것이 아니다. 시간이 변했을 뿐. 값은 새로 창조되었다. 뭐 이런 이야기로 해도 실재 세상에서도 한 곳에서 값이 변해야 하는 상황이 있다. 그럴때 atom을 사용하는 것이다.
user=> (def my_atom (atom 0))
#'user/my_atom
user=> @my_atom
0
user=> (swap! my_atom inc)
1
user=> @my_atom
1
대충 무슨 짓을 했는지 알기 바란다. (atom 0)으로 아톰을 생성했고, @를 붙여서 아톰의 값을 한번 봐보았다. 그리고 swap! 이놈으로 값을 변화시켰다. 그리고 다시 @로 값을 보면??? 값이 변했다!! 이 외에도 다른 방법으로 변화를 시키는 애가 있다.
(reset! my_atom 0)
0
@my_atom
0

[clojure] 검증자 (validator)



validator

검증자(validator)는 어떤 상태를 참조로 써도 좋은지 정한다.
만약 10을 넘으면 안되게 하는 validator를 만들어보자.
(defn max-ten-validator
  [{:keys [number]}]
  (< number 10))

(def n
  (atom
    {:number 0 :name "Younghwan"}
     :validator max-ten-validator))

user=> (swap! n update-in [:number] + 3)
{:number 3, :name "Younghwan"}
user=> (swap! n update-in [:number] + 3)
{:number 6, :name "Younghwan"}
user=> (swap! n update-in [:number] + 3)
{:number 9, :name "Younghwan"}
user=> (swap! n update-in [:number] + 3)
IllegalStateException Invalid reference state  clojure.lang.ARef.validate (ARef.java:33)

[clojure] date time 날짜 시간



Clojure Date/Time

본 내용은 tutorialspoint를 보고 거의 똑.같.이. 정리한 것이다. 자바를 아십니까? 이 3가지를 이용하여 시간을 뽑아보자. 1 Java.Util.Date 2 java.text.SimpleDateFormat 3 getTime

1. Java.Util.Date

(def date (.toString (java.util.Date.)))

(println date)
"Fri Dec 09 11:21:14 KST 2016"

2. SimpleDateFormat

(def date2 (.format (java.text.SimpleDateFormat. "MM/dd/yyyy") (new java.util.Date)))

date2
"12/09/2016"

3. getTime

(import java.util.Date)

(def date (.getTime (java.util.Date.)))

date
1481251602775

2016년 12월 8일 목요일

[clojure-docs]update-in



update-in

(update-in m [k & ks] f & args) 도큐먼트내용처럼 내용을 바꾼다.(update) 첫번째 아규먼트는 바꿀 자료구조(m) 그 다음은 자료구조 안에 있는 키값([k & ks]) 그리고 f 는 키 값에 걸리는 놈을 실행할 함수! 그 뒤는 그 함수에서 사용할 아규먼트!
(def here {:name "Younghwan" :age 26})
(update-in here [:age] * 10)
{:name "Younghwan", :age 260}
이해 되었길 빈다.

2016년 12월 7일 수요일

[clojure-docs]add-watch



add-watch

나는 클로저에 대해 많이 관심이 많다. 그런데 이 watch에 대해 제대로 알지는 못한다. 내가 아는 것은 watcher로 해당 원자값의 상태를 지속적으로 확인한다는 것이다. 그 시작은 watcher를 더하는 것으로 시작되는 것이다. 관찰(watch)는 인자 네 개를 받는 함수이다. 1. 키 2. 관찰되는 참조 3. 이전 상태 4. 새로운 상태 이것이다. 링크에 있는 첫번째 문장을 보자. Adds a watch function to an agent/atom/var/ref reference. The watch fn must be a fn of 4 args: a key, the reference, its old-state, its new-state. 일단 보면 모르겠는데 일단 이상한 보면 무슨 느낌인지 알 것이다.
(def at (atom 0)) 
#'fwpd.core/at

(add-watch at :watcher
  (fn [key atom old-state new-state]
    (println "The valueof the atom has been changed")
    (println "key is " key)
    (println "atom is " @atom)
    (println "old-state is " old-state)
    (println "new-state is " new-state)
    (if (> new-state 10)
      (println "TOO BIG!!!!!!")
      (println "HAHA"))))
#object[clojure.lang.Atom 0x20788a98 {:status :ready, :val 0}]
자 아톰을 생성했다. 아톰이 뭔지 모른다고? 그냥 값이라고 생각하자. 그런데 아주 안전한 값이다. 그래서 단순히 값을 변경할 수 없다. 바꾸려면 특정 함수를 써야 하는데 그것은 swap! 이다. 중요하기 때문에 느낌표까지 붙어있다. 기억하자. 일단 실행해보겠다.
(swap! at inc)
The valueof the atom has been changed
key is  :watcher
atom is  1
old-state is  0
new-state is  1
HAHA
1
;;10까지 올려보자
(swap! at inc)
The valueof the atom has been changed
key is  :watcher
atom is  11
old-state is  10
new-state is  11
TOO BIG!!!!!!
11
무엇이 바뀌었는가??? HAHA 에서 TOO BIG!!!!!! 으로 바뀌었다. 바로 watcher가 이렇게 계속 관찰해서 반응한 것이다.

[clojure-docs]merge-with



merge-with

말처럼 합치는 것이다(merge). 그런데 합치면서 무얼하는 것(with)이다. 링크에 있는 내용을 보면서 위에 한 문장을 곱씹자.
(merge-with + {:a 1 :b 1 :c 1} {:a 10 :c 13})
{:a 11, :c 14, :b 1}
이렇게 병합하면서(merge) 더한다.(with)

2016년 11월 23일 수요일

[clojure-docs]assoc-in



assoc-in

assoc-in은 정말 어려운 함수이다. 정말 처음엔 뭐 이런게 있나 싶다.
(assoc-in {} [:cookie :monster :vocals] "Finntroll")
{:cookie {:monster {:vocals "Finntroll"}}}
이것은 잘 보자. {}안에 :cookie 안에 :monster 안에 :vocals 안에 "Finntroll"을 넣는 것이다. 그렇다면 이걸보자. 내가 임의로 비슷한 함수를 만들었다.
((fn [new-board [p1 p2] neighbor] (assoc-in new-board [p1 :connections p2] neighbor)) {} [1 3] 4  )
{1 {:connections {3 4}}}
보면 assoc-in은 new-board안에 순서대로 넣는 것이다. {} 안에 1 그리고 그 1안에 :connections 그리고 그 안에 3 그리고 그 값으로 4를 넣은 것이다. 정리하면 assoc의 매개변수는 순서대로 1. 값을 들어갈 곳 2. 키값의 덩어리들 3. 키값 안에 들어가 숨어있을 값 좀 더 이해를 위해 get-in을 사용해보자.
(get-in {1 {:connections {3 4}}} [1])
{:connections {3 4}}

(get-in {1 {:connections {3 4}}} [1 :connections])
{3 4}

[clojure-doce][tip] 함수만들기의 이상한 아규먼트



내가 의아하게 본 소스가 이거다.
(defn a [new-board [a b]]
...
)
뭐 이런 식으로 만들어지는 함수를 보았는데 참으로 신기했다. 그래서 하나씩 다 넣어봤는데 결과를 얻었다. 잘 보자.
(defn a [new-board [a b]]
  new-board
)

(a [1] [2 3])
[1]
나머지 a, b도 무엇인지 보자.
(defn a [new-board [a b]]
  a
)

(a [1] [2 3])
2

(defn a [new-board [a b]]
  b
)

(a [1] [2 3])
3

[clojure-docs]memoize



memoize

클로저에서 값은 불면하다. 그렇기에 이런 이상한 일이 가능한 것인데 호출한 함수를 메모리에 올려서 기억시키는 일이다. (memoize: 이전에 계산한 값 자체를 저장하여 프로그램 실행 시간을 빠르게 하는 기술)
(defn sleep [x] (Thread/sleep 1000) x)

(sleep "Mr.KIM")
"Mr.KIM"

(sleep "Mr.KIM")
"Mr.KIM"
당연히 둘다 잘 나올 것이다. 단 3초 뒤에 나오는 것이다. 만약 이 3초가 강제로 잠재운 것이 아니라 오랜 시간 동안 계산해서 얻어내야 하는 값이라면?
(def memo-sleep (memoize sleep))

(memo-sleep "Mr.KIM")
"Mr.KIM"

(memo-sleep "Mr.KIM")
"Mr.KIM"
값은 같지만 두번째로 호출 할때 3초를 쉬지않고 바로 일을 한다. 왜냐하면 값을 이미 가지고 있기 때문ㅇ

[clojure-docs]Decision-Making 조건문



조건문

tutorial point에는 다섯개의 조건문을 소개한다. if if/do nested if case cond

if

user=> (defn one? [x]
  #_=>     (if (= x 1)
  #_=>       (println "EQUAL!!!!")
  #_=>       (println "no...")))
#'user/one?
user=> (one? 1)
EQUAL!!!!
nil

if/do

if문의 큰 단점이 있는데 그것은 하나만 할 수 있다는 것이다. 그래서 do를 붙인다. 일단 보자.
user=> (defn two? [x]
  #_=>   (if (= x 2)
  #_=>     (do (println "TWO!!!!")
  #_=>         (one? x))
  #_=>     (do (println "not Two")
  #_=>         (one? x))))
#'user/two?
user=> (two? 1)
not Two
EQUAL!!!!
nil
여러 개의 일을 do안에 넣는 것이다.

nested if

(defn Example [] (
   if ( and (= 2 2) (= 3 3))
   (println "Values are equal")
   (println "Values are not equal")))
(Example)
그냥 and를 사용할 수 있다는 점을 기억하기 바란다.

case

case expression
value1 statement #1
value2 statement #2
valueN statement #N
statement #Default
case문이라고 생각하면 될듯하다.
user=> (defn score [x]
  #_=>   (case x
  #_=>   10 (println "score 10")
  #_=>   (println "not 10")))
#'user/score
user=> (score 10)
score 10
nil

cond

cond
(expression evaluation1) statement #1
(expression evaluation2) statement #2
(expression evaluationN) statement #N
:else statement #Default
그냥 condition이라 생각하면 편하다
(defn Example []
   (def x 5)
   (cond
      (= x 5) (println "x is 5")
      (= x 10)(println "x is 10")
      :else (println "x is not defined")))
(Example)

[clojure-docs]loop 루프


loops

클로저에서 반복문 덩어리들을 죄다 살펴보자. tutorial point에는 4가지 반복문이 소개되고 있다. while doseq dotimes loop

while

(while(expression)
   (do
      codeblock))
while문의 expression이 true인 동안만 codeblock을 실행시키는 것이다.
(let [x 10 y (atom 0)]
    (while (< @y x)
      (do
        (println @y)
        (swap! y inc))))
0
1
2
3
4
5
6
7
8
9
nil

doseq

(doseq (sequence)
   statement#1)
doseq는 'foreach'문과 비슷하다. 일단 보면서 foreach문을 연상하자.
(def seq [1 2 3 4 5 6 7])

(defn go [seq]
  (doseq [n seq]
    (println n)))

(go seq)
1
2
3
4
5
6
7
nil

dotimes

(dotimes [n 10]
    (println n))
1
2
3
4
5
6
7
8
9
nil
횟수만큼 index가 돈다고 생각하면 된다.

loop

loop [binding]
(condition
   (statement)
   (recur (binding)))
loop는 for-loop를 말하는 것이 아님. loop은 let처럼 값을 바인딩하고 recursion포인트를 지정한다. (그러니 재귀로 돌아갈 곳을 지정한다)
(loop [x 10]
    (if (< x 1)
      x
      (recur (- x 1))))
0

2016년 11월 21일 월요일

[clojure-docs] ->



->

-> 이건 사실 없어도 된다. 괄호의 지옥에서 벗어나기 위해 만들어진 창과 같은 것이다. 링크에 있는 예제를 보자.
(first (.split (.replace (.toUpperCase "a b c d") "A" "X") " "))
"X"

(-> "a b c d" 
           .toUpperCase 
           (.replace "A" "X") 
           (.split " ") 
           first)
"X"
괄호만 있을 때 보다 화살표로 만들어진 것이 더 읽기 쉽다.

2016년 11월 20일 일요일

[clojure-docs]comp



comp

comp함수는 함수 여러개를 같이 실행시키게 되는 것이다.
((comp str +) 8 8 8)   ;;=> "24"
기억해야 할 것은 오른쪽에서 왼쪽으로 실행한다는 것이다 comp를 써서 문자 속성 값을 얻는 방법
(def character
  {:name "Smooches McCutes"
   :attributes {:intelligence 10 :strength 4 :dexterity 5}})

(def c-int (comp :intelligence :attributes))
(def c-str (comp :strength :attributes))
(def c-dex (comp :dexterity :attributes))

(c-int character)
10
(c-str character)
4
(c-dex character)
5
이게 뭐야? 왜 저런 값이 나오는 거지? 라고 생각할 수 있다. 자 comp가 어디서부터 실행된다고? 오른쪽에서 왼쪽으로
(#(:intelligence (:attributes %)) character)
10
(#(:strength (:attributes %)) character)
4
(#(:dexterity (:attributes %)) character)
5
이렇게 되는 것과 같다. 어떻게 이렇게 되는 건가?
(defn two-comp
  [f g]
  (fn [& args]
    (f (apply g args))))

[clojure-docs]assoc



assoc

(assoc {} :key1 "value" :key2 "another value")
{:key2 "another value", :key1 "value"}
딱 보면 알듯이 {}안에 다 넣는 것이다.
(assoc nil :key1 4)
{:key1 4}
(assoc {} nil nil)
{nil nil}

[clojure-docs][tip] parse



parse

parse에 사용할 만한 아주 단순한 패턴이다.
(defn parse
  "Convert a CSV into rows of columns"
  [string]
  (map #(clojure.string/split % #",")
    (clojure.string/split string #"\n")))
순간 이게 뭔가 싶었다.
(clojure.string/split string #"\n")
이것만 일단 실행해보자.
자 이렇게 벡터로 만들어진다. 이거에서 하나하나 쉼표로 나누려면 map으로 하는 것이다. 하나씩 나누는 것이다.