Clojure에서 println 어렵게 해보기

 

lamda 함수 및 "->" 연산자의 작동 방식 이해를 위해

;; 1)
(println "hello")

;; 2)
((fn [str] (println str)) "hello")

;; 3)
(-> "hello" (println))

;; 4)
(-> "hello" ((fn [str] (println str))))
,
Clojure에서 키워드는 앞에 :이 붙은 요소인데, 설명을 읽어봐도 잘 이해가 되지 않으므로 사용용도를 통해 그 정체를 기억하자.


맵의 이름은 함수처럼 사용해서 데이터를 참조하는 것이 가능한데, 키워드를 키로 지정할 경우, 이 키워드도 함수처럼 사용하는 것이 가능하다. (다른 맵 간에 키워드 중복 사용도 무방)



맵 정의

(def planet {:name "earth" :type "terrestrial"
        :satelite {:sateliteName "moon" :alternateName "luna"}})


데이터 참조

; 아래 세 구문 모두 같은 값 "terrestrial"을 갖는다.

(planet :type)
(get planet :type)
(:type planet)  ; 만약 키가 "type"(문자열)이라면, 이런 식의 사용은 불가능하다.

-> "terrestrial"


연쇄 참조

; 아래 세 구문 모두 같은 값 "luna"을 갖는다.

(:alternateName (:satelite planet))
(-> planet :satelite :alternateName)
(get-in planet [:satelite :alternateName])

-> "luna"


키가 존재하지 않을 경우

; 존재하지 않는 키인 키워드를 사용할 경우 nil을 반환한다.

(:orbit planet)

-> nil


키워드에 네임스페이스 사용

; / 기호를 사용해 키워드에 네임스페이스를 줄 수도 있다.

(:etc/alternateName {:name/sateliteName "moon" :etc/alternateName "luna"})

-> "luna"


<테스트 환경>
- OS : Windows 10
- Leiningen 버전 : 1.0.0
,

Clojure 활용의 핵심! 자바 클래스 사용방법에 대해 정리해보았습니다.

 

 

클래스 import

; 풀네임으로 쓰는 방법
java.util.Date  ; => java.util.Date


; 줄여서 쓰는 방법
(import java.util.Date)
Date  ; => java.util.Date

 

자동 import

; java.lang 밑에 있는 클래스들은 별도의 import 없이 줄여쓰는 방법으로 쓸 수 있다
Math  ; => java.lang.Math
String  => java.lang.String

 

이너(Inner) 클래스 import

; 풀네임으로 쓰는 방법
java.util.Map$Entry  ; => java.util.Map$Entry


; 줄여서 쓰는 방법
(import java.util.Map$Entry)
Map$Entry  ; => java.util.Map$Entry

 

인스턴스 생성

; new 구문을 사용
(new java.util.Date)

; 약식 구문
(java.util.Date.)

; 약식 구문 (import 후)
(import java.util.Date)
(Date.)

; 생성자 파라미터 전달하는 경우
(java.net.URI. "http://clojure.org")

 

인스턴스 메소드 호출

; . 구문을 사용
(let [d (java.util.Date.)]
  (. d getTime))

; 약식 구문
(let [d (java.util.Date.)]
  (.getTime d))

 

정적 메소드 호출

; . 구문을 사용
(. Math floor 5.677)

; 약식 구문
(Math/floor 5.677)

 

연쇄 호출

(.. (java.util.Date.) getTime toString)  ; (-> (java.util.Date.) (. getTime) (. toString)) 과 동일
                                         ; (-> (java.util.Date.) (.getTime) (.toString)) 과도 동일

 

동일 오브젝트에 복수 호출

(doto (java.util.Stack.)
  (.push 42)
  (.push 13)
  (.push 7))  ; 첫번째 파라미터의 오브젝트가 나머지 호출의 첫번째 파라미터로 들어간다

; 아래와 같이 써도 동일
(doto (java.util.Stack.)
  (. push 42)
  (. push 13)
  (. push 7))

 

자바 필드(멤버 변수) 읽기

; . 구문을 사용
(let [pt (java.awt.Point. 10 20)]
(. pt x))  ; pt.x를 읽음

; 약식 구문
(let [pt (java.awt.Point. 10 20)]
(.x pt))

 

자바 필드(멤버 변수) 쓰기

(let [pt (java.awt.Point. 10 20)]
  (set! (.y pt) 100))  ; pt.y -> 100으로 세팅됨

 

Enum 타입 사용

java.util.concurrent.TimeUnit/MILLISECONDS

 

상수/인스턴스의 자바 클래스 얻기

(class 1)       ; => java.lang.Long
(class "docs")  ; => java.lang.String
(class (java.net.URI. "http://github.com"))  ; => java.net.URI

 

클래스명으로 클래스 얻기

(Class/forName "java.util.Date")  ; => java.util.Date

 

참고: http://clojure-doc.org/articles/language/interop.html

<테스트 환경>
- OS : Windows 10
- Leiningen 버전 : 1.0.0

 

,
1) ->를 사용하지 않은 코드
(reduce (map (map xs bar) foo) baz)
2) ->를 사용한 코드
(-> xs (map bar) (map foo) (reduce baz))
  • 1번과 같은 중첩된 호출 대신에 2번과 같은 순차적인 호출로 표현할 수 있게 해준다.

  • 앞쪽의 함수의 호출 결과가 그 다음 호출의 첫번째 파라미터로 들어간다. (xs는 변수)


<테스트 환경>
 - OS : Windows 10
 - Leiningen 버전 : 1.0.0
,

다양한 선택권을 제공하는(..이라고 쓰고 ‘헷갈리는’ 이라고 읽는다) Clojure 실행방법과 그 사용 조건에 대해 정리해보겠다.



1.

lein run -m <네임스페이스>/<함수명>
  • 사용 조건

    • 없음

2.

lein run -m <네임스페이스>
  • 사용 조건

    • 해당 네임스페이스에 -main 함수 존재

3.

lein run
  • 사용 조건 1

    • project.clj에 메인 네임스페이스 및 함수 지정
  • 사용 조건 2

    • project.clj에 메인 네임스페이스 지정되어 있고,

    • 해당 네임스페이스에 -main 함수 존재

4.

lein uberjar
java -jar <생성된 jar 파일명>.jar
  • 사용 조건

    • project.clj에 메인 네임스페이스 지정되어 있고,

    • 해당 네임스페이스에 -main 함수 존재하고,

    • 해당 네임스페이스에 (:gen-class) 옵션 존재

5.

lein uberjar
java -cp <추가적인 class path or jar 파일>;<생성된 jar 파일명>.jar
  • 사용 조건

    • project.clj에 메인 네임스페이스 지정되어 있고,

    • 해당 네임스페이스에 -main 함수 존재하고,

    • 해당 네임스페이스에 (:gen-class) 옵션 존재하고,

    • 추가적인 class path 혹은 jar 파일 지정 필요시

자바 명령으로 실행하기 위해서는 (:gen-class) 옵션이 들어가야 한다는 것과 -main 함수만을 엔트리 함수로 사용할 수 있다는 것에 유의하자.


<테스트 환경> 
- OS : Windows 7 
- Leiningen 버전 : 1.0.0
,

사전 준비사항

  1. app template을 사용하여 프로젝트를 생성한다.

    lein new app clj-test-01
    

  2. project.clj 파일의 dependencies 부분에 아래와 같이 필요한 모듈을 정의한다.

    (defproject clj-test-01 "0.1.0-SNAPSHOT"
      :description "FIXME: write description"
      :url "http://example.com/FIXME"
      :license {:name "Eclipse Public License"
                :url "http://www.eclipse.org/legal/epl-v10.html"}
      :dependencies [[org.clojure/clojure "1.8.0"] [clj-http "2.2.0"] [enlive "1.1.6" :exclusions [org.clojure/clojure]]]
      :main ^:skip-aot clj-test-01.core
      :target-path "target/%s"
      :profiles {:uberjar {:aot :all}})
    
    

HTML 파싱 예제

(ns clj-test-01.core
  (:gen-class) 
  (:require [net.cgrand.enlive-html :as html])
  (:require [clj-http.client :as client]))

(use 'clojure.pprint)

(defn html-data []
  ;; clj-html 모듈을 사용하여 웹페이지의 내용을 가져온다.

  (html/html-resource (java.io.StringReader.
      (:body (client/get "http://httpbin.org"))))
)

(defn -main [& args]
  
  ;; 태그로 찾기
  (pprint (html/select (html-data) [:a]))
  
  ;; id로 찾기
  (pprint (html/select (html-data) [:#AUTHOR]))
  
  ;; class로 찾기
  (pprint (html/select (html-data) [:.bash]))
  
  ;; 태그와 class를 조합해서 찾기
  (pprint (html/select (html-data) [:code.bash]))

)

반환값

html/select 함수의 결과는 아래와 같이 맵의 리스트 형태로 반환된다.

({:tag :a, :attrs {:href "http://httpbin.org"}, :content ("HTTP")}
  ... )

각각의 맵은 태그에 대한 정보를 담고 있는데, 태그의 내용물(content)이 단순 텍스트일 경우에 :content 부에 문자열의 리스트의 형태로 들어가며, 또다른 태그일 경우는 :content 부에 다시 맵의 리스트 형태로 들어간다.


태그의 내용물이 단순 텍스트일 때, 그 첫번째 문자열을 얻는 구문은 아래와 같이 된다.
(first (:content (first (html/select (html-data) [:.bash]))))

참고사이트


<테스트 환경>
- OS : Windows 7
- Leiningen 버전 : 1.0.0
,

사전 준비사항

  1. app template을 사용하여 프로젝트를 생성한다.


  2. seesaw 모듈 사용을 위해서 project.clj 파일의 dependencies 부분에 아래와 같이 추가해준다. (버전은 참고사이트에서 최신 버전을 확인하자)

    :dependencies [[org.clojure/clojure "1.8.0"] [seesaw/seesaw "1.4.5"]]
    


사용 예제


다음은 간단한 윈도우 창을 표시하는 예제이다.

(ns web.core
   (:gen-class)
   (:require [seesaw.core :as seesaw]))
(def window (seesaw/frame
   :title "First Example"
   :content "hello world"
   :width 200
   :height 50))
(defn -main
   [& args]
   (seesaw/show! window))


참고 사이트

,

Clojure에서 커맨드 파라미터 처리하는 예제 코드

(ns example.core
  (:gen-class))

(defn -main [& args]
  (println (format "args=%s" args))
  (if (not (empty? args))
    (doseq [arg args]
      (println arg)
    )
    (println "no argument")
  ))


테스트 버전
 - Leiningen 버전 : 1.0.0
,