2013-07-25 32 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