【问题标题】:Not repeating myself in recursion不在递归中重复自己
【发布时间】:2023-03-18 21:16:02
【问题描述】:

我有一个使用 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))))))))))

【问题讨论】:

    标签: recursion clojure


    【解决方案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)))))))))
    

    【讨论】:

      【解决方案2】:

      也许改写成

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

      这会在if 的“else”分支中测试(nil? cache-item) 两次。您可以引入 cache-hit? 本地来存储 (nil? cache-item) 的值,以避免重复输入 nil? 调用;但是,不是出于性能原因,因为实际上应该没有区别。

      【讨论】:

        猜你喜欢
        • 2019-06-28
        • 1970-01-01
        • 2019-10-04
        • 2014-03-02
        • 1970-01-01
        • 2021-12-27
        • 2011-02-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多