对“让”感到困惑在 Clojure 中
我刚刚开始使用 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
会导致崩溃?
I just started playing with Clojure, and I wrote a small script to help me understand some of the functions. It begins like this:
(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))"
])
Then it goes through *exprs-to-test*
, evaluates them all, and prints the output like this:
(doseq [exstr *exprs-to-test*]
(do
(println "===" (first (read-string exstr)) "=========================")
(println "Code: " exstr)
(println "Eval: " (eval (read-string exstr)))
)
)
The above code is all working fine. However, (read-string exstr)
is repeated so I tried to use let
to eliminate the repetition like so:
(doseq [exstr *exprs-to-test*]
(let [ex (read-string exstr)] (
(do
(println "===" (first ex) "=========================")
(println "Code: " exstr)
(println "Eval: " (eval ex))
)
))
)
But this works once for the first item in *exprs-to-test*
, then crashes with a NullPointerException
. Why is the addition of let
causing the crash?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
do
形式周围有一组额外的括号。您的代码正在执行此操作:它尝试执行(作为函数调用)整个
do
表单的值,但do
返回nil
,因为do
形式中的最后一个println
返回nil
。请注意,您的缩进样式是非标准的。您不应该将右括号放在自己的行上。而且
let
有一个隐式的do
,所以你不需要在那里。试试这个:You have an extra set of parentheses around the
do
form. Your code is doing this:It's trying to execute (as a function call) the value of the entire
do
form, butdo
is returningnil
, because the lastprintln
in thedo
form returnsnil
.Note, your indentation style is non-standard. You shouldn't put the closing parens on their own lines. And
let
has an implicitdo
so you don't need one there. Try this:我认为其他答案忽略了房间里的大象:你为什么要这样做?你的代码中有很多东西让我担心你在学习 Clojure 的过程中走上了错误的道路:
学习 Clojure API 的最佳方法是通过 REPL。您应该设置您的环境,无论是 Vim、Emacs 还是 IDE,以便您可以轻松地在文本文件中的静态代码和交互式 REPL 之间来回切换。 这里有一个很好的细分许多 Clojure IDE。
现在,就您的代码而言,需要记住一些事情。首先,几乎没有充分的理由使用 eval。如果您发现自己这样做,请问问自己是否真的有必要。其次,请记住,Clojure 是一种函数式语言,通常您不需要使用“do”宏集。当您需要产生副作用时,“do”宏非常有用(在您的示例中,副作用是 println 到 *out*)最后,也应该避免使用全局变量。如果确实需要使用变量,则应考虑使用绑定宏将变量本地绑定到线程到不可变值,这样就不会出现并发问题。
我绝对建议您花时间学习 Clojure 编程或 LISP 的其他更深入的参考资料,以真正理解您在思考编程方式时所必需的转变,以有效地利用 Clojure。你这里的小样本让我感觉好像你正在尝试在 Clojure 中编写命令式代码,这根本不会很好地工作。
I think the other answers are ignoring the elephant in the room: why are you doing this? There are a lot of things in your code that make me worry you are forging on the wrong path through learning Clojure:
The best way to learn the APIs of Clojure is through the REPL. You should get your environment set up, be it Vim, Emacs, or an IDE such that you can easily move back and forth between static code in text files and an interactive REPL. Here is a good breakdown of a number of Clojure IDEs.
Now, as far as your code goes, a few things to remember. First, there's almost never a good reason to use eval. If you find yourself doing this, ask yourself if its really necessary. Second, remember, Clojure is a functional language and generally you should not need to use the "do" set of macros. The "do" macros are useful when you need to have side effects (in your example, the side effect is the println to *out*) Finally, using global vars should be avoided as well. If you do need to use vars, you should consider using the bindings macro to bind the vars locally to the thread to immutable values so there are no concurrency issues.
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.
Brian 已经回答了您的问题,所以我只想为您提供一些有关 let-form 的一般指示:
Brian already answered your question, so I just want to give you some general pointers for the let-form: