当运行剂量在体内有多个表达式时,在clojure中的Nullpointer

Han*_*son 5 clojure

clojure中的以下表达式非常有用:

(doseq [x '(1 2 3 4)] (println x))
Run Code Online (Sandbox Code Playgroud)

这个给了我一个nullpointer:

(doseq [x '(1 2 3 4)] ((println x)(println "x")))
Run Code Online (Sandbox Code Playgroud)

它产生以下输出:

user=> (doseq [x '(1 2 3 4)] ((println x)(println "x")))
1
x
java.lang.NullPointerException (NO_SOURCE_FILE:0)
user=> (.printStackTrace *e)
java.lang.NullPointerException (NO_SOURCE_FILE:0)
  at clojure.lang.Compiler.eval(Compiler.java:4639)
  at clojure.core$eval__5182.invoke(core.clj:1966)
  at clojure.main$repl__7283$read_eval_print__7295.invoke(main.clj:180)
  at clojure.main$repl__7283.doInvoke(main.clj:197)
  at clojure.lang.RestFn.invoke(RestFn.java:426)
  at clojure.main$repl_opt__7329.invoke(main.clj:251)
  at clojure.main$legacy_repl__7354.invoke(main.clj:292)
  at clojure.lang.Var.invoke(Var.java:359)
  at clojure.main.legacy_repl(main.java:27)
  at clojure.lang.Repl.main(Repl.java:20)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
  at java.lang.reflect.Method.invoke(Method.java:597)
  at jline.ConsoleRunner.main(ConsoleRunner.java:69)
Caused by: java.lang.NullPointerException
  at user$eval__266.invoke(NO_SOURCE_FILE:26)
  at clojure.lang.Compiler.eval(Compiler.java:4623)
  ... 14 more
nil
Run Code Online (Sandbox Code Playgroud)

只需在doseq的主体周围添加一组额外的括号就可以得到nullpointer.我究竟做错了什么?

jan*_*nko 10

好吧,你已经找到了解决方案,所以只需要一些提示来解释这个行为:

在Clojure中(就像在Lisp,Scheme等中),一切都是表达式,表达式是原子或列表.关于列表,Clojure手册说

非空列表被视为对特殊表单,宏或函数的调用.调用具有表单(运算符操作数*).

在您的示例中,正文((println x) (println x))是一个列表,运算符本身是Clojure必须评估以获取实际运算符的表达式.也就是说,您正在说"评估第一个表达式并将其返回值作为调用第二个表达式的函数".但是,println正如您所注意到的那样,仅返回nil.这导致NullPointerExceptionif nil被解释为运算符.

您的代码可以使用,(do (println x) (println x))因为它do是一种特殊形式,它依次计算每个表达式并返回最后一个表达式的值.这do是运算符和带有println操作数的表达式.

要理解这种行为的有用性,请注意函数是Clojure中的第一类对象,例如,您可以从另一个函数返回一个函数.例如,请使用以下代码:

(doseq [x '(1 2 3 4)] ((if (x > 2)
    (fn [x] (println (+ x 2)))
    (fn [x] (println (* x 3)))) x))
Run Code Online (Sandbox Code Playgroud)

在这里,我动态地找出运算符来调用序列中的元素.首先,if评估-expression.如果x大于2,则if对打印x + 2的函数进行评估,否则评估为打印的函数x * 3.此功能适用于x序列.


Tim*_*ley 5

我知道你已经意识到了这个问题,但请注意你不需要做:

(doseq [x '(1 2 3 4)] (println x) (println "x"))
Run Code Online (Sandbox Code Playgroud)

doseq是(顾名思义)a已经:)