【问题标题】:ClassCastException in ClojureClojure 中的 ClassCastException
【发布时间】:2014-09-11 18:37:11
【问题描述】:

我是 clojure 的新手,我一直在尝试不同的程序。这是我的程序:

(defn sdsu-reverse [x]
  (loop [n (count x) x x]
  (if (= 0 n)
    (x)
    (recur (- n 1) (conj (next x) (first x))))))

(= (sdsu-reverse [1 2 3 4 5]) [5 4 3 2 1])

我收到错误:java.lang.ClassCastException: clojure.lang.Cons cannot be cast to clojure.lang.IFn C:\Users\Shalima\Documents\Textbooks\Functional Programming\Programs\sample.clj:44 user/sdsu-reverse

但我似乎无法弄清楚。你能帮我解决这个问题吗?

谢谢。

【问题讨论】:

  • (x) 尝试将x (这是一个序列)作为函数调用。您只需要x。
  • 另外(因为你正在学习)你不需要每次都计算你的序列。您只想检查它是否为空。
  • @DiegoBasch 确实,您只想检查它是否为空,但请注意,原始代码只计算一次,而不是每次。
  • 正确,我以为我在比较中看到了计数。 OP 的代码(至少现在是这样)在循环开始时计算在内。但它确实保留了一个不必要的计数器。

标签: clojure


【解决方案1】:

正如上面 Alex 所说,您需要在 if 表达式中将 (x) 替换为 x。当您想要返回一个值时,将 x 括在括号中会将其视为一个函数。

至于代码中的另一个问题:

conj 是一个有点令人困惑的函数。

clojure.core/cons
([x seq])
  Returns a new seq where x is the first element and seq is
    the rest.

听起来足够清晰。但是看看当 conj 应用于列表而不是向量 (source) 时会发生什么。

user=> (conj [1 2 3] 4)
[1 2 3 4]

user=> (conj '(1 2 3) 4)
(4 1 2 3)

原因与向量和列表数据类型的构造方式有关。向量从右侧接收新值;列表,从左侧开始。

当您拨打conj (next x) (first x) 时,您实际上是在一遍又一遍地拨打conj '(2 3 4) '(1)。因此,您最终会得到与开始时相同的值。

更好的方法是使用递归,如下所示。

(defn sdsu-reverse [x]
  (if (empty? x)
    nil
    (cons (last x) (sdsu-reverse (drop-last x)))))

我希望这会有所帮助。

编辑以响应 Leonid 的 cmets。

Leonid 是正确的,上面的方法对于大序列会失败。或者你可以做类似的事情

(defn reverse' 
  ([x] (reverse' x nil))
  ([x acc] (if (empty? x) 
             acc 
             (recur (rest x) (cons (first x) acc)))))

【讨论】:

  • 使用recur总是比使用直接递归更好,不仅因为它快很多,还因为直接递归会导致堆栈溢出。尝试反转 (range 99999) 之类的内容,您就会明白我的意思了。
  • 使用last 和drop-last 代替first 和rest 也是一个非常糟糕的主意,因为它们在应用于序列时非常无效。
  • 同意。修改了我的答案。
  • 为了完整起见:最简单最有效的恢复序列的方法是使用reduce高阶函数:(reduce conj () x),这正是内置@987654340的方式@ 函数有效。
【解决方案2】:

你的代码中的类转换异常是因为你试图从一个常量中创建一个列表 即 (x) 所以只需用 x 替换它就不会出现类转换异常。

【讨论】:

    猜你喜欢
    • 2014-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-09
    • 2012-06-25
    • 2019-05-12
    • 2014-04-09
    • 2017-12-05
    相关资源
    最近更新 更多