不要在递归中重复自己

时间:2013-07-26 08:07:17

标签: recursion clojure

我使用loop recur进行递归,但有一些相当复杂的逻辑。事实证明我应该在两个分支中做相同的事情,这两个分支都应该导致递归。我不相信我可以使用函数来抽象它,因为recur的局限性,所以我怎么能这样做而没有重复的代码。我必须使用宏吗?

代码如下,重复的代码用REPEATED CODE HERE

突出显示
(defn overlapping-sampler [n-samples]
  (let [...]
    (loop [samples [] cache (zipmap boxes []) n-samples n-samples]
      (cond
        (zero? n-samples)
        samples

        :else
        (let [box (categorical boxes volumes)
              cache-item (peek (cache box))]
          (if (nil? cache-item)
              ; REPEATED CODE HERE
              (let [sample (interval-sample (:internals box))
                    all-boxes (map #(apply (:formula %) sample) boxes)
                    pos-dominant (max-pred-index true? all-boxes)
                    pos-box (max-pred-index #(= box %) boxes)]
                    (if (= pos-dominant pos-box)
                        (recur (conj samples sample) cache (dec n-samples))
                        (recur samples
                               (update-in cache [(nth boxes pos-dominant)]
                                          #(conj % {:from box :sample sample}))
                               n-samples)))

              ; Otherwise with prob p=ratio of overlapping region/box, take sample
              (if (flip (/ (volume (overlap box (:from cache-item))) (volume box)))
                  (recur (conj samples (:sample cache-item)) ; I should take the sample
                         (update-in cache [box]
                                    #(pop %))
                         (dec n-samples))

                  (let [sample (gen-until #(interval-sample (:internals box))
                                    #(and (apply (:formula box) %)
                                          (not (apply (:formula (:from cache-item)) %))))
                        ;REPEATED CODE HERE
                        all-boxes (map #(apply (:formula %) sample) boxes)
                        pos-dominant (max-pred-index true? all-boxes)
                        pos-box (max-pred-index #(= box %) boxes)]
                        (if (= pos-dominant pos-box)
                            (recur (conj samples sample) cache (dec n-samples))
                            (recur samples
                                   (update-in cache [(nth boxes pos-dominant)]
                                              #(conj % {:from box :sample sample}))
                                   n-samples))))))))))

2 个答案:

答案 0 :(得分:1)

使用本地功能:

(defn overlapping-sampler [n-samples]
  (let [fun (fn [sample samples]
              (let  [all-boxes (map #(apply (:formula %) sample) boxes)
                     pos-dominant (max-pred-index true? all-boxes)
                     pos-box (max-pred-index #(= box %) boxes)]
                (if (= pos-dominant pos-box)
                  [(conj samples sample) cache (dec n-samples)]
                  [samples
                   (update-in cache [(nth boxes pos-dominant)]
                              #(conj % {:from box :sample sample}))
                 n-samples])))]
    (loop [[samples cache n-samples] [[] (zipmap boxes []) n-samples]]
      (cond
        (zero? n-samples)
        samples

        :else
        (let [box (categorical boxes volumes)
              cache-item (peek (cache box))]
          (if (nil? cache-item)
              ; REPEATED CODE HERE
            (let [sample (interval-sample (:internals box))]
              (recur (fun sample) samples))
              ; Otherwise with prob p=ratio of overlapping region/box, take sample
              (if (flip (/ (volume (overlap box (:from cache-item))) (volume box)))
                  (recur [(conj samples (:sample cache-item)) ; I should take the sample
                         (update-in cache [box]
                                    #(pop %))
                         (dec n-samples)])

                  (let [sample (gen-until #(interval-sample (:internals box))
                                    #(and (apply (:formula box) %)
                                          (not (apply (:formula (:from cache-item)) %))))]
                    (recur (fun sample) samples)))))))))

答案 1 :(得分:0)

也许改写为

(let [cache-item ...]
  (if (and (nil? cache-item) (flip ...))
    (recur ...)
    (let [sample (if (nil? cache-item)
                   (... calculate sample ... )
                   (... use cache-item ... ))
          ; REPEATED CODE HERE
          ...]
       ...)))

这会在(nil? cache-item)的“else”分支中对if进行两次测试。您可以引入cache-hit?本地来存储(nil? cache-item)的值,以避免重复输入nil?来电;然而,不是出于性能原因,因为几乎没有区别。