2013-12-18 27 views
1

請考慮這段代碼。如何避免兩次調用函數(recur ..)

(loop [k from res '()] 
    (if (< (count res) n) 
     (recur (next-num k) (conj res (next-num k))) 
     (sort res))) 

現在,假設函數(next-num k)執行了一些昂貴的計算。我們負擔不起這兩次。什麼是替代方案?我是Clojure的新手,並不瞭解許多基本功能。但我相信肯定有辦法。

回答

5

使用let

(loop [k from res '()] 
    (if (< (count res) n) 
     (let [the-next-num (next-num k)] 
      (recur the-next-num (conj res the-next-num))) 
     (sort res))) 
+4

或者,避免循環完全:'( - >> (迭代從明年-NUM) 休息 (取n) 排序)' – Beyamor

+0

@Beyamor:您的建議正在般的魅力。但我不知道爲什麼。你是否願意詳細說明,可能是在你的一個單獨的答案。 –

+0

謝謝,內森。這就是訣竅。 –

5

像@NathanDavis說,let允許你命名的中間值:

(loop [k from res '()] 
    (if (< (count res) n) 
    (let [next-k (next-num k)] 
     (recur next-k (conj res next-k))) 
    (sort res))) 

然而,在達到了loop之前,它的價值看,如果你可以撰寫核心功能達到相同的效果。通常,你可以寫一些不那麼複雜的東西,並揭示重要的細節。

代碼的肉涉及構建next-num的重複應用程序的序列。幸運的是,這有一個核心功能:iterate。使用iterate,我們可以創造價值的無限慵懶的序列:

(iterate next-num from) 
; => (from, from', from'', ...) 

然而,我們不希望第一這些值。我們能獲得與,以及序列的其餘部分,rest

(rest 
    (iterate next-num from)) 
; => (from', from'', from''', ...) 

在這一點上,我們可以抓住n值與take

(take n 
    (rest 
    (iterate next-num from))) 
; => (from', from'', from''') 

最後,我們可以將這些n值進行排序:

(sort 
    (take n 
    (rest 
     (iterate next-num from)))) 
; => (from'', from', from''') 

當然,深層嵌套函數調用很快變得尷尬。穿線宏->>(像它的兄弟->)是有點語法糖,讓我們重新安排我們的代碼到的東西更好一點:

(->> 
    (iterate next-num from) 
    rest 
    (take n) 
    sort) 

所以,你可以看到序列操作功能強大的庫如何讓我們擺脫低級循環。

+1

真的很棒的解釋。雖然,我已經接受了另一個答案,因爲「let」實際上正是我的問題的答案。但是這種方法絕對和明確地更好。 –