3

我正在尝试使用 SICP 学习函数式编程。我想使用 Clojure。

Clojure 是 Lisp 的一种方言,但我对 Lisp 非常陌生。此代码片段不干净且不可读。如何使用 Lisp 方言编写更高效的代码?

以及如何从其他函数传递多个参数函数?

(defn greater [x y z]
  (if (and (>= x y) (>= x z)) 
    (if (>= y z)
      [x,y]
      [x,z]) 
    (if (and (>= y x) (>= y z)) 
      (if (>= x z)
        [y,x]
        [y,z]) 
      (if (and (>= z x) (>= z y)) 
        (if (>= y x)
          [z,y]
          [z,x])))))

(defn sum-of-squares [x y]
    (+ (* x x) (* y y)))

(defn -main
  [& args]
  (def greats (greater 2 3 4))
  (def sum (sum-of-squares greats)))
4

3 回答 3

7

你问了两个问题,我将尝试以相反的顺序回答它们。

应用集合作为参数

要将集合用作函数参数,其中每个项目都是函数的位置参数,您将使用该apply函数。

(apply sum-of-squares greats) ;; => 25


可读性

至于更普遍的可读性问题:

您可以通过概括问题来获得可读性。从您的代码示例中,看起来问题在于对集合中的两个最大数字执行平方和。因此,sort按降序排列并获取前两项在视觉上对集合更清晰。

(defn greater [& numbers]
  (take 2 (sort > numbers)))

(defn sum-of-squares [x y]
  (+ (* x x) (* y y)))

然后,您可以使用apply将它们传递给您的sum-of-squares函数。

(apply sum-of-squares (greater 2 3 4)) ;; => 25

请记住:排序功能不是惰性的。所以,它会实现并排序你给它的整个集合。在某些情况下,这可能会对性能产生影响。但是,在这种情况下,这不是问题。


更进一步

您可以通过将两个参数和切换到一个集合来进一步概括您的sum-of-squares函数以处理多个参数。xy

(defn sum-of-squares [& xs]
  (reduce + (map #(* % %) xs)))

上面的函数创建了一个匿名函数,使用#()简写语法对一个数字求平方。然后将该函数延迟映射,使用map,在xs集合中的每个项目上。所以,[1 2 3]会变成(1 4 9). 该reduce函数获取每个项目并将+函数应用于它和当前总数,从而产生集合的总和。(因为+需要多个参数,在这种情况下您也可以使用apply.)

如果使用其中一个线程宏将它们放在一起->>,它开始看起来非常平易近人。(尽管可以提出这样的论点,在这种情况下,我已经牺牲了一些可组合性以获得更高的可读性。)

(defn super-sum-of-squares [n numbers]
  (->> (sort > numbers)
       (take n)
       (map #(* % %))
       (reduce +)))

(super-sum-of-squares 2 [2 3 4]) ;;=> 25


于 2013-10-16T20:42:39.650 回答
3

(defn greater [& args] (take 2 (sort > args)))

(defn -main
  [& args]
  (let [greats (greater 2 3 4)
        sum (apply sum-of-squares greats)]
    sum))

良好的 clojure 风格的一个关键是使用内置的序列操作。另一种方法是单一的 cond 形式,而不是深度嵌套的 if 语句。

def不应在函数体内使用。

一个函数应该返回一个可用的结果(如果您运行项目,将打印 -main 返回的值)。

apply使用列表作为所提供函数的参数。

于 2013-10-16T20:43:04.040 回答
1

要编写可读的代码,尽量使用语言提供的函数: 例如 greater可以定义为

(defn greater [& args]
   (butlast (sort > args)))

sum-of-squares处理 from 的返回值greater,请使用参数解构

(defn sum-of-squares [[x y]] 
   (+ (* x x) (* y y)))

这需要知道参数序列中元素的数量,

或定义sum-of-squares以单个序列作为参数

(defn sum-of-squares [args]
   (reduce + (map (fn [x] (* x x)) args)))
于 2013-10-16T20:43:34.077 回答