2013-06-21 72 views
4

我有一些用clojure編寫的測試。這是一個簡單的例子:Clojure函數 - 在上次語句之前計算的返回值

(defn test1 
    [] 
    (start-server) 
    (run-pvt-and-expect "PVT-0") 
    (stop-server) 
) 

我想回的「運行PVT-和期望」的結果,但我需要它後執行其他功能。我怎樣才能以功能的方式在clojure中做到這一點(不使用像「let」這樣的結構)?謝謝。

注:我讀了this的問題及其答案,但無法將其應用於我的案例。另外,一個評論要求提供從未給出的例子,所以請考慮這個例子...

+7

你能解釋一下「功能性」是什麼意思嗎?對我來說,沒有任何關於'let'的非函數,但是你的例子中的其他語句都是*無功能的。 –

回答

7

你可以使用finally塊來做到這一點。

(try 
    (+ 2 3) 
    (finally (+ 6 7))) 

產生的5不13.

Here is the relevant doc page. 結果的releavant部分是:

任何最後exprs將被用於它們的副作用評價。

另一種選擇是編寫一個像with-open一樣的宏來啓動和停止服務器,同時返回正確的結果。雖然這只是將問題轉移到你如何編寫宏,因爲它可能需要使用let或者最終的try。

+3

嘗試/最後是給出例*的唯一正確的解決方案*:我敢打賭,如果'run-pvt ...'拋出,你仍然想停止服務器。 – dimagog

+0

謝謝。儘管我也喜歡Omri Bernstein建議的方法,但我最終採用了這種方法。謝謝。 – sebi

1

這裏是一個 「功能」 的方式,不使用讓結構:

(defn apply-get-first [c f] (f) c) 

(defn p [] 
    (start 
    (apply-get-first (compute) 
        #(end))) 

和現在令:

,你可以做這樣的:

(defn p[] 
    (let [s (start) 
     r (compute) 
     e (end)] 
    r)) 
4

那麼,我知道你可以使用任何內置的東西(除了你不想要的let以外)。但美麗的是,你可以自己建造它。

一個想法是創建一個「新」defn,其中表達式左側的:return關鍵字表示從該表中返回值,而不是最後一個表達式。一個宏將適用於此,因爲我們將構建一個修改後的(defn ...)表達式,該表達式需要未經評估的代碼。

(defmacro defn-return-middle 
    [nm arg-vec & body-exprs] 
    `(defn ~nm ~arg-vec 
    ~(if (some #{:return} body-exprs) 
     (let [[before _ [to-be-returned & after]] 
       (partition-by #{:return} body-exprs)] 
      `(let [ret# (do [email protected] ~to-be-returned)] 
      [email protected] 
      ret#)) 
     body-exprs))) 

這擴大是這樣的:

(defn-return-middle f [] 
    a 
    :return b 
    c) 

喜歡的東西:

(defn f [] 
    (let [ret (do a b)] 
    c 
    ret)) 

例如,你現在可以這樣做:

(defn-return-middle blah [a] 
    (+ 1 a) 
    :return (+ 2 a) 
    (println "does this work?")) 

現在在REPL :

user> (blah 5) 
does this work? 
=>7 

(耶!)

或者您的例子,你現在會做:

(defn-return-middle test1 
    [] 
    (start-server) 
    :return (run-pvt-and-expect "PVT-0") 
    (stop-server)) 

真,宏使用let,但它的工作原理是自動化let擴張如果你是手寫每次它,你會做。意思是現在,使用這個宏時,你不會再手寫let。此外,此宏目前不在聲明多個arities的函數定義上工作。但修改它以適應這些問題也不會太困難。

+0

哇。我真的很喜歡你的想法。儘管我選擇了不同的答案,因爲代碼更容易理解(我仍然有很多Clojure的道路可以覆蓋......) – sebi

3

我認爲你在這裏尋找的一般概念是K-combinator。你可以在Fogus' library中看到它的一個例子。你可以使用這樣的:

(defn test2 [] 
    (println "start server") 
    ((K (identity :return-value)) 
    (println "stop server"))) 

=> (test2) 
start server 
stop server 
:return-value 

這似乎對於這種情況有點過於複雜,所以也許我們可以簡化它(我不知道,如果在這一個匿名函數是「正式」的K-組合子與否):

(defn test3 [] 
    (println "start server") 
    ((fn [a b] a) 
    (identity :return-value) 
    (println "stop server"))) 

=> (test3) 
start server 
stop server 
:return-value 

由於字面向量不懶,你也可以先用同樣的方式:

(defn test4 [] 
    (println "start server") 
    (first 
    [(identity :return-value) 
    (println "stop server")])) 

=> (test4) 
start server 
stop server 
:return-value 

老實說,雖然,在實際的代碼我只用讓(就像你不想這樣做)因爲t他的意圖似乎更清晰的對我說:

(defn test5 [] 
    (println "start server") 
    (let [result (identity :return-value)] 
    (println "stop server") 
    result)) 

=> (test5) 
start server 
stop server 
:return-value 
1

這就是是!只要觀察試圖避免它時產生的不必要的複雜性即可。

這種情況是關於在返回第二個結果時必須執行三個副作用。典型用例:數據庫訪問。