2009-06-10 28 views
24

Tôi đang cố gắng che giấu và có một chút rắc rối khi cố gắng xác định clojure (và/hoặc Lisp) tương đương với thành ngữ python phổ biến này.Tương đương clojure của thành ngữ Python "if __name__ == '__main__'" là gì?

Các thành ngữ là ở dưới cùng của một module python thường có một chút mã kiểm tra, và sau đó một tuyên bố mà chạy mã, ví dụ:

# 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() 

này rất hữu ích cho đơn giản, quảng cáo -hoc thử nghiệm. Người ta thường sử dụng mô-đun này bằng cách viết from mymodule import MyClass, trong trường hợp này _runTests() không bao giờ được gọi, nhưng với đoạn mã ở cuối, người ta cũng có thể chạy nó bằng cách gõ python mymodule.py trực tiếp từ dòng lệnh.

Có thành ngữ tương đương trong Clojure (và/hoặc lisp chung) không? Tôi không phải sau một thư viện kiểm tra đơn vị toàn diện (tốt, tôi, nhưng không phải trong câu hỏi này), tôi chỉ muốn bao gồm một số mã trong một mô-đun sẽ chỉ chạy trong một số trường hợp, vì vậy tôi có thể có một cách nhanh chóng để chạy mã tôi đã làm việc trên nhưng vẫn cho phép tệp của tôi được nhập như một mô-đun/không gian tên bình thường.

Trả lời

27

Nó không phải là thành ngữ để chạy tập lệnh Clojure hơn và hơn từ dòng lệnh. REPL là một dòng lệnh tốt hơn. Clojure là một Lisp, nó phổ biến để kích hoạt Clojure và để lại cùng một cá thể chạy mãi mãi, và tương tác với nó thay vì khởi động lại nó. Bạn có thể thay đổi các hàm trong cá thể đang chạy một lần, chạy chúng và chọc chúng khi cần thiết. Thoát khỏi chu trình chỉnh sửa/biên dịch/gỡ rối truyền thống và chậm chạp là một tính năng tuyệt vời của Lisps.

Bạn có thể dễ dàng viết các hàm để thực hiện những việc như chạy thử nghiệm đơn vị và chỉ cần gọi các hàm đó từ REPL bất cứ khi nào bạn muốn chạy chúng và bỏ qua chúng. Nó phổ biến ở Clojure để sử dụng clojure.contrib.test-is, thêm chức năng thử nghiệm của bạn vào không gian tên của bạn, sau đó sử dụng clojure.contrib.test-is/run-tests để chạy tất cả.

Một lý do chính đáng khác để không chạy Clojure từ dòng lệnh là thời gian khởi động của JVM có thể bị cấm.

Nếu bạn thực sự muốn chạy tập lệnh Clojure từ dòng lệnh, có một số cách bạn có thể thực hiện. Xem the Clojure mailing list để thảo luận.

Một cách là kiểm tra sự hiện diện của đối số dòng lệnh. Đưa ra điều này foo.clj trong thư mục hiện tại:

(ns foo) 

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

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

Bạn sẽ nhận được hành vi khác nhau tùy thuộc vào cách bạn bắt đầu 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=> 

Xem src/clj/clojure/main.clj trong nguồn Clojure nếu bạn muốn xem cách tính năng này hoạt động.

Cách khác là biên dịch mã của bạn thành .class tệp và gọi chúng từ dòng lệnh Java. Cho một tệp nguồn foo.clj:

(ns foo 
    (:gen-class)) 

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

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

Tạo một thư mục để lưu trữ các tệp .class đã biên dịch; giá trị mặc định là ./classes. Bạn phải tự tạo thư mục này, Clojure sẽ không tạo thư mục đó. Ngoài ra, hãy đảm bảo bạn đặt $CLASSPATH để bao gồm ./classes và thư mục có mã nguồn của bạn; Tôi sẽ giả sử foo.clj nằm trong thư mục hiện tại. Vì vậy, từ dòng lệnh:

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

Trong thư mục classes bây giờ bạn sẽ có một loạt các .class tập tin.Để gọi mã của bạn từ dòng lệnh (chạy -main chức năng theo mặc định):

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

Có rất nhiều thông tin về biên dịch mã Clojure trên clojure.org.

1

Tôi rất mới với Clojure nhưng tôi nghĩ rằng this discussion trên nhóm Clojure có thể là giải pháp và/hoặc giải pháp, cụ thể là bài đăng của Stuart Sierra vào ngày 17 tháng 4 lúc 10:40 tối.

0

Bạn có thể muốn xem thư viện test-is từ clojure-contrib. Nó không phải là thành ngữ giống nhau, nhưng nó sẽ hỗ trợ một quy trình làm việc khá giống nhau.

1

Trong Lisp thông thường, bạn có thể sử dụng đọc có điều kiện với features.

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

Chỉ đọc ở trên và do đó thực thi trong khi tải nếu danh sách đối tượng địa lý được gắn với cl:*features* sẽ chứa ký hiệu: kiểm tra.

Ví dụ

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

sẽ tạm thời thêm: kiểm tra vào danh sách các tính năng.

Bạn có thể xác định các tính năng của riêng mình và kiểm soát các biểu thức mà hệ thống Common Lisp đọc và biểu thức nào bỏ qua.

Ngoài ra bạn cũng có thể làm:

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

Tôi không nghĩ rằng * các tính năng * là tốt cho việc này. * Tính năng * hiển thị các tính năng có sẵn, không phải trạng thái môi trường hoặc yêu cầu chạy mã. –

+0

tại sao không? * Các tính năng * được sử dụng cho tất cả các loại nội dung: để mô tả phần cứng đang chạy trên đó, một số lõi có sẵn, một số chế độ của phần mềm, phiên bản triển khai Lisp, phiên bản ngôn ngữ, cho dù đó là: chế độ hoặc: chế độ phát triển, v.v. –

0

Common Lisp và Clojure (cũng như lisps khác) cung cấp môi trường tương tác với REPL, và bạn không cần thủ thuật như «if __name__ == '__main__'». Có các môi trường giống như REPL cho python: python từ dòng lệnh, ipython, chế độ python cho Emacs, v.v.

Bạn chỉ cần tạo thư viện, thêm một testuite vào nó (có nhiều khung kiểm tra cho Common Lisp) ; Tôi thích khung 5am, có một khảo sát về các khung có sẵn here). Và sau đó bạn tải thư viện, và trong REPL bạn có thể làm bất cứ điều gì với thư viện: chạy thử nghiệm, chức năng gọi, thử nghiệm, v.v.

Khi bạn tìm thấy một thử nghiệm không thành công, bạn sửa chữa nó, biên dịch lại thay đổi và tiếp tục thử nghiệm, chạy thử nghiệm mà không cần khởi động lại toàn bộ ứng dụng.Điều này tiết kiệm rất nhiều thời gian, bởi vì ứng dụng đang chạy có thể đã tích lũy rất nhiều trạng thái (nó có thể đã tạo ra các cửa sổ gui, kết nối với cơ sở dữ liệu, đạt đến một số thời điểm quan trọng không dễ tái tạo), và bạn không phải khởi động lại nó sau mỗi thay đổi.

Dưới đây là một ví dụ cho Common Lisp (từ thư viện cl-sqlite của tôi):

Mã:

(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)")))) 
... 

và phiên tương tác:

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> 

Bây giờ giả sử rằng tôi thấy lỗi trong sqlite: thực thi-to-list. Tôi đi đến mã của chức năng này, sửa lỗi và biên dịch lại chức năng này. Sau đó, tôi gọi chức năng cố định và đảm bảo rằng nó hoạt động. Cơ sở dữ liệu trong bộ nhớ không biến mất, nó có cùng trạng thái như trước khi biên dịch lại.

+3

Thành ngữ __name __ == '__ main__' thực sự không liên quan gì đến REPL - một phương thức phân biệt giữa "được nhập dưới dạng mô-đun" và "chạy dưới dạng tập lệnh". Các mã trong nó thường không phải là mã noodling và thử nghiệm bạn muốn thử nghiệm tại REPL, nhưng mã mà bạn muốn thực hiện chính xác cùng một lần. Mã thử nghiệm là một ví dụ, nhưng phổ biến nhất thường là có một tập lệnh cũng cho phép sử dụng lại như một mô-đun. – Brian

+0

Có, nói chung, đó là những thứ khác nhau. Nhưng trong bối cảnh của câu hỏi này, hãy kiểm tra __name__ đã được sử dụng để chạy (và chạy lại) thử nghiệm, và REPL là thành ngữ cho trường hợp sử dụng như vậy trong lisps. –

+0

Người dùng đã yêu cầu tên == thành ngữ chính, KHÔNG phải là repl, KHÔNG phải là bộ thử nghiệm. – mcandre

-3

Nếu bạn đang nói về việc có một "điểm nhập" bạn chắc chắn có thể làm điều đó:

(ns foo) 

(defn foo [n] 
    (inc n)) 

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

(main) 

gì sẽ xảy ra bây giờ là bất cứ lúc nào mã này là (tải tập tin "foo.clj")' d hoặc (sử dụng 'foo) hoặc (yêu cầu' foo), sau đó (chính) sẽ được gọi, điều đó thường không được thực hiện.

Phổ biến hơn là một tệp mã có thể được tải tại REPL và sau đó chức năng chính sẽ được gọi bởi người dùng.

+0

Điều này có thể được thực hiện theo cách mà (chính) chỉ được kích hoạt khi foo.clj được chạy trực tiếp chứ không phải khi một tập lệnh khác tải nó? – mcandre

+0

Tôi không nghĩ vậy vì trong cả hai trường hợp, bạn sẽ đánh giá (và sau đó biên dịch) tất cả các biểu thức. Luôn luôn có AOT biên dịch cho phép định nghĩa của một entry-point: http://clojure.org/compilation – Chris

0

Boot là công cụ xây dựng (thay thế cho leiningen), supports scripts. Vì vậy, bạn có thể có một tập lệnh khởi động bắt đầu với #!/usr/bin/env boot có thể có phương thức -main.

Bạn cũng có thể thực hiện các tác vụ được gọi từ dòng lệnh sẽ gọi các chức năng khác nhau của mã của bạn. Và bạn có thể có một nhiệm vụ đóng gói có thể tạo ra một uberjar cho một trong các chức năng này như là các điểm vào.

Các vấn đề liên quan