Patrick (patrickwonders) wrote,
Patrick
patrickwonders

  • Mood:

Still Learning...

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)))))
Tags: ask lj, idea, lisp
Subscribe
  • Post a new comment

    Error

    default userpic

    Your reply will be screened

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 9 comments