5

我刚开始玩 Clojure,写了一个小脚本来帮助我理解其中的一些功能。它是这样开始的:

(def *exprs-to-test* [  
    "(filter #(< % 3) '(1 2 3 4 3 2 1))"
    "(remove #(< % 3) '(1 2 3 4 3 2 1))"
    "(distinct '(1 2 3 4 3 2 1))"
])

然后它通过*exprs-to-test*,评估它们,并像这样打印输出:

(doseq [exstr *exprs-to-test*]
    (do 
        (println "===" (first (read-string exstr)) "=========================")
        (println "Code: " exstr)
        (println "Eval: " (eval (read-string exstr)))
    )
)

上面的代码一切正常。但是,(read-string exstr)是重复的,所以我尝试使用let来消除重复,如下所示:

(doseq [exstr *exprs-to-test*]
    (let [ex (read-string exstr)] (
        (do 
            (println "===" (first ex) "=========================")
            (println "Code: " exstr)
            (println "Eval: " (eval ex))
        )
    ))
)

但这适用于 中的第一项*exprs-to-test*,然后与NullPointerException. 为什么添加会let导致崩溃?

4

3 回答 3

7

您在表单周围有一组额外的括号do。您的代码正在执行此操作:

((do ...))

它试图执行(作为函数调用)整个do表单的值,但do正在返回nil,因为表单println中的最后一个do返回nil

请注意,您的缩进样式是非标准的。您不应该将结束括号放在自己的行上。并且let有一个隐含的do,所以你不需要一个。试试这个:

user> (doseq [exstr *exprs-to-test*]
        (let [ex (read-string exstr)] 
          (println "===" (first ex) "=========================")
          (println "Code: " exstr)
          (println "Eval: " (eval ex))))
=== filter =========================
Code:  (filter #(< % 3) '(1 2 3 4 3 2 1))
Eval:  (1 2 2 1)
=== remove =========================
Code:  (remove #(< % 3) '(1 2 3 4 3 2 1))
Eval:  (3 4 3)
=== distinct =========================
Code:  (distinct '(1 2 3 4 3 2 1))
Eval:  (1 2 3 4)
于 2010-03-07T03:49:12.180 回答
4

我认为其他答案忽略了房间里的大象:你为什么要这样做?你的代码中有很多东西让我担心你通过学习 Clojure 走错了路:

  • 使用全局绑定 ( exprs-to-test )
  • 使用doseq/println依次尝试代码
  • 使用评估

学习 Clojure API 的最佳方式是通过 REPL。您应该设置好您的环境,无论是 Vim、Emacs 还是 IDE,以便您可以轻松地在文本文件中的静态代码和交互式 REPL 之间来回切换。以下是一些 Clojure IDE 的详细分类

现在,就您的代码而言,有几件事要记住。首先,几乎没有使用 eval 的充分理由。如果你发现自己这样做了,问问自己是否真的有必要。其次,请记住,Clojure 是一种函数式语言,通常您不需要使用“do”宏集。当您需要产生副作用时,“do”宏很有用(在您的示例中,副作用是 println 到 *out*) 最后,还应避免使用全局变量。如果确实需要使用 var,则应考虑使用 bindings 宏将本地到线程的 var 绑定到不可变值,这样就不会出现并发问题。

I definitely recommend you take the time to pick up Programming Clojure or another deeper reference to LISP to truly understand the shift necessary in the way you think about programming to leverage Clojure effectively. Your small sample here makes me feel as though you are trying to write imperitive code in Clojure, which is not going to work well at all.

于 2010-03-17T04:42:37.927 回答
1

布赖恩已经回答了你的问题,所以我只想给你一些关于 let-form 的一般指示:

于 2010-03-08T08:55:09.790 回答