Clojure清理内部减速器

Clojure清理内部减速器,clojure,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和声明内部的step函数,但代码似乎更干净。我很欣赏任何关于如何使代码更地道的建议 (defn mul-all [find-for prod-multiples factors] (let [step (

我试图计算一个数的因子,给定素数,例如:

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和声明内部的step函数,但代码似乎更干净。我很欣赏任何关于如何使代码更地道的建议

(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))))))
-------编辑---------

@韦伯-谢谢你的代码。我习惯于命令式编程,所以我用Groovy重写了您的示例,以了解它在做什么。findFactorInject()方法使用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))
}
static List recurFn(设置a、整数n、整数p){
打印“a$a n$n$p$p”
如果(n%p==0){
a、 addAll(a.collect{Integer it->it*p})
recurFn(a,(整数)(n/p),p);
}否则{
返回[a,n]
}
}
静态findFactors(整数findFor,集合素数){
列表结果=[]
for(素数中的整数素数){
if(result.size()==0){
结果=recurFn([1]作为集合,findFor,prime)
}否则{
结果=recurFn((设置)结果[0],(整数)结果[1],素数)
}
}
返回结果
}
静态FindFactorInject(整数findFor,设置素数){
primes.inject([[1]作为集合,findFor],
{列表累加,整数素数->
recurFn((集合)累加[0],(整数)累加[1],素数)
})
}
静态干管(args){
println findFactors(1176,((套)[2,3,7]套)
println FindFactorInput(1176,((套)[2,3,7]套)
}

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

  • 找出每个因素及其多重性
  • 生成所有子集(如果需要,使用clojure.math.combinationals)
  • 映射通过子集应用乘法
  • 但你们可以一起做

    (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}
    

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

    (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}
    

    我不确定我是否完全理解你的问题,但如果

    (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 ))))
    

    谢谢,我编辑了我的条目以显示与Groovy中的代码相同的内容。有什么建议/练习可以让你更好地进行函数式编程吗?@jcheat有一套很好的分级函数式问题。提交自己的解决方案后,您可以跟踪其他用户并查看他们的解决方案。