0
votes

I'm new to Clojure and having some trouble creating a macro.

Code in Clojure that evaluates the sum of the first 1,000,000 integers:

(apply + (range 1E6))

This benchmark code evaluates the sum, but also prints the number of nanoseconds this code takes:

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

How would I go about creating a macro in the following form meeting s.t. the value of (benchmark expr) is the evaluation of expr AND it prints the time it takes to evaluate expr in nanoseconds?

(defmacro benchmark [code]
  ...)

Also, I'm a bit confused as to how macros work in general. Why can't we use an ordinary function in the following form?

(defn benchmark [code]
  ...)
2
See the implementation of time, which does exactly that. - glts
Just a note, if you want real benchmarking, Criterium is great. It's one of the few libraries I use. To do what you're trying to do, you could just write (criterium/bench (apply + (range 1E6))). - Carcigenicate

2 Answers

1
votes

the main difference between macro and plain function in your case, is in the arguments evaluation order. Arguments, passed to the function are evaluated before being passed to the function body, while macro arguments are passed to macro body unevaluated (meaning that macro body sees the code form you enter and can operate on the pure code). Little example:

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

This means that making up a function for logging would be nonsence, because the benchmarked code, passed as an arg would be evaluated before the first timestamp.

and your desired macro could look like that for example:

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

which is expanded at compile-time into the following:

;;(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__)
0
votes

This is a really good website to learn about single quotes and how to use them properly https://8thlight.com/blog/colin-jones/2012/05/22/quoting-without-confusion.html

As a side note I'm sure the point of this assignment is to try it yourself rather than copy an answer :).