首页 > 解决方案 > UnmodifiableRandomAccessList 和“包含?”的问题 功能

问题描述

我正在尝试为一个名为stensil的开源应用程序做出贡献。它是用 Clojure 编写的。

stensil是一个在 .docx 之上工作的模板引擎。模板引擎声明了一组要在模板中使用的自定义函数。完整的函数列表很短,可以在此处查看

我需要添加另一个自定义函数来检查列表是否包含元素。按照现有示例,我这样做了:

(defmethod call-fn "contains" [_ item items] (contains? items item))

为了测试这一点,我创建了一个 .docx 文件,其中包含以下代码段:

{%= contains(5, data) %}

和一个包含以下内容的 .json 文件:

{
  "data": [9, 4, 1, 6, 3]
}

然后我像这样运行应用程序:

java -jar stencil-core-0.3.8-standalone.jar template.docx sample.json

不幸的是,在使用时,我定义的函数不起作用,抛出以下异常:

java.lang.IllegalArgumentException: contains? not supported on type: java.util.Collections$UnmodifiableRandomAccessList

看来items是类型java.util.Collections$UnmodifiableRandomAccessList。确实,如果我将 a 添加(println items)到函数中,我会得到:

#object[java.util.Collections$UnmodifiableRandomAccessList 0x778ca8ef [9, 4, 1, 6, 3]]

some我尝试通过使用函数来做不同的事情。这个答案表明它应该在大多数情况下工作。不幸的是,下面的两个变体都返回nil

(defmethod call-fn "contains" [_ item items] (some #{item} items))
(defmethod call-fn "contains" [_ item items] (some #(= item %) items))

我以前从未写过一行 Clojure,现在有点迷茫。我究竟做错了什么?some将列表从 UnmodifiableRandomAccessList 转换为可以使用或contains?可以使用的东西是否有意义?如果是,我如何对items变量执行这种转换?

更新。尝试.contains按照cfrick在答案中的建议使用,如下所示:

(defmethod call-fn "contains" [_ item items] (.contains items item))

这不会引发运行时错误,但输出始终是false现在。println调试揭示了这一点:

(defmethod call-fn "contains" [_ item items]
  (println item)
  (println items)
  (.contains items item))
1
#object[java.util.Collections$UnmodifiableRandomAccessList 0x778ca8ef [9, 4, 1, 6, 3]]

为什么?

标签: javaclojureleiningen

解决方案


上面的答案都是正确的,因为contains?不能用于此目的的列表,而且我们正在尝试比较不同的类型。

这个特定示例中的问题是第一个参数中的数字是 a java.lang.Long,而第二个参数中的列表包含java.math.BigDecimal实例。这是因为默认 JSON 解析器模板在独立模式下使用将数字反序列化为 BigDecimal

一个快速而肮脏的解决方案是在任何地方都使用字符串。

您还可以定义专门用于数字的函数的变体:

(defmethod call-fn "ncontains" [_ item items]
  (boolean (some #{(double item)} (map double items))))

或将所有数字强制为同一类型:

(defmethod call-fn "contains" [_ item items]
  (letfn [(norm [x] (if (number? x) (double x) x))]
    (boolean (some #{(norm item)} (map norm items)))))

推荐阅读