2014-09-10 84 views
2

我是clojure的新手(以及針對該問題的函數式編程),我試圖做一些基本問題。我試圖在沒有遞歸的序列中找到第n個元素。Clojure手動查找序列中的第n個元素

所以像

(my-nth '(1 2 3 4) 2) => 3 

我有一個很難通過列表循環和返回時,我找到了第n個元素。我嘗試了一堆不同的方式和我結束了代碼

(defn sdsu-nth 
[input-list n] 
(loop [cnt n tmp-list input-list] 
    (if (zero? cnt) 
     (first tmp-list) 
     (recur (dec cnt) (pop tmp-list))))) 

這給了我一個異常,說:「從空單斜面彈出」

我不需要的代碼,但如果有人能指向我在正確的方向,這將真的幫助!

+0

當你說 「沒有遞歸」 你的意思是 「遞歸」?這就是循環/重複結構。 – 2014-09-10 15:34:32

+0

我的意思是說,我們可能想要使用循環結構,但函數不應該明確地對自己進行調用。 – 2014-09-10 15:39:57

+0

請注意,即使在'loop'中使用'recur',它也是模仿遞歸(儘管它會被編譯爲循環),所以將這種構造看作遞歸匿名函數更自然。 – Mark 2014-09-10 16:00:28

回答

3

您正在使用函數pop,它對不同的數據結構具有不同的行爲。

user> (pop '(0 1 2 3 4)) 
(1 2 3 4) 
user> (pop [0 1 2 3 4]) 
[0 1 2 3] 
user> (pop (map identity '(0 1 2 3 4))) 
ClassCastException clojure.lang.LazySeq cannot be cast to clojure.lang.IPersistentStack clojure.lang.RT.pop (RT.java:640) 

此外,你混合調用pop通過調用first。如果重複,則使用peek/popfirst/rest作爲成對,混合兩者可能會導致意外的結果。 first/rest是最低公分母,如果你想泛化各種順序類型,使用這些類型,並且他們會強制順序工作,如果可以的話。

user> (first "hello") 
\h 
user> (first #{0 1 2 3 4}) 
0 
user> (first {:a 0 :b 1 :c 2}) 
[:c 2] 

隨着你的功能,具有rest更換pop,我們得到了預期的結果:

user> (defn sdsu-nth 
     [input-list n] 
     (loop [cnt n tmp-list input-list] 
       (if (zero? cnt) 
        (first tmp-list) 
       (recur (dec cnt) (rest tmp-list))))) 

#'user/sdsu-nth 
user> (sdsu-nth (map identity '(0 1 2 3 4)) 2) 
2 
user> (sdsu-nth [0 1 2 3 4] 2) 
2 
user> (sdsu-nth '(0 1 2 3 4) 2) 
2 
user> (sdsu-nth "" 2) 
\2 
2

你真正想要做的是使用內置的nth功能它你問什麼: http://clojuredocs.org/clojure_core/clojure.core/nth

但是,因爲你正在學習這仍然是一個很好的鍛鍊。你的代碼實際上適用於我。確保你給它一個列表,而不是一個矢量 - 流行與矢量做了不同的事情(它返回的矢量沒有最後一項,而不是第一項 - see here)。

+0

是的,我知道內置的第n個功能,但我只是在這裏鍛鍊。 – 2014-09-10 15:48:47

1

如果提供的索引不等於或大於序列長度(您已實現零索引nth),那麼對於列表,您的代碼可以正常工作。當tmp-list在您的cnt達到零之前變空時,會出現此錯誤。

它不與向量工作這麼好:

user> (sdsu-nth [1 2 3 4] 2) 
;; => 1 
user> (sdsu-nth [10 2 3 4] 2) 
;; => 10 

似乎回到0元素,每提供的索引。由於噪音信號注意到它發生,因爲pop由於其內部結構對載體的作用不同。對於向量pop將從最後刪除元素,然後first返回任何向量的第一個值。

如何修復:使用rest而不是pop,以刪除應用於列表和向量時函數行爲的差異。

+0

它會從尾數倒數 – noisesmith 2014-09-10 16:14:14

+0

@noisesmith,剛剛嘗試過......它似乎返回任何提供的索引的第一個元素......等一下.. – Mark 2014-09-10 16:16:26

+0

'user>(pop [0 1 2 3 4 ])=> [0 1 2 3]' - 使用這種方法,你從結尾獲得第n個項目,而不是從開始 – noisesmith 2014-09-10 16:21:15

0

一個,我認爲這樣做的,使其成爲真正的非遞歸(即不用於/易復發的多種方式)是

(defn sdsu-nth 
[input-list n] 
(if (zero? (count input-list)) 
    (throw (Exception. "IndexOutOfBoundsException")) 
    (if (>= n (count input-list)) 
     (throw (Exception. "IndexOutOfBoundsException")) 
     (if (neg? n) 
      (throw (Exception. "IndexOutOfBoundsException")) 
      (last (take (+ n 1) input-list)))))) 
+0

'(第一(drop n input-list))'也可以,嘗試使用'cond'消除'if'' else'語句鏈。 – Mark 2014-09-11 07:40:32

1

給予列表list_nums,需要長達n + 1然後從那返回的最後一個元素是nth

(fn [list_nums n] (last (take (inc n) list_nums))) 

還或者:

#(last (take (inc %2) %1)) 

證明:

(= (#(last (take (inc %2) %1)) '(4 5 6 7) 2) 6) ;; => true 
1
(fn [xs n] 
    (if (= n 0) 
    (first xs) 
    (recur (rest xs) (dec n))))