clojure – 네임스페이스안에 정의되어 있는 함수 보기

클로저 개발을 하다 보면, 네임스페이스 안에 정의되어 있는 함수들을 보고 싶을 때가 있다.

LightTable이 아닌 autocomplete가 제공되지 않는 IDE에서 개발을 할때면, 아.. 뭐지? 공욕을 치룰때가 있다.

그럼 어떻게 네임스페이스안의 함수들을 볼 수 있을지 알아 보자.

1. keys 함수를 이용하여, 함수 시퀀스 반환하기.

user=> (keys (ns-publics 'foo))

그 외 ns- 많은 함수들을 제공하니 찾아서 실험해보도록 하자.

2. dir 함수를 이용하기. dir함수는 clojure.repl에 선언되어 있는 함수이다.

user=> (require 'clojure.repl)
user=> (clojure.repl/dir 'foo)

그 외 다른 방법도 있으나, 위 두가지 방법이 제일 괜찮은거 같다.

이상.

Clojure – Clojure Cider REPL에서 쓰래드를 사용하였을 때 print 되지 않는 이유와 해결 방법

참고 링크

Q> Emacs Cider REPL에서 백그라운드 쓰레드에서 println을 돌렸을 때 왜 출력이 되지 않는가?

A> 원인은 출력되는 버퍼가 다르기 때문이다.

println의 행동은 동적으로 *out* 라는 output stream에 바인딩 되어 출력되는데, 만약 thread를 사용한다면, 그 thread는 루트 *out* 에 바인딩 될것이고, 그렇게 되면 사용하고 있는 REPL에서는 확인을 할 수 없다.

그러면 어디서 확인할 수 있는가?

buffer list에서 *nrepl-server*라는 이름을 가진 버퍼에서 확인 할 수 있다.

또는

(alter-var-root #'*out* (constantly *out*))

와 같이 바꾸어 기존 buffer에서 확인 할 수 있다.

참고 링크

만약 Cider REPL을 사용하지 않는다면, nrepl을 시작한 터미널에서 확인 할 수 있다.

Clojure – ` syntax quote에 대해서

`syntax quote에 대해서 알아보자.

`와 연관있는 ~ unquote, 그리고 ~@unquote splicing이 있다.

위 두가지는 syntax quote를 이용하였을 때 그 반대의 영향을 주기 위해서 사용된다.

심볼을 심볼 그 자체로 활용하기 위해 사용된다. 'quote와 비슷하면서도 다르다. sytax quote 설명은 다른 장에서 알아보자.


user> (def five 5)
user/five
user>`five
user/five

p>한가지 기억하자. Clojure에서 모든 것들이 데이터이다. 위와 같이 quote를 사용하였을 때 평가되지 않고 심볼 자체 또는 리스트 자체로 출력이 되는 것을 볼수 있다.

그럼 왜 quote가 필요할까? 한가지 더 실험을 해보자.

(1 2 3)
Unhandled java.lang.ClassCastException
java.lang.Long cannot be cast to clojure.lang.IFn

` 위와 같이 에러가 발생된다. 이유는 현재 1이라는 심볼이 정의 되어 있지 않기 때문에 발생된다. (, )에 둘러 쌓여있을 때 첫번 째 argument는 심볼로 인식된다.

다시 quote를 활용해보자.


user>`(1 2 3)
(1 2 3)

에러 없이 정상으로 작동된다.

다음 장에서 `syntax-quote와 '의 차이점에 대해서 알아보자.

reference

Clojure – ~ (unqoute)에 대해서

~ unqoute 매크로. ~ 는 unqoute 라고 읽는다. unqoute를 이해하기 위해서는 ` (syntax quote)에 대해서 알아야한다. 그럼 일단 syntax sqoute에 대해서 알아보자. 영어에서 syntax는 문법, 구문론 등으로 해석되며, qoute는 `으로 “인용하다.” 또는 “전달하다.”로 해석된다.

그럼 Clojure에서는 어떻게 사용될까?

클로저에서 메서드에 syntax qoute를 사용했을 때 말 그대로 메서드 자체(구문론 자체를)를 그대로 전달하라는 의미를 갖는다. 아래와 같다.


user> `(+ 1 1) (clojure.core/+ 1 1)

위와 같이 심볼 자체로 출력된다. unqoute는 이때 사용된다. 심볼을 평가하고 싶을때 아래와 같이 사용된다.


user>`~(+ 1 1) 2

심볼을 평가하고 싶을때 unqoute를 사용하면 된다.

위와 같이만 예로 들 경우 어디에서 사용해야 하는지 감이 제대로 잡히지 않는다. 좀 더 깊히 들어가보자.


user> (def three-and-four (list 3 4))

\'user/three-and-four

user> `(1 three-and-four)

어떤 값이 나올까? 예상하는 바와 같이 아래와 같이 출력된다. \'user/three-and-four

평가되지 않았다. 위 unqoute때문에 평가되지 않았다. 이때 1은 data이므로 평가하지 않아야하고 three-and-four만 평가를 하고 싶을때 아래와 같이 사용할 수 있다.

user>`(1 ~three-and-four)

어떻게 될까? (1 (2 3))


위와 같이 평가되어 위와 같이 출력된다.


다음장에서 ~@ unquote splicing macro를 사용하여 다른값을 유추해보자.

Clojure – 특정 폴더에서 파일들을 읽어오고 싶을때

상황

특정 폴더에서 파일 들을 읽어오고 싶을 때가 있다 이럴 때는 어떻게 해야할까?

문제 해결

Tip 아래의 방법을 이용하여 샘플 폴더와 샘플 파일들을 만들어보자. (리눅스 또는 맥 환경에서)

$ mkdir -p next-gem
$ touch next-gen/picard.jpg next-gen/locutus.bmp next-gen/data.txt

java.io.File 객체들을 lazy sequence로 받기 위해 file-seq 함수를 사용 할 수 있다.

(def tag-dir (file-seq (clojure.java.io/file "./next-gen")))

tng-dir
;; -> (#<File ./next-gen>
;;     #<File ./next-gen/picard.jpg>
;;     #<File ./next-gen/locutus.bmp>
;;     #<File ./next-gen/data.txt>)

시퀀스는 클로저에서 좀더 파워풀하게 표현하는 추상적인 개념이다. map또는 filter함수를 사용하여 디렉토리 계층에 있는 것들을 재사용할 수 있다.

주의사항으로 만약 디렉토리가 아닐 파일만 필요할 경우 파일 객체의 .isFile 속성을 사용하길 권장한다.

(defn only-files
[files]
(filter #(.isFile %) files))
(only-files tng-dir)
;; -> (#<File ./next-gen/data.txt>
;;     #<File ./next-gen/locutus.bmp>
;;     #<File ./next-gen/picard.jpg>)

만약 파일명만 보여주고 싶다면 파일객체의 .getName을 이용하자.

(defn names
[files]
(map #(.getName %) files))

(-> tng-dir only-files names)
;; -> ("data.txt" "locutus.bmp" "picard.jpg")

이상!

Clojure – appengine-magic

현재 5.1버전까지 나온 appegine-magic은 leiningen 2.0이 필요하다. 그리고 Java는 1.6 또는 그 이후 버전이 필요하고 실제 구동을 시키면 몇 가지 에러가 발생되는데, 이 문제에 대해서는 5.0 사용을 권하고 있다.

몇 가지 테스트 결과 의존 되는 라이브러리 때문에 진행이 안되는 것이며, 이 문제는 간단하게 해결할 수 있었다. 수동으로 의존되는 라이브러리를 추가하면 되는 것이다.

이상으로 끄적끄적이였다.

Clojure > How to list the functions of a namespace.

ref

(keys (ns-publics 'foo))

to list Vars exported by the namespace foo; e.g. for clojure.contrib.monads this returns

(defmonad censor m-when-not m+write+m maybe-m maybe-t ...)

(the … stands for quite a lot more).

More generally, there’s a bunch of functions whose names start in ns- which list Vars by namespace, with certain additional criteria attached:

ns-map — the most general function of all, returns a map keyed by symbols (non-namespace-qualified symbols actually), where the value corresponding to each symbol is the Var or class that symbol resolves to in the given namespace.

ns-interns — like ns-map, but includes only the Vars interned in the given namespace (as opposed to Vars from other namespaces which are accessible from the given namespace due to a use or refer call or the implicit referral of Vars from clojure.core.

ns-publics — like ns-interns, but includes only the non-private Vars.

ns-imports — like ns-map, but includes only the entries whose values correspond to Java classes.

There’s also ns-aliases which lists symbols which can be used as shorthand aliases when referring to Vars from other namespaces; e.g. if you call (require ‘[clojure.contrib.math :as math]), ns-aliases will include an entry with the key of math (the symbol), whose value will be the actual namespace clojure.contrib.math. These mapping are not included in the map returned by ns-map.

or you can use as the following.

(dir clojure.string)

Clojure – 새로 시작하는 마음으로…

클로저 라이브러리에 대해서 알아보자.

클로저 코드는 Library처럼 패키지 되어 있다.

각각의 클로저 라이브러리는 네임스페이스로 되어있으며, Java 패키지와 유사한 구조로 되어있다.

만약 클로저에서 라이브러리를 호출하기 위해서 아래와 같이 사용할 수 있다.

(require quoted-namespace-symbol)

clojure.contrib.str-utils와 같은 패키지를 사용할 때

이는 clojure/contrib/str-utils.clj CLASSPATH에 위치하며 아래와 같이 사용할 수 있다.

(require 'clojure.contrib.str-utils)

Single quote(‘)는 꼭 필요하다.

Single quote는 Reader Macro의 의미를 가지며, 텍스트를 Clojure data structures로 변환하는 역할을 한다.

기본 폼에서 추가적으로 또한 클로저 리더는 리더 매크로를 구성한다.

리더 매크로는 앞 문자 케릭터로 부터 트리거를 발생시킨다?

대부분의 유사한 리더 매크로는 주석이다.

매크로 Character는 주석으로 작동되며 또한 세미콜론을 (;)의미한다.

또한 특수 리더로서 현 라인의 끝까지 모든 명령을 무시한다.

Require을 사용하지말고, Use!를 사용하자.

만약 Require을 사용한다면 네이스페이즈를 지정해주어야 한다.

refer

*refer*를 사용하면 지정된 네이스페이스 안에 있는 모든 아이템을 다 가져온다.

require 또는 use와 함께 강제적으로 모든 라이브러리를 다시 불러들이고 싶을때는

:reload-all 플래그를 사용할 수 있다.

만약 만드는 과정에서 라이브러리가 변경되거나 재시작 없이 정확한 값을 보기를 원한다면

:reload-all 플래그로 유용하게 사용할 수 있다.

 

 

 

Clojure – (read-line) doesn’t wait for input

“lein run”을 이용하여 (read-line) 함수를 사용하는 중에 문제가 발생하였다.

(read-line) 함수는 (. (new java.util.Scanner (. System in)) nextLine)]와 동일하다.

문제의 원인은 leiningen은 새로운 프로세스를 생성하여 사용하므로 사용자 콘솔에 접근을 할 수 없다는 문제이다.

ref : http://stackoverflow.com/questions/7707558/clojure-read-line-doesnt-wait-for-input

Try “lein trampoline run”. See

 Q: I don't have access to stdin inside my project.
 A: There's a problem in the library that 
Leiningen uses to spawn new processes that blocks access to console input. 
This means that functions like read-line will not work as expected in most contexts, 
though the repl task necessarily includes a workaround. 
You can also use the trampoline task to launch 
your project's JVM after Leiningen's has exited rather than launching it as a subprocess.

Clojure – Query (Insert & Delete & Query & update)

테이블명은 ‘users’ 이며 두가지의 컬럼(fname, age)을 가지고 있습니다.

1. 데이터 추가하기

[sourcecode language=”clojure”]
(insert-values
:users
["fname", "age"]
["hojun", "27"])
[/sourcecode]

2. 데이터 수정하기

[sourcecode language=”clojure”]
(update-values
:users
["fname=?"
"hojun"]
{:age "28"})
[/sourcecode]

3. 데이터 검색하기

[sourcecode language=”clojure”]
(with-query-results
results
["select fname from users"]
(doall results))

[/sourcecode]

4. 데이터 삭제하기

[sourcecode language=”clojure”]
(delete-rows
:users
["fname=?ezcocoa"])

[/sourcecode]