2013-07-25 33 views
2

我是clojure的新手,所以对不起,如果我还没有使用正确的术语。我正在寻找一种方式,以最习惯的方式进行以下操作:如何在clojure中返回以前计算的结果?

我有一个函数将任意给定数量的地图合并为一个,我们称之为merge-maps

现在我需要第二个函数调用第一个函数,然后用结果调用一个静态void java函数,然后返回完全相同的结果。我得到了前两件事,但我没有做第三件事。由于静态无效的方法evaulates为零,我必须以某种方式返回值,但我不知道如何:

(defn my-funky-function [& ms] 
    (def merged (merge-maps ms)) 
    (SomeJavaClass/someStaticVoidMethod merged, "some", "other", "stuff") 
    ;????? <- how do I "return" merged from here?) 
) 
+0

IIRC函数中最后一行的评估结果是返回值。 – m0skit0

+2

您应该**从不**在函数内部使用'def'。不仅因为你影响全局范围,而且因为Clojure在编译期间为'def'创建了变量 –

+0

@ m0skit0它不是,因为void方法返回nil。 –

回答

1

无需功能,也不要使用def在函数中,除非你明确打算“创建和实习生全局变量”(中def文档字符串)。合并地图可以使用merge完成。

(doto (merge ms) (SomeJavaClass/someStaticVoidMethod "some" "other" "stuff")) 

编辑:因为我的答案可能有关正确使用高清的误解(见狮子座流星雨的评论):在函数中使用清晰度是不是一个好主意。我引用了文档字符串来暗示使用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 

因此,如果首先让块产生你想要的结果,让块可以与多托这是一个帮助宏不会完全不是别的,创建替换让你阻止。

(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) 

既然你不是“做什么”到您的合并地图,名称为“多托”可能有些误导,但希望我可以convice您保存的冗余几行代码。

+0

对于全局变量使用'def'是一个非常糟糕的主意。还有很多其他的方法可以做到这一点:原子,动态变量(参见[set!](http://clojuredocs.org/clojure_core/clojure.core/set!)),参考文献。 “def”意味着每个变量只能使用一次(设置根绑定)并且只能从顶层使用。 –

+0

我完全同意你的看法。你想写关于Dominics答案的评论吗?我很难想象在功能中使用def的一个理由。也许重新设置一个像名称空间那样的kitchensink,它只能从repl中用于开发。但我从来没有这样做,肯定不会推荐它。我引用了文档字符串来暗示def的作用,同时也明确了使用def绑定符号与使用let绑定不同。 –

1

像这样

(defn my-funky-function [& ms] 
    (def merged (merge-maps ms)) 
    (SomeJavaClass/someStaticVoidMethod merged, "some", "other", "stuff") 
    merged) 

注:应使用let,而不是绑定创建一个Var与def

(defn my-funky-function [& ms] 
    (let [merged (merge-maps ms)] 
    (SomeJavaClass/someStaticVoidMethod merged, "some", "other", "stuff") 
    merged)) 
+0

第一个选项不是线程安全的。如果两次调用my-funky函数同时发生,它们将共享合并的单个值。几乎没有理由在函数中使用def。你介意去除那个例子,这样人们就不会复制它了吗? –

+0

@ArthurUlfeldt我替换了*你可以使用let而不是def *和*你应该使用let而不是def * – sloth