Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何从Clojure错误中获得更好的反馈?_Clojure_Runtime Error - Fatal编程技术网

如何从Clojure错误中获得更好的反馈?

如何从Clojure错误中获得更好的反馈?,clojure,runtime-error,Clojure,Runtime Error,与我使用过的所有其他编程语言相比,我发现调试代码中的Clojure错误非常困难。我的主要编程语言是Java,我对Clojure非常陌生。我写Clojure的大部分时间都花在试图弄清楚“为什么我会犯这个错误?”我想改变这一点。我使用逆时针方向作为主要IDE。我还不知道如何使用Emacs 下面是一个例子: (ns cljsandbox.core) (def l [1 2 3 1]) (defn foo [l] (->> l (group-by identity)

与我使用过的所有其他编程语言相比,我发现调试代码中的Clojure错误非常困难。我的主要编程语言是Java,我对Clojure非常陌生。我写Clojure的大部分时间都花在试图弄清楚“为什么我会犯这个错误?”我想改变这一点。我使用逆时针方向作为主要IDE。我还不知道如何使用Emacs

下面是一个例子:

(ns cljsandbox.core)

(def l [1 2 3 1])

(defn foo
  [l]
  (->> l
    (group-by identity)
    ;vals  ;commented out to show my intent
    (map #(reduce + %))))
在这里,我错误地认为,
groupby
返回一个列表列表,但它实际上返回了一个
的映射,或者用Java术语表达它。这将显示一条错误消息,其中显示:

ClassCastException clojure.lang.PersistentVector不能强制转换为java.lang.Number clojure.lang.Numbers.add(Numbers.java:126)

这不是很有帮助,因为没有堆栈跟踪。如果我输入
(e)
,它会显示:

java.lang.ClassCastException: clojure.lang.PersistentVector cannot be cast to java.lang.Number
 at clojure.lang.Numbers.add (Numbers.java:126)
    clojure.core$_PLUS_.invoke (core.clj:944)
    clojure.core.protocols/fn (protocols.clj:69)
    clojure.core.protocols$fn__5979$G__5974__5992.invoke (protocols.clj:13)
    clojure.core$reduce.invoke (core.clj:6175)
    cljsandbox.core$foo$fn__1599.invoke (core.clj:10)
    clojure.core$map$fn__4207.invoke (core.clj:2487)
    clojure.lang.LazySeq.sval (LazySeq.java:42)
我不知道如何才能从这个错误消息理解“您以为您正在将列表列表传递到
map
中,但实际上您正在传递一个map数据类型”。堆栈跟踪显示问题是在
reduce
内部报告的,而不是在
groupby
内部报告的,但在我看来,这不是我作为一个人犯的错误。这正是程序发现错误的地方

像这样的问题可能需要我15分钟以上才能解决。我怎样才能使这花费更少的时间


我知道期望一种动态语言捕捉这些错误是太过分了。但是,我觉得其他动态语言(如javascript)的错误消息更有帮助

我在这里感到非常绝望,因为我已经在clojure中编写代码1-2个月了,我觉得我应该更好地处理这些问题。我尝试在函数上使用
:pre
/
:post
,但存在一些问题

  • 关于
    :pre
    /
    :post
    的报道有点糟糕。它只打印出你测试的内容。因此,除非您投入大量精力,否则错误消息是没有帮助的
  • 这感觉不是很地道。我见过的唯一使用
    :pre
    /
    :post
    的代码是解释如何使用
    :pre
    /
    :post
    的文章
  • 把线程宏的步骤拉到它们自己的
    defn
    s中,这样我就可以把
    :pre
    /
    :post
    放进去,这真是一件痛苦的事
  • 如果我虔诚地遵循这种做法,我认为我的代码可能会变得像Java一样冗长。我会手工重新发明打字系统
  • 我已经到了这样的地步,我在代码中添加了如下安全检查:

    (when (= next-url url)
                (throw (IllegalStateException. (str "The next url and the current url are the same " url))))      
    (when-not (every? map? posts-list)
                (throw (IllegalStateException. "parsed-html->posts must return a list of {:post post :source-url source-url}")))
    
    这只修正了第一个要点

    我也觉得

  • 我有一个非常非常错误的开发过程,我不知道
  • 有一些调试工具/库,我不知道其他人都知道
  • 其他人都有这样的问题,这是Clojure肮脏的小秘密/其他人都习惯于动态语言,并期望通过与我一样的努力来解决错误
  • 逆时针方向有一些错误,它让我的生活变得比它需要的更艰难
  • 我应该为Clojure代码编写比Java代码多得多的单元测试。即使我在写一次性代码
    在这种情况下,发现问题的根源很容易:

  • 我们有一个应用于已知项向量的函数。我们也期待着一个特别的结果

  • 应用该函数会导致出现问题。让我们先看看函数的内部;它恰好是一个
    ->
    管道

  • 诊断问题最直接的方法是省去管道的一些最后阶段,看看转换的中间阶段是否如我们预期的那样


  • 做3。在REPL中尤其直截了当;一种方法是将输入和中间结果定义为临时变量,另一种方法是使用
    *1
    *2
    *3
    。(如果管道很长或者计算需要很多时间,我建议至少每隔几步执行一次临时的
    def
    ,否则
    *n
    s就足够了。)


    在其他情况下,您可能会做一些稍有不同的事情,但在任何情况下,将工作分解为可管理的块,以便在REPL中使用是关键。当然,熟悉Clojure的序列和集合库会大大加快这个过程;但是,在实际任务的小部分环境中使用它们是了解它们的更好方法之一。

    理解clojure异常的最好方法是了解clojure是使用Java类实现的,接口等。因此,每当您遇到此类异常时,请尝试将异常中提到的类/接口映射到clojure概念


    例如:在当前的异常中,可以很容易地推断,
    clojure.lang.PersistentVector
    正在尝试在方法
    clojure.lang.Numbers.add
    中键入cast to
    java.lang.Number
    。从这些信息中,您可以查看您的代码,直观地找出您在代码中使用
    add
    的位置,即
    +
    ,然后通过以下事实诊断该问题:不知何故,this+将向量作为参数而不是数字

    我发现clojure.tools.logging/spy宏对于调试非常有用。它打印出包装好的表达式及其值。如果您现在不想设置clojure.tools.logging(它需要正常的Java日志配置),您可以使用以下方法:

    (defmacro spy
      [& body]
      `(let [x# ~@body]
         (printf "=> %s = %s\n" (first '~body) x#)
         x#))
    
    *请记住,上面的代码不会打印出惰性seq的值,如果它还没有打印出来的话