[英]Divisor function in clojure
我是clojure的新手,我想創建一個函數,它返回某個Number的所有除數的向量。
例如:
[1 2 3]為6作為輸入
(defn div [x seq]
(let [r (range 1 (+ (/ x 2) 1)) ]
(dotimes [i (count (range 1 (+ (/ x 2) 1) ))]
(cond
(= (mod x (nth r i )) 0)(print (conj seq (nth r i )))
))))
此函數以以下格式返回輸出:
[1] [2] [4] [5] [10] [20] [25] [50]為100,但我想在一個向量中得到輸出。 似乎seq變量不斷被每個循環步驟覆蓋。 誰能解釋這種行為並為我提供一個解決方法?
在此先感謝您的問候
您可以使用更慣用的解決方案來避免循環:
(defn divisors
[n]
(filter (comp zero? (partial rem n)) (range 1 n)))
您的根本問題是您正在嘗試采用命令式方法,但Clojure集合是不可變的。 此外,我認為dotimes
將始終返回nil
,並且print
在打印其參數后返回nil
。
有一種更好的方法,但讓我們首先看看我們如何使用原子來獲得一個必要的解決方案:
(defn div [x seq]
(let [r (range 1 (+ (/ x 2) 1)) ]
(dotimes [i (count (range 1 (+ (/ x 2) 1) ))]
(cond
;; "Append" the ith element of r to seq
;; (Technically, we are replacing the value stored in seq
;; with a new list -- the result of conj-ing (nth r i)
;; to the current value stored in seq)
(= (mod x (nth r i )) 0) (swap! seq conj (nth r i ))))) ;; <= don't print
seq) ;; <== seq is what we're interested in, so we return it here.
;; Otherwise, we would return the result of dotimes,
;; which is nil
請注意,我們已經消除了print
並期望seq
成為一個原子(我們使用swap!
更新swap!
)。 我們現在可以調用div
如下:
user> (deref (div 6 (atom [])))
[1 2 3]
我們可以通過運動改善這種seq
從參數列表, let
里面的函數,然后取消引用它,當我們返回。 但最好首先避免變異。 正如七巧板在他的回答中指出的那樣,這很容易for
:
(defn div [x]
(let [r (range 1 (+ (/ x 2) 1))]
;; Loop over r, taking only elements that are divisors of x
(for [i r
:when (= (mod x i) 0)]
i))) ;; <= for accumulates the result of this expression (here simply i) into a sequence
user> (div 6)
(1 2 3)
在生產代碼中,您可能會在這種情況下內聯r
,但我希望盡可能保留原始結構。 我們也可以使用zero?
在:when
子句中。 但是我們在for循環中真正做的只是一個簡單的過濾器,所以我們可以采用Guillermo的方法並使用filter
。
div
將整數x
和序列seq
作為參數:
它首先聲明r
范圍1..(x/2)
;
然后迭代i
從0
到(count r)
,即從0
到(x/2) - 1
:
對於每個i
,它打印(conj seq (nth ri))
的結果,即(conj seq (+ i 1))
。
Clojure使用不可變數據結構,這意味着(conj seq d)
返回一個包含所有seq
元素加d
的新序列; 特別是,如果seq
是空向量[]
,則返回[d]
。
最終結果: (div n [])
如果d
除以x
則打印[d]
,每個d
從1
到(x/2)
。
您的代碼失敗,因為您嘗試以命令方式編寫Clojure代碼; 它不是標准的(也不推薦),但可以通過使用可變引用,例如原子 (參見@NathanDavis的答案 )或瞬態 (可變)數據結構:
(defn div [n]
(let [coll (transient [])]
(dotimes [i (quot n 2)]
(let [d (inc i)]
(when (zero? (rem n d))
(conj! coll d))))
(persistent! coll)))
一個慣用的解決方案將for
(如@tangrammer 建議 ); 它不是一個循環結構(就像在Java中一樣),而是一個返回延遲序列的宏:
(defn div [n]
(for [:let [from 1 ;; :let bindings are great for
upto (inc (quot n 2)) ;; readability
divides? #(zero?
(rem % %2))]
d (range from upto) ;; This binding form translates to
;; "each d from 1 to x/2".
;; You can have several binding forms
;; in a single for, to iterate over
;; several variables.
:when (divides? n d)] ;; Selects d only if it divides n.
;; You can also define termination
;; conditions with :while.
d)) ;; Each valid d is appended to a growing
;; lazy sequence i.e. a sequence that is
;; constructed the first time it is
;; traversed.
for
沒有身體可言的形式感覺有點辱罵(雖然賞心悅目,恕我直言); 另一種選擇是@GuillermoWinkler回答中顯示的過濾器:
(defn divisors
[n]
(->> (range 1 (inc (quot n 2))) ;; the threading macros ->, ->> and .. are
(filter #(zero? (rem n %))))) ;; great to write sequence-manipulation
;; code as a sequence of step, though
;; maybe overkill in the present case
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.