[英]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.