2009-06-10 3 views
24

나는 clojure를 물들이며,이 공통적 인 파이썬 관용구에 해당하는 clojure (및/또는 Lisp)를 결정하는 데 약간의 문제가 있습니다."if __name__ == '__main__'"과 같은 Python 관용구에 해당하는 clojure는 무엇입니까?

# mymodule.py 
class MyClass(object): 
    """Main logic/code for the library lives here""" 
    pass 

def _runTests(): 
    # Code which tests various aspects of MyClass... 
    mc = MyClass() # etc... 
    assert 2 + 2 == 4 

if __name__ == '__main__': _runTests() 

이 간단하고 광고하는 데 유용합니다 :

관용구는 파이썬 모듈의 하단이 테스트 코드의 비트가 종종 다음 예를 들어, 코드를 실행하는 문이다 -hoc 테스트. 일반적으로 from mymodule import MyClass을 쓰면이 모듈을 사용할 수 있습니다.이 경우 _runTests()은 호출되지 않지만 끝에 코드 조각을 사용하면 명령 줄에서 python mymodule.py을 직접 입력하여 실행할 수 있습니다.

Clojure (및/또는 공통 리스프)에 동일한 관용구가 있습니까? 나는 본격적인 유닛 테스트 라이브러리 (글쎄, 나는이 질문에 있지 않다)가 아니며, 일부 상황에서만 실행될 모듈에 일부 코드를 포함하기를 원한다. 내가 작업 해 왔던 코드를 실행하는 빠른 방법이긴하지만 내 파일을 일반 모듈/네임 스페이스처럼 가져올 수 있습니다.

답변

27

Clojure 스크립트를 명령 줄에서 계속 실행하는 것은 관용적이지 않습니다. REPL은 더 나은 명령 행입니다. Clojure는 Lisp이기 때문에 Clojure를 실행하고 동일한 인스턴스를 영원히 계속 실행하고 다시 시작하지 않고 상호 작용하는 것이 일반적입니다. 실행중인 인스턴스의 기능을 한 번에 하나씩 변경하고 실행하고 필요에 따라 펑크 할 수 있습니다. 지루하고 느린 전통적인 편집/컴파일/디버그주기를 피하는 것이 Lisps의 큰 특징입니다.

단위 테스트 실행과 같은 기능을 쉽게 작성할 수 있으며 실행하고 싶을 때마다 REPL에서 해당 함수를 호출 할 수 있습니다. Clojure에서는 clojure.contrib.test-is을 사용하고 테스트 함수를 네임 스페이스에 추가 한 다음 clojure.contrib.test-is/run-tests을 사용하여 모두 실행합니다.

Clojure를 명령 줄에서 실행하지 않는 또 다른 이유는 JVM의 시작 시간이 길어질 수 있다는 것입니다.

명령 줄에서 Clojure 스크립트를 실제로 실행하려면 여러 가지 방법이 있습니다. 자세한 내용은 the Clojure mailing list을 참조하십시오.

한 가지 방법은 명령 줄 인수가 있는지 테스트하는 것입니다. 현재 디렉토리에서이 foo.clj을 감안할 때 :

(ns foo) 

(defn hello [x] (println "Hello," x)) 

(if *command-line-args* 
    (hello "command line") 
    (hello "REPL")) 

당신은 Clojure를 시작하는 방법을 따라 다른 동작을 얻을 수 있습니다. 당신이 작동하는 방법을보고 싶다면

$ java -cp ~/path/to/clojure.jar:. clojure.main foo.clj -- 
Hello, command line 
$ java -cp ~/path/to/clojure.jar:. clojure.main 
Clojure 1.1.0-alpha-SNAPSHOT 
user=> (use 'foo) 
Hello, REPL 
nil 
user=> 

는 Clojure의 소스에 src/clj/clojure/main.clj를 참조하십시오.

또 다른 방법은 코드를 .class 개의 파일로 컴파일하고 Java 명령 줄에서 호출하는 것입니다. 소스 파일 foo.clj 감안할 때 :

(ns foo 
    (:gen-class)) 

(defn hello [x] (println "Hello," x)) 

(defn -main [] (hello "command line")) 

컴파일 된 .class 파일을 저장할 디렉토리를 확인; 기본값은 ./classes입니다. 이 폴더는 직접 만들어야하며 Clojure는이 폴더를 만들지 않습니다. 또한 $CLASSPATH./classes과 소스 코드가 포함 된 디렉토리를 포함하도록 설정해야합니다. foo.clj이 현재 디렉토리에 있다고 가정합니다. 명령 줄에서 그래서 다음 classes 디렉토리에서

$ mkdir classes 
$ java -cp ~/path/to/clojure.jar:./classes:. clojure.main 
Clojure 1.1.0-alpha-SNAPSHOT 
user=> (compile 'foo) 
foo 

지금 .class 파일들을 것입니다.

$ java -cp ~/path/to/clojure.jar:./classes foo 
Hello, command line. 

clojure.org에 Clojure의 코드를 컴파일에 대한 많은 정보가있다 : (기본적으로 -main 기능을 실행) 명령 줄에서 코드를 호출합니다. 당신은 "진입 점"을 가지고 얘기하는 경우

1

저는 Clojure에 새로 입문했지만 Clojure 그룹의 this discussion은 4 월 17 일 오후 10:40에 Stuart Sierra의 솔루션 및/또는 해결 방법, 특히 Stuart Sierra의 게시물 일 수 있다고 생각합니다.

0

clojure-contrib의 test-is 라이브러리를보고 싶을 수도 있습니다. 그것은 같은 관용구가 아니지만 꽤 비슷한 워크 플로우를 지원해야합니다.

1

Common Lisp에서는 features으로 조건부 읽기를 사용할 수 있습니다.

#+testing (run-test 'is-answer-equal-42) 

위는 읽기 전용 및 cl:*features*에 바인딩 기능 목록이 기호를 포함 할 경우에 따라서로드하는 동안 실행됩니다 : 테스트를.

(let ((*features* (cons :testing *features*))) 
    (load "/foo/bar/my-answerlib.lisp")) 

예를

를 들어

일시적으로 추가합니다 : 기능 목록에 테스트.

사용자 고유의 기능을 정의하고 Common Lisp 시스템이 읽는 식과 건너 뛸 식을 제어 할 수 있습니다.

은 또한 당신은 또한 수행 할 수 있습니다

#-testing (print '|we are in production mode|) 
+0

필자는 * 기능 *이 좋지 않다고 생각합니다. * 기능 * 일부 환경 상태 또는 코드 실행 요청이 아닌 사용 가능한 기능을 표시합니다. –

+0

왜 안 되니? * 기능 * 모든 종류의 물건에 사용 : 하드웨어가 실행되고있는 하드웨어, 사용 가능한 일부 핵심 라이브러리, 소프트웨어의 일부 모드, Lisp 구현 버전, 언어 버전 여부 : 제작 여부 - 모드 또는 : 개발 모드 등 –

0

커먼 리스프와 Clojure의 (뿐만 아니라 다른 lisps는) REPL 대화 형 환경을 제공하고, 당신은«if __name__ == '__main__'»같은 트릭이 필요하지 않습니다. REPL과 비슷한 Python 환경 : 명령 행의 파이썬, ipython, Emacs 용 파이썬 모드 등

라이브러리를 작성하고 테스트 스위트를 추가해야한다 (Common Lisp에 대한 많은 테스트 프레임 워크가있다. 나는 5am 프레임 워크를 선호하는데, 프레임 워크에 대한 설문 조사는 here입니다. 그런 다음 라이브러리를로드하고 REPL에서 라이브러리와 관련된 모든 작업을 수행 할 수 있습니다. 테스트 실행, 함수 호출, 실험 등

실패한 테스트를 찾으면 수정 된 내용을 다시 컴파일하고 코드를 작성하고 실험을 계속하고 전체 응용 프로그램을 다시 시작하지 않고 테스트를 실행하십시오.실행중인 응용 프로그램이 많은 상태를 축적했기 때문에 시간을 많이 절약 할 수 있습니다 (GUI 창을 만들었거나 데이터베이스에 연결되어 있고 쉽게 복제 할 수없는 중요한 순간에 도달했을 수 있으므로 다시 시작할 필요가 없습니다). 모든 변화 후에. 여기

는 (내 CL-sqlite가 라이브러리에서) 커먼 리스프에 대한 예입니다 :

코드 :

(def-suite sqlite-suite) 

(defun run-all-tests() 
    (run! 'sqlite-suite));' 

(in-suite sqlite-suite) 

(test test-connect 
    (with-open-database (db ":memory:"))) 

(test test-disconnect-with-statements 
    (finishes 
    (with-open-database (db ":memory:") 
     (prepare-statement db "create table users (id integer primary key, user_name text not null, age integer null)")))) 
... 

및 대화 형 세션 :

이제
CL-USER> (sqlite-tests:run-all-tests) 
....... 
Did 7 checks. 
    Pass: 7 (100%) 
    Skip: 0 (0%) 
    Fail: 0 (0%) 

NIL 
CL-USER> (defvar *db* (sqlite:connect ":memory:")) 
*DB* 
CL-USER> (sqlite:execute-non-query *db* "create table t1 (field text not null)") 
; No value 
CL-USER> (sqlite:execute-non-query *db* "insert into t1 (field) values (?)" "hello") 
; No value 
CL-USER> (sqlite:execute-to-list *db* "select * from t1") 
(("hello")) 
CL-USER> 

내가 발견 한 가정 sqlite의 버그 : execute-to-list. 이 함수의 코드로 이동하여 버그를 수정하고이 함수를 다시 컴파일합니다. 그런 다음 고정 된 함수를 호출하여 제대로 작동하는지 확인합니다. 메모리 내 데이터베이스가 사라지지 않았습니다. 다시 컴파일하기 전의 상태와 같습니다.

+3

__name __ == '__ main__'관용구는 REPL과는 아무런 관련이 없습니다. "모듈로 가져 오기"와 "스크립트로 실행"을 구별하는 방법입니다. 일반적으로 코드는 REPL에서 실험 할 수있는 noodling 및 trial 코드가 아니라 동일한 코드를 반복해서 실행하고자하는 코드입니다. 테스트 코드가 하나의 예이지만 일반적으로 모듈로 재사용 할 수있는 스크립트가 일반적입니다. – Brian

+0

예, 일반적으로 다른 것들입니다. 그러나이 질문의 맥락에서 __name__에 대한 검사는 테스트를 실행 (및 다시 실행)하는 데 사용되었으며, REPL은 lisps에서 이러한 사용 사례에 대해 관용적입니다. –

+0

사용자가 이름 == main idiom을 요청했지만 repl이 아니라 테스트 스위트가 필요합니다. – mcandre

-3

당신은 확실히 그렇게 할 수 있습니다

(ns foo) 

(defn foo [n] 
    (inc n)) 

(defn main [] 
    (println "working") 
    (println "Foo has ran:" (foo 1))) 

(main) 

무엇 지금 일어날 것은 언제든지이 코드는 점이다 (로드 파일 "foo.clj") ' d 또는 ('foo'를 사용하거나 'foo'가 필요하다면) (main)이 호출됩니다.

훨씬 더 일반적으로 코드 파일을 REPL에로드 한 다음 주 함수를 호출 할 수 있습니다.

+0

foo.clj가 직접 실행될 때 (main)가 트리거되고 다른 스크립트가 스크립트를로드 할 때만 트리거되지 않도록 할 수 있습니까? – mcandre

+0

두 경우 모두 모든 식을 평가 (및 컴파일) 할 것이기 때문에 나는 그렇게 생각하지 않습니다. 엔트리 포인트의 정의를 허용하는 AOT 컴파일이 항상 있습니다 : http://clojure.org/compilation – Chris

1

다른 가능성 목록도 에 있습니다. (새 것을 찾으면 그것을 추가하십시오. ;-))

0

은 빌드 도구입니다 (leiningen의 대안), 그 supports scripts입니다. 따라서 #!/usr/bin/env boot으로 시작하는 부팅 스크립트는 -main 방법을 가질 수 있습니다.

코드의 다른 기능을 호출하는 명령 줄에서 작업을 호출 할 수도 있습니다. 그리고 이러한 기능 중 하나에 대한 uberjar를 엔트리 포인트로 만들 수있는 패키징 작업을 할 수 있습니다.