【问题标题】:Clojure clean up inner reduceClojure 清理内部减少
【发布时间】:2014-09-06 10:15:38
【问题描述】:

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

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

-------编辑---------

@A。韦伯 - 感谢您的代码。我习惯了命令式编程,所以我用 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))
}

【问题讨论】:

    标签: clojure


    【解决方案1】:

    考虑将您的代码分解为 3 个函数来组合

    1. 找出每个因素及其多重性
    2. 生成所有子集(如果需要,使用 clojure.math.combinatorics)
    3. 通过子集应用乘法映射

    但你可以一起做这一切

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

    【讨论】:

    • 谢谢,我编辑了我的条目以显示与您在 Groovy 中的代码等效的内容。您有什么建议/练习可以让您更擅长函数式编程吗?
    • @jcheat 4clojure 有一套很好的分级功能问题。提交自己的解决方案后,您可以关注一组其他用户并查看他们的解决方案。
    【解决方案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}
    

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

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        猜你喜欢
        • 2018-10-29
        • 1970-01-01
        • 2020-03-25
        • 1970-01-01
        • 2016-03-26
        • 2011-03-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多