¿Cuál es el equivalente de clojure del idioma de Python “si __name__ == ‘__main__’”?

Estoy incursionando en clojure y estoy teniendo un pequeño problema al tratar de determinar el equivalente de clojure (y / o Lisp) de este lenguaje común de python.

El idioma es que en la parte inferior de un módulo de python a menudo hay un poco de código de prueba, y luego una statement que ejecuta el código, por ejemplo:

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

Esto es útil para pruebas simples, ad-hoc. Normalmente, se usaría este módulo escribiendo from mymodule import MyClass , en cuyo caso nunca se llama a _runTests() , pero con el fragmento al final, también se puede ejecutar escribiendo python mymodule.py directamente desde la línea de comandos.

¿Hay un idioma equivalente en Clojure (y / o tinte común)? No busco una biblioteca de pruebas de unidad completa (bueno, lo estoy, pero no en esta pregunta), me gustaría incluir algún código en un módulo que solo se ejecutará en algunas circunstancias, por lo que puedo tener una forma rápida de ejecutar el código en el que he estado trabajando, pero aún así puedo importar mi archivo como un módulo / espacio de nombres normal.

    No es idiomático ejecutar los scripts de Clojure una y otra vez desde la línea de comandos. El REPL es una mejor línea de comando. Como Clojure es un Lisp, es común iniciar Clojure y dejar la misma instancia ejecutándose para siempre, e interactuar con ella en lugar de reiniciarla. Puede cambiar las funciones en la instancia en ejecución de una en una, ejecutarlas y presionarlas según sea necesario. Escapar del tedioso y lento ciclo tradicional de edición / comstackción / depuración es una gran característica de Lisps.

    Puede escribir fácilmente funciones para hacer cosas como ejecutar pruebas unitarias, y simplemente llamar a esas funciones desde el REPL cuando quiera ejecutarlas e ignorarlas de otra manera. Es común en Clojure usar clojure.contrib.test-is , agregar sus funciones de prueba a su espacio de nombres, luego usar clojure.contrib.test-is/run-tests para ejecutarlas todas.

    Otra buena razón para no ejecutar Clojure desde la línea de comandos es que el tiempo de inicio de la JVM puede ser prohibitivo.

    Si realmente desea ejecutar un script de Clojure desde la línea de comandos, hay varias formas de hacerlo. Consulte la lista de correo de Clojure para más información.

    Una forma es probar la presencia de argumentos de línea de comando. Dado este foo.clj en el directorio actual:

     (ns foo) (defn hello [x] (println "Hello," x)) (if *command-line-args* (hello "command line") (hello "REPL")) 

    Tendrás un comportamiento diferente dependiendo de cómo inicies 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=> 

    Consulte src/clj/clojure/main.clj en la fuente de Clojure si desea ver cómo funciona esto.

    Otra forma es comstackr su código en archivos .class e invocarlos desde la línea de comandos de Java. Dado un archivo fuente foo.clj :

     (ns foo (:gen-class)) (defn hello [x] (println "Hello," x)) (defn -main [] (hello "command line")) 

    Haga un directorio para almacenar los archivos .class comstackdos; este por defecto es ./classes . Debe crear esta carpeta usted mismo, Clojure no la creará. También asegúrese de configurar $CLASSPATH para incluir ./classes y el directorio con su código fuente; foo.clj que foo.clj está en el directorio actual. Así que desde la línea de comando:

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

    En el directorio de classes ahora tendrás un montón de archivos .class . Para invocar su código desde la línea de comandos (ejecutando la función -main de forma predeterminada):

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

    Hay mucha información sobre cómo comstackr el código de Clojure en clojure.org .

    Soy muy nuevo en Clojure, pero creo que esta discusión sobre los grupos de Clojure puede ser una solución y / o una solución alternativa, específicamente el post de Stuart Sierra el 17 de abril a las 10:40 PM.

    En Common Lisp puedes usar la lectura condicional con características .

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

    Lo anterior solo se leerá y, por lo tanto, se ejecutará durante la carga si la lista de características vinculadas a cl: * features * contendrá el símbolo: testing.

    Por ejemplo

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

    agregará temporalmente: pruebas a la lista de características.

    Puede definir sus propias características y controlar qué expresiones lee el sistema Common Lisp y cuáles omite.

    Además también puedes hacer:

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

    También hay una lista de diferentes posibilidades en http://rosettacode.org/wiki/Scripted_Main#Clojure . (Si encuentra uno nuevo, por favor agréguelo ;-))

    Es posible que desee echar un vistazo a la biblioteca test-is de clojure-contrib. No es el mismo idioma, pero debería admitir un flujo de trabajo bastante similar.

    Common Lisp y Clojure (así como otros lisps) proporcionan un entorno interactivo con REPL, y no necesitas trucos como « if __name__ == '__main__' ». Existen entornos similares a REPL para python: python desde la línea de comandos, ipython, modo python para Emacs, etc.

    Solo debe crear la biblioteca, agregarle un entorno de prueba (hay muchos marcos de prueba para Common Lisp; prefiero el marco de las 5 am , hay una encuesta de marcos disponible aquí ). Y luego carga la biblioteca, y en el REPL puede hacer cualquier cosa con la biblioteca: ejecutar pruebas, llamar a funciones, experimentar, etc.

    Cuando encuentre una prueba fallida, realice una corrección, vuelva a comstackr el código modificado y continúe experimentando, ejecutando pruebas sin reiniciar toda la aplicación. Esto ahorra mucho tiempo, ya que la aplicación en ejecución podría haber acumulado mucho estado (podría haber creado ventanas de interfaz gráfica de usuario, conectarse a bases de datos, haber alcanzado un momento crítico que no es fácilmente reproducible), y no tiene que reiniciarlo después de cada cambio.

    Aquí hay un ejemplo para Common Lisp (de mi biblioteca cl-sqlite):

    El código:

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

    y la sesión interactiva:

     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> 

    Ahora supongamos que encontré un error en sqlite: execute-to-list. Voy al código de esta función, soluciono el error y recompilo esta función. Entonces llamo a la función fija y me aseguro de que funcione. La base de datos en memoria no se ha ido, tiene el mismo estado que tenía antes de volver a comstackr.

    Boot es una herramienta de comstackción (una alternativa a leiningen), que admite scripts . Por lo tanto, podría tener un script de arranque que comience con #!/usr/bin/env boot que puede tener un método -main .

    También puede realizar tareas invocadas desde la línea de comandos que llamen a diferentes funciones de su código. Y podría tener una tarea de empaquetado que puede crear un uberjar para una de estas funciones como puntos de entrada.

    Si estás hablando de tener un “punto de entrada”, ciertamente puedes hacer eso:

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

    lo que sucederá ahora es que cada vez que este código es (archivo de carga “foo.clj”) ‘d o (use’ foo) o (require ‘foo), entonces (main) se llamará, generalmente no se hace.

    Mucho más común es que un archivo de código se puede cargar en el REPL y luego el usuario llamará a la función principal.