在Clojure的条件表达式中保存重复计算?

在Clojure的条件表达式中保存重复计算?,clojure,Clojure,在下面的代码中,我需要计算来自键盘的一个元素和来自驱动器的一个元素的总和的最大值,前提是总和应小于或等于s (def s 10) (def keyboards '(3 1)) (def drives '(5 2 8)) (let [k (sort (fn [x y] (> x y)) keyboards) ; sort into decreasing d (sort (fn [x y] (> x y)) drives) ; sort into

在下面的代码中,我需要计算来自键盘的一个元素和来自驱动器的一个元素的总和的最大值,前提是总和应小于或等于
s

  (def s 10)
  (def keyboards '(3 1))
  (def drives '(5 2 8))

  (let [k (sort (fn [x y] (> x y)) keyboards) ; sort into decreasing
        d (sort (fn [x y] (> x y)) drives)    ; sort into decreasing
        ]
    (loop [k1 (first k) ks (rest k) d1 (first d) ds (rest d)]
      (cond
        (or (nil? k1) (nil? d1)) -1     ; when one of the list is empty
        (< (+ k1 d1) s) (+ k1 d1)       ; whether (+ k1 d1) can be saved to compute once?
        (and (empty? ks) (empty? ds)) -1
        (empty? ks) (if (< (+ k1 (first ds)) s) (+ k1 (first ds)) -1) ; whether (+ k1 (first ds)) can be saved once?
        (empty? ds) (if (< (+ d1 (first ks)) s) (+ d1 (first ks)) -1) ; whether (+ d1 (first ks)) can be saved once?
        :else (let [bs (take-while #(< % s) [ (+ k1 (first ds)) (+ (first ks) d1) ])]
                (if (empty? bs) (recur (first ks) (rest ks) (first ds) (rest ds))
                    (apply max bs))))))
(def s 10)
(def键盘(3 1))
(def驱动器’(5 2 8))
(让[k(排序(fn[xy](>xy))键盘);排序为递减
d(排序(fn[xy](>xy))驱动器);排序为递减
]
(循环[k1(第一个k)ks(剩余k)d1(第一个d)ds(剩余d)]
(续)
(或(nil?k1)(nil?d1))-1;当其中一个列表为空时
(<(+k1 d1)s)(+k1 d1);是否可以保存(+k1 d1)进行一次计算?
(和(空?ks)(空?ds))-1
(空?ks)(如果(<(+k1(第一个ds))s)(+k1(第一个ds))-1;(+k1(第一个ds))是否可以保存一次?
(空?ds)(如果(<(+d1(第一个ks))s)(+d1(第一个ks))-1;(+d1(第一个ks))是否可以保存一次?
:else(让[bs(花时间#)(<%s)[(+k1(第一个ds))(+(第一个ks)d1)])
(如果(空?bs)(重复出现(第一个ks)(剩余ks)(第一个ds)(剩余ds))
(应用最大bs(()()())))
正如评论中所指出的,我想知道是否有任何方法可以进一步优化条件表达式中重复的add操作。 在条件检查之前,使用let绑定计算所有条件可能不是最佳选择,因为只有一个条件为真,因此其他条件的计算将被浪费

我想知道Clojure编译器是否足够聪明,可以为我优化重复计算,或者是否有一个聪明的表达式使操作在检查和返回值中只执行一次


如有任何建议能使代码更加地道,我们将不胜感激

如果要保留当前代码的结构,可以使用Mark Engelberg的库:

(需要“[better-cond.core:as b]”)
(第10条)
(def键盘(3 1))
(def驱动器’(5 2 8))
(让[k(排序(fn[xy](>xy))键盘);排序为递减
d(排序(fn[xy](>xy))驱动器)];排序为递减
(循环[k1(第一个k)ks(剩余k)d1(第一个d)ds(剩余d)]
(b/秒
(或(nil?k1)(nil?d1))-1;当其中一个列表为空时
:let[x(+k1 d1)]
(
如果要保留当前代码的结构,可以使用Mark Engelberg的库:

(需要“[better-cond.core:as b]”)
(第10条)
(def键盘(3 1))
(def驱动器’(5 2 8))
(让[k(排序(fn[xy](>xy))键盘);排序为递减
d(排序(fn[xy](>xy))驱动器)];排序为递减
(循环[k1(第一个k)ks(剩余k)d1(第一个d)ds(剩余d)]
(b/秒
(或(nil?k1)(nil?d1))-1;当其中一个列表为空时
:let[x(+k1 d1)]
(
这听起来有点像。有更多计算效率的方法来计算它,但是如果你处理的是少于几百个的两个或三个小列表,并且如果它不是一个在热环中运行的关键代码,那么考虑更简单的:

(let [upper-limit 10
      keyboards [3 1]
      drives [5 2 8]]

  (apply max
         (for [k keyboards
               d drives
               :let [sum (+ k d)]
               :when (<= sum upper-limit)]
           sum)))

这听起来有点像。有更多计算效率的方法来计算它,但是如果你处理的是少于几百个的两个或三个小列表,并且如果它不是一个在热环中运行的关键代码,那么考虑更简单的:

(let [upper-limit 10
      keyboards [3 1]
      drives [5 2 8]]

  (apply max
         (for [k keyboards
               d drives
               :let [sum (+ k d)]
               :when (<= sum upper-limit)]
           sum)))

也许我有点误解,但你为什么要担心计算
(+k1 d1)
所需的时间呢?我只是想学会尽可能地优化。这里重复的操作只是“添加”,成本非常低。但是操作可能是更昂贵的任何东西。
(sort(fn[xy](>xy))键盘)
只是
(sort>键盘)
<代码>>与其他任何函数一样是一个函数。不要使用
-1
表示缺席。使用
nil
。也许我有点误解,但你为什么要担心计算
(+k1 d1)
所需的时间?我只想学习尽可能优化。这里重复的操作只是“添加”,成本非常低。但是操作可能是更昂贵的任何东西。
(sort(fn[xy](>xy))键盘)
只是
(sort>键盘)
<代码>>与其他任何函数一样是一个函数。不要使用
-1
表示缺席。请改用
nil
。谢谢!这正是我要找的!这可能是宏魔力的另一个展示。当然,我也对更好的惯用表达持开放态度。实际上,我上面的实现可能有错误,有时找不到最大和。请注意,我上面的实现是不正确的。下面的乔希是正确的。这很好地引入了更好的条件,谢谢!这正是我要找的!这可能是宏魔力的另一个展示。当然,我也对更好的惯用表达持开放态度。实际上,我上面的实现可能有错误,有时找不到最大和。请注意,我上面的实现是不正确的。下面的乔希是正确的。这对我们很有好处
(let [upper-limit 10
      keyboards [3 1]
      drives [5 2 8]
      eligbles (for [k keyboards
                     d drives
                     :let [sum (+ k d)]
                     :when (<= sum upper-limit)]
                 sum)]
  (if (empty? eligbles)
    nil
    (apply max eligbles)))