用于基准表达的Clojure宏

时间:2017-11-26 17:25:39

标签: clojure macros benchmarking

我是Clojure的新手,在创建宏时遇到了一些麻烦。

Clojure中的代码,用于评估前1,000,000个整数的总和:

(apply + (range 1E6))

此基准代码评估总和,但也会打印此代码所需的纳秒数:

(let [start (System/nanoTime)
      result (apply + (range 1E6))]
      end (System/nanoTime)]
  (println "Took:" (- end start) "ns")
  result)

我将如何以下列形式与s.t.创建一个宏。 (benchmark expr)的值是expr的评估它打印以纳秒为单位评估expr所需的时间?

(defmacro benchmark [code]
  ...)

另外,关于宏如何工作,我有点困惑。为什么我们不能使用以下形式的普通函数?

(defn benchmark [code]
  ...)

2 个答案:

答案 0 :(得分:1)

在您的情况下,宏和普通函数之间的主要区别在于参数评估顺序。传递给函数的参数在传递给函数体之前进行求值,而宏参数传递给宏体未评估(意味着宏体看到您输入的代码形式并且可以对纯代码进行操作)。小例子:

user> 
(defn add2 [a]
  (println "a is" a)
  (+ a 2))
#'user/add2

user> (add2 (+ 10 10))
a is 20
22

user> 
(defmacro add2-m [a]
  (println "a is" a)
  `(+ ~a 2))
#'user/add2-m

user> (add2-m (+ 10 10))
a is (+ 10 10)
22

这意味着编写日志记录功能将是不存在的,因为作为arg传递的基准测试代码将在第一个时间戳之前进行评估。

并且您想要的宏看起来像是这样的:

(defmacro bm [form]
  `(let [start# (System/nanoTime)
         result# ~form
         end# (System/nanoTime)]
     (println "Took:" (- end# start#) "ns")
     result#))

在编译时扩展为以下内容:

;;(bm (apply + (range 1E6)))
;;=> Took: 131009572 ns
;;=> 499999500000

(let*
  [start__6355__auto__
   (. java.lang.System nanoTime)
   result__6356__auto__
   (apply + (range 1000000.0))
   end__6357__auto__
   (. java.lang.System nanoTime)]
  (println "Took:" (- end__6357__auto__ start__6355__auto__) "ns")
  result__6356__auto__)

答案 1 :(得分:0)

这是一个非常好的网站,可以了解单引号以及如何正确使用它们 https://8thlight.com/blog/colin-jones/2012/05/22/quoting-without-confusion.html

作为旁注,我确定这项任务的目的是自己尝试而不是复制答案:)。