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
lisp
exercises. Most of the top results all end up pointing
to
this
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
:else keywords.
(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 nil.
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
runtime.
(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
whipping out 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)))))