I've started doing some things in Lisp, as you may have noticed. But, I find myself thinking that there are probably already functions or macros out there that I should be using instead of duplicating.
I started thinking, there should be a standard set of exercises one can go through to get familiar with a language. I'm thinking about starting a web forum with exercises where people can post solutions in various languages and discuss why one way is preferable (within a given language) to another way of going about the same problem. In some ways, the site would become an evolving Cookbook for multiple languages.
Anyhow, that's not why I'm posting here. I Google-d for
exercises. Most of the top results all end up pointing
short list of exercises. This is a great set to start with.
It's very short. None of them took very long to do. And, you
needed multiple tools to tackle them.
But, I'm not satisfied with two of my solutions.
Any criticism is welcome. I am particularly interested in when my macros leak or double-evaluate things or what-have-you.
My solution to #2 is a bit verbose, but I couldn't find any other
way to do it that passed all of the tests he demonstrated. Plus,
my solution allows multiple
:then and multiple
(defmacro key-if (test &rest forms) (let ((tag :none) (then nil) (else nil)) (dolist (cur forms) (cond ((eql cur :then) (setf tag :then)) ((eql cur :else) (setf tag :else)) ((eql tag :then) (push cur then)) ((eql tag :else) (push cur else)) (t (error "Need a :then or :else tag before exprs")))) (if (null then) (push nil then) (setf then (nreverse then))) (if (null else) (push nil else) (setf else (nreverse else))) `(cond (,test ,@then) (t ,@else))))
My solution to #4 is not satisfying either. I wanted to do the one-liner:
(define-modify-macro delete-car (&rest args) cdr)
But, he had some silly idea that I should be able to do
(delete-car nil) and have that return
So, I couldn't think of any way to do it that didn't double-evaluate.
And, so I double-evaluated: once at compile-time and once at
(define-modify-macro delete-car-real (&rest args) cdr) (defmacro delete-car-bad (l) (if (null l) nil `(delete-car-real ,l)))
So, ugly things like
(delete-car (nth (incf x) y))
increment too many times. I've tried five different ways including
get-setf-expansion and such. But, I can't
find any way to make this a macro that doesn't expand with a
(setq nil something) in it even if only in a path that
doesn't get executed.
(defmacro delete-car (l) (multiple-value-bind (dummies vals new setter getter) (get-setf-expansion l) `(let* (,@(mapcar #'list dummies vals) (,(car new) ,getter)) (if (null ,getter) nil (progn (car ,(car new)) (setq ,(car new) (cdr ,(car new))) ,setter)))))