对“让”感到困惑在 Clojure 中

发布于 2024-08-24 16:49:49 字数 1021 浏览 7 评论 0原文

我刚刚开始使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

娇柔作态 2024-08-31 16:49:49

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)

You have an extra set of parentheses around the do form. Your code is doing this:

((do ...))

It's trying to execute (as a function call) the value of the entire do form, but do is returning nil, because the last println in the do form returns nil.

Note, your indentation style is non-standard. You shouldn't put the closing parens on their own lines. And let has an implicit do so you don't need one there. Try this:

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)
溺渁∝ 2024-08-31 16:49:49

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

  • 使用全局绑定(exprs-to-test
  • 使用doseq/println 来尝试代码使用
  • eval

学习 Clojure API 的最佳方法是通过 REPL。您应该设置您的环境,无论是 Vim、Emacs 还是 IDE,以便您可以轻松地在文本文件中的静态代码和交互式 REPL 之间来回切换。 这里有一个很好的细分许多 Clo​​jure 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:

  • Using global bindings (exprs-to-test)
  • Using doseq/println to try out code in sequence
  • Using eval

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.

烟雨凡馨 2024-08-31 16:49:49

Brian 已经回答了您的问题,所以我只想为您提供一些有关 let-form 的一般指示:

Brian already answered your question, so I just want to give you some general pointers for the let-form:

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文