You are here:   ArielOrtiz.com > Programming Languages > Macros Exercises

Macros Exercises

Note: This is a class activity, nothing needs to be delivered.

  1. Write a Clojure macro called debug, that provides a simple debugging facility for Clojure code. When (debug original-expression) is evaluated, a debugging line should be printed to the standard output with the following format:

    debug: original-expression => evaluated-expression

    Additionally, the debug macro should return the result of the evaluated expression. For example, the following code

    (inc (debug (* 4 5)))

    should return 21 and print this to the standard output:

    debug: (* 4 5) => 20

    Test your function with the following code:

    (defn fact
      [n]
      (if (zero? n)
          1
          (debug (* n (fact (dec n))))))
    
    (println (fact 5))

    The expected output is:

    debug: (* n (fact (dec n))) => 1
    debug: (* n (fact (dec n))) => 2
    debug: (* n (fact (dec n))) => 6
    debug: (* n (fact (dec n))) => 24
    debug: (* n (fact (dec n))) => 120
    120
  2. Write a Clojure macro called IF (note the uppercase letters). Its purpose is to provide a conditional statement that is syntactically a bit more similar to those found in languages like Pascal or Fortran. It has the following form:

    (IF condition :THEN exp1 exp2:ELSE exp3 exp4 …)

    It should basically expand to the following conventional Clojure if form:

    (if condition (do exp1 exp2 …) (do exp3 exp4 …))

    Almost everything is optional in the IF macro, except condition. Also, the :ELSE keyword may come before the :THEN keyword. The following examples show some legal forms and their results:

    user=> (IF (> 3 1) :THEN 'ok :ELSE 'oops)
    ok
    user=> (IF (> 3 1) :THEN 'ok)
    ok
    user=> (IF (< 3 1) :ELSE 'ok)
    ok
    user=> (IF (> 3 1) :ELSE 'oops)
    nil
    user=> (IF (> 3 1) :THEN)
    nil
    user=> (IF (> 3 1))
    nil
    user=> (macroexpand-1 '(IF (> 3 1) 
                               :ELSE (println "Else section.") 'oops 
                               :THEN (println "Then section.") 'ok))
    (if (> 3 1) (do (println "Then section.") (quote ok)) 
                (do (println "Else section.") (quote oops)))            
    user=> (macroexpand-1 '(IF (< 3 1) :ELSE 'ok))
    (if (< 3 1) (do) (do (quote ok)))

    Any expression in-between condition and the first occurrence of the :THEN or :ELSE keywords should just be ignored:

    user=> (macroexpand-1 '(IF (< 3 1) 1 2 3 :THEN 4 5 6 :ELSE 7 8 9))            
    (if (< 3 1) (do 4 5 6) (do 7 8 9))