I'm new to clojure and I want to create a function which returns a vector of all divisors of a certain Number.
For instance:
[1 2 3] for 6 as input
(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 )))
))))
This function returns the output in the following format:
[1][2][4][5][10][20][25][50] for 100 but I want to get the output in one vector. It seems that the seq variable is constantly overwritten with each loopstep. Could anyone explain this behaviour and provide me a workaround?
Thanks in advance and best regards
You can avoid looping using a somewhat more idiomatic solution:
(defn divisors
[n]
(filter (comp zero? (partial rem n)) (range 1 n)))
I think your approach is not correct, you can take a look to:
And this code (using ' for ' function ) can easily resolve your specification
(let [n 100]
(for [x (range 1 n)
:when (zero? (rem n x))]
x))
=>(1 2 4 5 10 20 25 50)
Your fundamental problem is that you are attempting to take an imperative approach, but Clojure collections are immutable. Also, I think that dotimes
will always return nil
, and print
returns nil
after printing its parameter(s).
There is a better way, but let's first see how we can use atoms to obtain an imperative solution:
(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
Note that we have eliminated the print
and expect seq
to be an atom (which we update using swap!
). We can now call div
as follows:
user> (deref (div 6 (atom [])))
[1 2 3]
We could improve this by moving seq
from the parameter list to a let
inside the function, then dereferencing it when we return. But it would be better to avoid mutation in the first place. As tangrammer indicates in his answer, this is easily accomplished using 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)
In production code, you would probably inline r
in this case, but I wanted to retain your original structure as much as possible. We could also use zero?
in the :when
clause. But all we're really doing in the for loop is a simple filter, so we could take Guillermo's approach and use filter
instead.
div
takes an integer x
and a sequence seq
as arguments:
it first declares r
the range 1..(x/2)
;
it then iterates for i
from 0
to (count r)
, ie from 0
to (x/2) - 1
:
for each i
, it prints the result of (conj seq (nth ri))
, ie the result of (conj seq (+ i 1))
.
Clojure uses immutable data structures which means that (conj seq d)
returns a new sequence containing all of seq
's elements plus d
; in particular, if seq
is the empty vector []
, it returns [d]
.
End result: (div n [])
prints [d]
if d
divides x
, for each d
from 1
to (x/2)
.
Your code fails because you try to write Clojure code in an imperative way ; it is not standard (nor recommended), but possible by using mutable references eg atoms (see @NathanDavis 's answer ) or transient (mutable) data structures:
(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)))
An idiomatic solution would use for
(as @tangrammer suggested ) ; it is not a loop structure (like in Java) but a macro that returns a lazy sequence:
(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.
A for
form with no body to speak of feels a bit abusive (though pleasing to the eye, IMHO) ; an alternative would be the filter displayed in @GuillermoWinkler 's answer :
(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
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.