Нулевой указатель в clojure при запуске доза с несколькими выражениями в теле - PullRequest
5 голосов
/ 07 октября 2009

Великолепно работает следующее выражение в 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

Простое добавление дополнительных скобок вокруг тела дозы дает мне нулевой указатель. Что я делаю не так?

Ответы [ 2 ]

10 голосов
/ 07 октября 2009

Ну, вы уже разобрались с решением, так что несколько советов, чтобы объяснить поведение:

В Clojure (как в Lisp, Scheme и т. Д.) Все является выражением, а выражение является либо атомом, либо списком. Что касается списков, руководство Clojure гласит:

Непустые списки считаются звонками в специальные формы, макросы или функции. Звонок имеет форму (операнды оператора *).

В вашем примере тело ((println x) (println x)) является списком, а оператор сам является выражением, которое Clojure должен вычислить для получения фактического оператора. То есть вы говорите «вычислите первое выражение и примите его возвращаемое значение как функцию, которая будет вызываться для второго выражения». Однако println возвращает, как вы заметили, только nil. Это приводит к NullPointerException, если 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))

Здесь я динамически вычисляю оператор для вызова элемента в последовательности. Сначала оценивается if -выражение. Если x больше двух, if преобразуется в функцию, которая печатает x + 2, иначе он оценивается в функцию, которая печатает x * 3. Эта функция применяется к x последовательности.

5 голосов
/ 07 октября 2009

Я вижу, вы уже поняли проблему, однако, пожалуйста, обратите внимание, что вам не нужно делать:

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

дозыq (как следует из названия) уже делать:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...