Clojure清理内部减少

时间:2014-07-16 02:44:26

标签: clojure

我正在尝试计算一个数字的因子,给出素数,例如:

REPL=> (find-factors 1176 #{2 3 7})
#{7 24 4 21 1176 294 56 168 196 6 28 588 3 12 2 14 392 98 147 42 8 49 84}

我正在使用reduce和声明内部步骤函数,但似乎代码可能更清晰。我很感激有关如何使代码更具惯用性的任何建议。

(defn mul-all [find-for prod-multiples factors]
  (let [step (fn [[prod-multiples factors] mult]
           (let [step2 (fn [[prod-mults factors] mult2]
               (let [multiple (* mult mult2)]
                   (if (zero? (rem find-for multiple)) 
                     [(into prod-mults [mult mult2 multiple]) (into factors [multiple])] 
                     [(reduce disj prod-mults [mult mult2]) factors])))]   
             (reduce step2 [prod-multiples factors] prod-multiples)))]
(reduce step [prod-multiples factors] factors)))

(defn find-factors [find-for prime-factors]
  (loop [result (mul-all find-for prime-factors prime-factors)]
    (if (zero? (count (first result)))
     (second result) 
     (recur (mul-all find-for (first result) (second result))))))

------- EDIT ---------

@A。 Webb - 感谢您的代码。我习惯于命令式编程,所以我在Groovy中重写了你的例子,以了解它正在做什么。 findFactorsInject()方法使用Groovy的inject()方法,该方法相当于Clojure中的reduce。

static List recurFn(Set a, Integer n, Integer p ) {
    println "a $a n $n p $p"
    if(n%p == 0) {
        a.addAll(a.collect{Integer it ->it*p})
        recurFn(a, (Integer)(n/p), p);
    } else {
        return [a, n]
    }
}

static findFactors(Integer findFor, Set<Integer> primes) {
    List result = []
    for(Integer prime in primes) {
        if(result.size() == 0) {
            result = recurFn([1] as Set, findFor, prime)
        } else {
            result = recurFn((Set)result[0], (Integer)result[1], prime)
        }
    }
    return result
}

static findFactorsInject(Integer findFor, Set<Integer> primes) {
    primes.inject ([[1] as Set, findFor], 
        { List accumulate, Integer prime ->  
            recurFn((Set)accumulate[0], (Integer)accumulate[1], prime)  
        })
}
static main(args) {
    println findFactors(1176, ( (Set) [2, 3, 7 ] as Set))
    println findFactorsInject(1176, ( (Set) [2, 3, 7 ] as Set))
}

3 个答案:

答案 0 :(得分:4)

考虑将代码分解为3个函数来构成

  1. 查找每个因素及其多重性
  2. 生成所有子集(如果需要,使用clojure.math.combinatorics)
  3. 地图应用乘法通过子集
  4. 但你可以一起做到这一点

    (defn factors [n primes] 
      (-> (reduce 
            (fn [[a n] p] 
              (if (zero? (rem n p)) 
                (recur [(concat a (map #(* p %) a)) (quot n p)] p)
                [a n])) 
            [[1] n] primes)
          first set))
    
    (factors 1176 [2 3 5 7])
    ;=> #{7 1 24 4 21 1176 294 56 168 196 6 28 588 3 12 2 14 392 98 147 42 8 49 84}
    

答案 1 :(得分:2)

......或者,严重依赖于序列函数:

(defn factors [n primes]
  (set
    (reduce
      (fn [xs ys] (for [x xs, y ys] (* x y)))
      [1]
      (map
        (fn [p] (take-while #(zero? (mod n %)) (iterate #(* p %) 1)))
        primes))))

例如,

(factors 1176 [2 3 5 7])
; #{1 2 98 3 4 196 6 294 7 8 168 392 42 12 588 14 49 147 84 21 24 56 1176 28}

如果我们命名函数并使用->>线程宏,这可能更容易阅读:

(defn factors [n primes]
  (letfn [(products [xs ys] (for [x xs, y ys] (* x y)))
          (factor-powers [p] (take-while #(zero? (mod n %)) (iterate #(* p %) 1)))]
    (->> primes
         (map factor-powers)
         (reduce products [1])
         set)))

...产生与以前相同的结果:

(factors 1176 [2 3 5 7])
; #{1 2 98 3 4 196 6 294 7 8 168 392 42 12 588 14 49 147 84 21 24 56 1176 28}

答案 2 :(得分:1)

我不确定我是否完全理解您的问题但是

(find-factors 1176 #{147 }) 
;user=> (147 294 588 1176)

然后这个工作

(defn find-factors [n c]
      (->> n
           ((comp rest range inc) )
           (remove #(ratio? (/ n %))) 
           (#(for [x c 
                   y % 
                   :when (not (ratio? (/ y x)))]
               y ))))