在正文中使用多个表达式运行doseq时clojure中的Nullpointer

在正文中使用多个表达式运行doseq时clojure中的Nullpointer,clojure,Clojure,clojure中的以下表达非常有效: (doseq [x '(1 2 3 4)] (println x)) 这个给了我一个空指针: (doseq [x '(1 2 3 4)] ((println x)(println "x"))) 它产生以下输出: user=> (doseq [x '(1 2 3 4)] ((println x)(println "x"))) 1 x java.lang.NullPointerException (NO_SOURCE_FILE:0) user=>

clojure中的以下表达非常有效:

(doseq [x '(1 2 3 4)] (println x))
这个给了我一个空指针:

(doseq [x '(1 2 3 4)] ((println x)(println "x")))
它产生以下输出:

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
只需在doseq的主体周围添加一组额外的括号,就可以得到空指针。
我做错了什么?

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

(doseq [x '(1 2 3 4)] (println x) (println "x"))

doseq(顾名思义)是一个doe ready:)

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

在Clojure中(就像在Lisp、Scheme等中一样),一切都是一个表达式,表达式要么是一个原子,要么是一个列表。关于名单,Clojure手册说

非空列表被视为调用 到特殊窗体、宏或 功能。电话的形式是 (运算符操作数*)

在您的示例中,body
((println x)(println x))
是一个列表,而操作符本身就是一个表达式,Clojure必须对其求值才能获得实际的操作符。也就是说,您说的是“计算第一个表达式并将其返回值作为函数调用第二个表达式”。但是,
println
返回,正如您所注意到的,只返回
nil
。如果将
nil
解释为运算符,则会导致
NullPointerException

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

为了理解这种行为的有用性,请注意函数是Clojure中的一级对象,例如,您可以从另一个函数返回一个函数作为结果。例如,以以下代码为例:

(doseq [x '(1 2 3 4)] ((if (x > 2)
    (fn [x] (println (+ x 2)))
    (fn [x] (println (* x 3)))) x))

在这里,我动态地计算出要在序列中的元素上调用的操作符。首先,计算
if
-表达式。如果
x
大于2,则
If
将求值为打印
x+2
的函数,否则求值为打印
x*3
的函数。这个函数应用于序列的
x

我已经找到了解决这个问题的方法,但我没有真正尝试过。当doseq执行它的主体时,它计算那里的表达式。((println x)(println x))上面的计算结果是一个列表,其头部有println的返回值(nil)。然后,在下一次迭代中,它尝试评估该列表。解决方法是使用“do”。(doseq[x'(1234)](do(println x)(println“x”))