代码之家  ›  专栏  ›  技术社区  ›  Tom Dalling

被clojure中的“let”所迷惑

  •  5
  • Tom Dalling  · 技术社区  · 16 年前

    我刚开始玩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))
            )
        ))
    )
    

    但这对第一个项目有效 *要测试的表达式* ,然后与 NullPointerException . 为什么 导致坠机?

    3 回复  |  直到 16 年前
        1
  •  7
  •   Brian Carper    16 年前

    do 形式。您的代码正在执行以下操作:

    ((do ...))
    

    它试图执行(作为函数调用)整个 形式,但 正在回归 nil ,因为最后一个 println 表格返回 .

    注意,缩进样式是非标准的。你不应该把结束语放在他们自己的行上。和 let 有一个隐含的 所以你不需要一个。试试这个:

    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)
    
        2
  •  4
  •   Greg Fodor    16 年前

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

    • 使用全局绑定( 要测试的表达式 )
    • 使用doseq/println按顺序尝试代码
    • 使用EVE

    学习clojure的api的最好方法是通过repl。您应该设置您的环境,无论是vim、emacs还是ide,这样您就可以轻松地在文本文件中的静态代码和交互式repl之间来回移动。 Here is a good breakdown of a number of Clojure IDEs

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

    我绝对建议您花点时间学习编程clojure,或者再深入了解一下lisp,以便真正理解在您考虑如何编程以有效利用clojure时所必需的转变。你在这里的小示例让我觉得你好像在用clojure编写命令代码,这根本不起作用。

        3
  •  1
  •   Michael Kohl    16 年前

    布赖恩已经回答了你的问题,所以我只想给你一些关于let表单的通用指针:

    推荐文章