我是clojure的新手,请原谅,如果我还没有使用正确的术语。我正在寻找一种方法,以最惯用的方式做到以下几点:
我有一个函数可以将任意给定数量的地图合并为一个,我们称之为merge-maps
。
现在我需要第二个调用第一个函数的函数,然后使用结果调用静态void java函数,然后返回完全相同的结果。我得到了前两件事,但我做了第三件事却失败了。由于静态void方法估计为nil,我必须以某种方式返回值,但我不明白如何:
(defn my-funky-function [& ms]
(def merged (merge-maps ms))
(SomeJavaClass/someStaticVoidMethod merged, "some", "other", "stuff")
;????? <- how do I "return" merged from here?)
)
答案 0 :(得分:1)
喜欢这个
(defn my-funky-function [& ms]
(def merged (merge-maps ms))
(SomeJavaClass/someStaticVoidMethod merged, "some", "other", "stuff")
merged)
注意:您应该使用let
绑定,而不是使用def
创建Var。
(defn my-funky-function [& ms]
(let [merged (merge-maps ms)]
(SomeJavaClass/someStaticVoidMethod merged, "some", "other", "stuff")
merged))
答案 1 :(得分:1)
不需要函数,也不要在函数内部使用def
,除非您明确打算“创建并实现全局变量”(def
的文档字符串)。合并地图可以使用merge
完成。
(doto (merge ms) (SomeJavaClass/someStaticVoidMethod "some" "other" "stuff"))
编辑:因为我的答案可能被误解为正确使用def(请参阅Leonids评论):在函数内部使用def绝不是一个好主意。我引用文档字符串来暗示使用def的全局效果,你肯定不希望在你的函数中使用def。
EDIT2:这里有关于上面这一行的更多解释:doto总是返回它的第一个参数。如果它是可变的并且后面的表达式将其修改为副作用,则返回修改后的版本。但是,如果您只想调用静态方法(可能)具有不会修改第一个参数的副作用,那么使用doto是正确的,因为您可以依赖地图的不变性。
既然你要求一般惯用法,并且想要在你的静态方法(或带副作用的函数)不希望你想要的返回值作为第一个参数时解决类似的问题,你总是可以这样做:
(doto (merge ms) (#(SomeJavaClass/someStaticMethod "Some" "other" "stuff" %)))
现在,如果由于任何原因你需要确保静态方法永远不会修改参数(如果它是一个可变的东西),使用let块也无济于事。这是因为在let-block中你将一个符号绑定到可变的东西上,你不会从符号中得到它的旧值。您需要事先创建旧值的副本,通常是通过调用它的构造函数并创建它的新实例。
repl上的示例:
(let [a (into-array [4 3 2 1]]
(java.util.Arrays/sort a)
a)
(first *1)
=> 1
;; now how to return the original thiing
(let [a (into-array [4 3 2 1])
a-copy (aclone a)]
(java.util.Arrays/sort a)
;; do some other ops on a, maybe invoke side-effects
a-copy)
(first *1)
=> 4
因此,如果第一个let-block产生了你想要的结果,那么let-block可以替换为doto,这是一个帮助宏,除了为你创建let-block之外别无其他。
(doto (into-array [4 3 2 1]) java.util.Arrays/sort)
(first *1)
=> 1
(macroexpand '(doto (into-array [4 3 2 1]) java.util.Arrays/sort))
=> (let* [G__7326 (into-array [4 3 2 1])] (java.util.Arrays/sort G__7326) G__7326)
由于你没有对你的合并地图“做任何事情”,“doto”这个名字可能会有些误导,但希望我能帮你保存几行冗余代码。