简体   繁体   English

clojure中的除数函数

[英]Divisor function in clojure

I'm new to clojure and I want to create a function which returns a vector of all divisors of a certain Number. 我是clojure的新手,我想创建一个函数,它返回某个Number的所有除数的向量。

For instance: 例如:
[1 2 3] for 6 as input [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 ))) 
     ))))

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. [1] [2] [4] [5] [10] [20] [25] [50]为100,但我想在一个向量中得到输出。 It seems that the seq variable is constantly overwritten with each loopstep. 似乎seq变量不断被每个循环步骤覆盖。 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: 我认为你的方法不正确,你可以看看:

  • the loop function to resolve this kind of problems (i mean the cases where you need iterate and yield value), 循环函数来解决这类问题(我的意思是你需要迭代和屈服值的情况),
  • iterate function 迭代函数
  • recursive functions (a function that calls itself) 递归函数(一个自我调用的函数)

And this code (using ' for ' function ) can easily resolve your specification 此代码(使用' for '函数)可以轻松解决您的规范

(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. 您的根本问题是您正在尝试采用命令式方法,但Clojure集合是不可变的。 Also, I think that dotimes will always return nil , and print returns nil after printing its parameter(s). 此外,我认为dotimes将始终返回nil ,并且print在打印其参数后返回nil

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! ). 请注意,我们已经消除了print并期望seq成为一个原子(我们使用swap!更新swap! )。 We can now call div as follows: 我们现在可以调用div如下:

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. 我们可以通过运动改善这种seq从参数列表, let里面的函数,然后取消引用它,当我们返回。 But it would be better to avoid mutation in the first place. 但最好首先避免变异。 As tangrammer indicates in his answer, this is easily accomplished using for : 正如七巧板在他的回答中指出的那样,这很容易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. 在生产代码中,您可能会在这种情况下内联r ,但我希望尽可能保留原始结构。 We could also use zero? 我们也可以使用zero? in the :when clause. :when子句中。 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. 但是我们在for循环中真正做的只是一个简单的过滤器,所以我们可以采用Guillermo的方法并使用filter

div takes an integer x and a sequence seq as arguments: div将整数x和序列seq作为参数:

  • it first declares r the range 1..(x/2) ; 它首先声明r范围1..(x/2) ;

  • it then iterates for i from 0 to (count r) , ie from 0 to (x/2) - 1 : 然后迭代i0(count r) ,即从0(x/2) - 1

    • for each i , it prints the result of (conj seq (nth ri)) , ie the result of (conj seq (+ i 1)) . 对于每个i ,它打印(conj seq (nth ri))的结果,即(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 ; Clojure使用不可变数据结构,这意味着(conj seq d)返回一个包含所有seq元素加d的新序列; in particular, if seq is the empty vector [] , it returns [d] . 特别是,如果seq是空向量[] ,则返回[d]

End result: (div n []) prints [d] if d divides x , for each d from 1 to (x/2) . 最终结果: (div n [])如果d除以x则打印[d] ,每个d1(x/2)

Your code fails because you try to write Clojure code in an imperative way ; 您的代码失败,因为您尝试以命令方式编写Clojure代码; it is not standard (nor recommended), but possible by using mutable references eg atoms (see @NathanDavis 's answer ) or transient (mutable) data structures: 它不是标准的(也不推荐),但可以通过使用可变引用,例如原子 (参见@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)))

An idiomatic solution would use for (as @tangrammer suggested ) ; 一个惯用的解决方案将for (如@tangrammer 建议 ); it is not a loop structure (like in Java) but a macro that returns a lazy sequence: 它不是一个循环结构(就像在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.

A for form with no body to speak of feels a bit abusive (though pleasing to the eye, IMHO) ; for没有身体可言的形式感觉有点辱骂(虽然赏心悦目,恕我直言); an alternative would be the filter displayed in @GuillermoWinkler 's answer : 另一种选择是@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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM