No matter what language I’m using, I often debug a program by injecting print or trace statements into the code which go to standard-out or a log file. This classic method of debugging has its advantages and disadvantages that I wont go into. While working on a Project Euler problem the other day I realized that writing trace statements can be quite tedious and make the code extremely ugly to look at. For example, if I want to print out the values of variables a, b and c I have to write this:
CL-USER> (format *trace-output* "A=~a B=~a C=~a" a b c) A=1 B=2 C=3 NIL
The above format statement requires a format string and a list of variables to output. It may not seem like a lot but when you are adding a bunch of these statements in your code it gets old really quick.
Ideally I want to keep trace statements as small as possible, making them easy to look at and type. Common Lisp makes these kinds of things really easy. Using macros I can abstract out all of the ugliness. Here’s what I want my new trace statement to look like:
CL-USER> (print-vars a b c) A=1 B=2 C=3 NIL
Notice the output of PRINT-VARS is the same as the FORMAT function above, but I did not have to type the *trace-output* stream or the format string. Getting rid of the *trace-output* stream could have simply been done with a wrapper function. But eliminating the format string requires knowledge of the variable names as well as their values. How could you do this in a more conventional language such as C++, Java or C#? I don’t think it can be done. C# probably stands the biggest chance of having a language feature to do it.
Enough language bashing, here’s the Common Lisp magic.. er, I mean, macro code to make it work:
(defmacro print-vars (&rest vars)
"Prints variables listed in vars to *trace-output*. The first argument
can be a string which will be printed directly."
`(progn ,(when (stringp (first vars))
`(format *trace-output* "~A " ,(pop vars)))
,@(loop for var in vars
collect `(format *trace-output* "~A=~A " ',var ,var))
(format *trace-output* "~%" )))
Now I admit, the code looks a bit ugly, especially to those who don’t know lisp. However, I will likely never have to look at it again and my trace statements, which I write and look at all the time are now simple and clean.
Posted by anthonyf
Posted by anthonyf
Posted by anthonyf