针对Clojure实现问题的不同解决方案

时间:2010-02-01 19:13:14

标签: clojure

这是一个问题声明:

  

定义一个过程,该过程将三个数字作为参数,并返回两个较大数字的平方和。

解决方案很长,

(defn large [x y]
(if (> x y) x y))

(defn large-3 [x y z]
(if(> (large x y) z) (large x y) z))

(defn small [x y]
(if (< x y) x y))

(defn small-3 [x y z]
(if (< (small x y) z ) (small x y) z))

(defn second-largest [x y z]
  (let [greatest (large-3 x y z)
    smallest (small-3 x y z)]
    (first (filter #(and (> greatest %) (< smallest %)) [x y z]))))

(defn square [a]
  (* a a)
)

(defn sum-of-square [x y z]
  (+ (square (large-3 x y z)) (square (second-largest x y z))))

只是想知道在Clojure中可以解决这个问题的不同/简洁方法。

3 个答案:

答案 0 :(得分:8)

(defn foo [& xs]
  (let [big-xs (take 2 (sort-by - xs))]
    (reduce + (map * big-xs big-xs))))

答案 1 :(得分:7)

为什么只有3? N怎么样

(defn sum-of-squares [& nums]  
  (reduce + (map #(* % %) (drop 1 (sort nums)))))

或者如果你想要“最大的两个数字之和:

(defn sum-of-squares [& nums]  
  (reduce + (map #(* % %) (take 2 (reverse (sort nums))))))

(从2MichałMarczyk的回答中取2(反向(排序)))。

答案 2 :(得分:3)

(在下面的答案的第二次更新中,请参阅问题的序列版本以及一个懒惰的解决方案。)

(defn square [n]
  (* n n))

;; generalises easily to larger numbers of arguments
(defn sum-of-larger-squares [x y z]
  (apply + (map square (take 2 (reverse (sort [x y z]))))))

;; shorter; generalises easily if you want
;; 'the sum of the squares of all numbers but n smallest'
(defn sum-of-larger-squares [x y z]
  (apply + (map square (drop 1 (sort [x y z])))))

更新

为了扩展上述评论,第一个版本的直接概括是:

(defn sum-of-larger-squares [n & xs]
  (apply + (map square (take n (reverse (sort xs))))))

第二个版本直截了当地概括了Arthur在此期间发布的版本:

(defn sum-of-larger-squares [n & xs]
  (apply + (map square (drop n (sort xs)))))

另外,我已经看到在Scheme中解决的问题完全相同,甚至可能在SO上...它包括一些有趣的解决方案,比如计算所有三个方块中的一些,然后减去最小的方块(这是非常的用Scheme原语直接表达)。这是“无效的”因为它计算了一个额外的正方形,但它肯定是非常可读的。不幸的是,现在似乎无法找到链接。

更新2:

回应Arthur Ulfeldt对这个问题的评论,一个懒惰的解决方案(希望有趣)不同版本的问题。代码优先,解释如下:

(use 'clojure.contrib.seq-utils) ; recently renamed to clojure.contrib.seq

(defn moving-sum-of-smaller-squares [pred n nums]
  (map first
       (reductions (fn [[current-sum [x :as current-xs]] y]
                     (if (pred y x)
                       (let [z (peek current-xs)]
                         [(+ current-sum (- (* z z)) (* y y))
                          (vec (sort-by identity pred (conj (pop current-xs) y)))])
                       [current-sum
                        current-xs]))
                   (let [initial-xs (vec (sort-by identity pred (take n nums)))
                         initial-sum (reduce + (map #(* % %) initial-xs))]
                     [initial-sum initial-xs])
                   (drop n nums))))

clojure.contrib.seq-utils(或c.c.seq)lib用于reductions函数。可以使用iterate代替,但不能没有一些额外的复杂性(除非有人愿意计算在开始时要处理的数字序列的长度,这与保持懒惰的目标不一致尽可能)。

使用示例说明:

user> (moving-sum-of-smaller-squares < 2 [9 3 2 1 0 5 3])
(90 13 5 1 1 1)

;; and to prove laziness...
user> (take 2 (moving-sum-of-smaller-squares < 2 (iterate inc 0)))
(1 1)

;; also, 'smaller' means pred-smaller here -- with
;; a different ordering, a different result is obtained
user> (take 10 (moving-sum-of-smaller-squares > 2 (iterate inc 0)))
(1 5 13 25 41 61 85 113 145 181)

通常,(moving-sum-of-smaller-squares pred n & nums)会在原始seq数字的越来越长的初始片段中生成n预先最小数字的平方和的懒惰seq,其中'pred-smallest'表示最小关于谓词pred引起的排序。使用pred = >,计算n最大正方形的总和。

这个函数在描述方案解决方案时使用了上面提到的技巧,它总结了三个方块,然后减去了最小的方格,因此能够将运行总和调整正确的数量而无需在每一步重新计算它。

另一方面,它确实执行了大量的排序;我发现尝试优化这个部分并不值得,因为被排序的seqs总是n个元素长,并且每个步骤最多有一个排序操作(如果总和不需要调整,则没有)。