将可变方法映射到 Clojure 中的 Java 个对象

Mapping mutable methods onto Java objects in Clojure

使用 Clojure,假设 t 表示一个 Java 对象,而我有一个 [ts].

的集合

如何将 .setter 映射到 [ts]?我在使用 (map #(.setter %1 %2) ts [vals]) 时遇到问题。当我之后访问相关的 getter 时,我 return 一个 nils.

的列表

听起来 setter 方法没有 returning 修改后的 t 对象。如果你写了 setter,你可以修改它,让它变成这样,或者你只需​​要坚持你原来的 ts(但也要确保使用 dorun 来挤出map 的懒惰):

(let [ts ...]
  (dorun (map #(.setter %1 %2) ts [vals]))
  (println "Modified ts:" ts))

如果拥有 map return 集合对您来说更方便,另一种方法是执行如下操作:

(dorun (map #(do (.setter %1 %2) %1) ts [vals]))

ArrayListJavaclass为例。我创建了其中三个,然后将 "s1" 添加到第一个,将 "s2" 添加到第二个,将 "s3" 添加到第三个。这是我的 "setter".

然后我读取每个的第一个值,我希望得到 "s1"、"s2"、"s3" - 所以在这个例子中是 getter。

(let [ts [(java.util.ArrayList. 1)
          (java.util.ArrayList. 1)
          (java.util.ArrayList. 1)]]
  ; Add one element to each of the ArrayList's
  (doall (map #(.add %1 %2) ts ["s1" "s2" "s3"]))

  ; Verify that elements are indeed added.
  (doall (map #(.get %1 0) ts)))

此示例按预期工作 - 后者 mapv returns ("s1" "s2" "s3").

为什么类似的方法对您不起作用?好吧,我强烈怀疑那是因为你使用了 map,其中 return 是一个惰性序列。

map return 是一个惰性序列,这意味着除非您尝试 get/use 它产生的值,否则它不会计算 - 而且您通常不需要 return 来自 setter 的值。这意味着您的 setter 将永远不会被调用。

这就是我使用 doall 的原因 - 它需要一个惰性序列并实现它,这意味着每个元素都会被计算(在你的情况下 - 每个 setter 都会被调用)。

我的示例,当我在设置元素时仅使用 map 而不是 doall 时,将会失败:

(let [ts [(java.util.ArrayList. 1)
          (java.util.ArrayList. 1)
          (java.util.ArrayList. 1)]]
  ; Use a lazy map here - without doall
  (map #(.add %1 %2) ts ["s1" "s2" "s3"])
  (doall (map #(.get %1 0) ts)))

; java.lang.IndexOutOfBoundsException: Index: 0, Size: 0

它确实失败了,因为没有添加任何元素。

或者,您可以使用 mapv 变体,它 return 是一个向量,因此总是不惰性的:

(let [ts [(java.util.ArrayList. 1)
          (java.util.ArrayList. 1)
          (java.util.ArrayList. 1)]]
  ; Use a lazy map, but realize sequence using doall
  (doall (map #(.add %1 %2) ts ["s1" "s2" "s3"]))
  (mapv #(.get %1 0) ts))

正如@John Wiseman 在评论中指出的那样,mapv 可能是一个更糟糕的选择,因为并不总是很清楚它用于强制实现,而 doall 则明确且显而易见。


关于惰性序列的一句话:

A lazy seq is a seq whose members aren't computed until you try to access them.

(source)

了解更多有关惰性序列的知识是一件好事,因为它们是 Clojure 代码库中广泛使用的强大工具。他们给 table 带来的一件有点违反直觉的事情是创建无限序列的能力,即 (range) 创建所有从 0 开始的数字的无限序列。所以这样做是完全合法的:

(map #(* % %) (range))

它将创建一个由所有数字组成的无限惰性平方序列!