困惑于;让我们;在Clojure

困惑于;让我们;在Clojure,clojure,eval,let,Clojure,Eval,Let,我刚开始玩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))" ]) (doseq [exstr *exprs-to-test*] (do (println "

我刚开始玩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))"
])
(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*
中的第一个项目,这只工作一次,然后由于
NullPointerException
而崩溃。为什么添加的
let
会导致崩溃?

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

((do ...))
它试图执行(作为函数调用)整个
do
表单的值,但是
do
返回
nil
,因为
do
表单中最后一个
println
返回
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)
user>(doseq[exstr*exprs to test*]
(let[ex(读取字符串exstr)]
(println“===”(第一次执行)”===============================================================================================================================================================================”)
(println“代码:”exstr)
(println“Eval:(Eval ex)))
==滤波器=========================
代码:(过滤器#(<%3)’(1234321))
评估:(1 2 1)
==删除=========================
代码:(删除#(<%3)“(1234321))
评估:(3 4 3)
==不同的=========================
代码:(不同的“(1234321))
评估:(1234)

Brian已经回答了你的问题,所以我只想给你一些关于let表单的一般提示:

  • (见“出租”一节)

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

      • 使用全局绑定(要测试的EXPR)
      • 使用doseq/println按顺序尝试代码
      • 使用eval
      学习Clojure API的最好方法是通过REPL。您应该设置好您的环境,无论是Vim、Emacs还是IDE,这样您就可以轻松地在文本文件中的静态代码和交互式REPL之间来回移动

      现在,就您的代码而言,有几件事需要记住。首先,几乎没有一个好的理由使用eval。如果你发现自己在这样做,问问自己这是否真的有必要。其次,请记住,Clojure是一种函数式语言,通常不需要使用“do”宏集。当您需要产生副作用时,“do”宏非常有用(在您的示例中,副作用是println to*out*)。最后,还应避免使用全局变量。如果您确实需要使用VARS,则应该考虑使用绑定宏将VARS本地绑定到线程到不可变的值,因此不存在并发问题。
      我绝对建议您花点时间学习编程Clojure或其他更深入的LISP参考,以真正理解编程方式的必要转变,从而有效地利用Clojure。您在这里的小示例让我感觉好像您正在尝试用Clojure编写基本的代码,这根本无法正常工作

      这就解决了问题。谢谢你的缩进风格提示。我知道在这个10行脚本中全局变量已经失控了,我不再使用“do”宏和eval,即使在实际需要它们的情况下也是如此。我也会接受你关于REPL的建议,因为我对我只使用了10分钟的语言有着完美的记忆,我不需要保存我的工作供以后参考。作为一名经验丰富的Clojure专业人士,你公正的不回答让我感到羞愧。下次我会更加努力。多么令人厌恶的尖刻和讽刺的评论。我是想帮忙。你真丢脸。我认为忽视这个问题,喋喋不休地谈论初学者的代码如何不完美是不合适的。我觉得你是在自吹自擂,而不是想帮忙。我想我理解。你觉得某人基于问题的认知基础发布更高层次的建议是不合适的。但是,如果某人花了一些时间打印出一个合理的回复试图帮助你,那么发布一个讽刺性的回复来侮辱他是非常合适的。你发现人们质疑你的问题是一种侮辱,即使你可能走错了方向。显然,你甚至看不到其他学习Clojure的人在这里游荡的价值。看到你的“不完美”代码,你会认为这是做事情的适当方式。作为记录,我在上周末开始用Clojure编写代码,所以我不是你心目中的那种“比你更神圣”的程序员。我是一个初学者,我试图帮助另一个初学者(在从我最初的错误中吸取教训之后)。很抱歉挫伤了你的自尊心。