Proper way to program in Lisp?
I'm new to Common Lisp, and found myself taking advantage of way functions returns values. The following are to two trivial examples:
(defun safe-avg (a b)
(and (numberp a) (numberp b) (/ (+ a b) 2)))
(defun safe-div (a b)
(and (numberp a) (numberp b) (> b 0) (/ a b)))
But I 开发者_JS百科could've written it like this (arguably clearer):
(defun safe-avg (a b)
(if (and (numberp a) (numberp b))
(/ (+ a b) 2)))
(defun safe-div (a b)
(if (and (numberp a) (numberp b) (> b 0))
(/ a b)))
I wanted to know what is the preferred method of doing something like this and the reasoning behind it, before I start abusing this habit.
Since you do not use the "else" branch, you could use when
:
(defun safe-div (a b)
(when (and (numberp a) (numberp b) (> b 0))
(/ a b)))
which is the same as:
(defun safe-div (a b)
(if (and (numberp a) (numberp b) (> b 0))
(/ a b)
nil))
which is the same as your version, but more explicit.
Anyway, these are all functionally equivalent. I would rather think about how these functions are to be used. If you do it like this, you will have to do null checks each time you call these functions, which is tedious.
It would be better to use conditions, either through type declarations, through asserts, or through explicit when
… signal
forms. You can then define handlers and restarts for these conditions for entire parts of your program. Further reading: Practical Common Lisp, ch. 19.
In this case, I would not handle this here at all:
(defun safe-div (a b)
(/ a b))
(or rather, just use /
).
If /
gets the wrong arguments, it will signal an error which you can then handle outside, where you know what that could mean.
The first form is idiomatically acceptable. It isn't the best example of using AND
's return value effectively, since the second form is a little clearer without being any lengthier. But you shouldn't be afraid to use LISP as intended!
For instance, going in an (inadvisable) direction...someone might argue that the implicit "nil return" of if
statements could be confusing and try and parallel if/else
structure to be more clear:
(defun safe-avg (a b)
(cond ((and (numberp a) (numberp b))
(/ (+ a b) 2))
(t
nil)))
That's bad. And you don't want to go down that road. So go ahead and use the expressions and trim the amount of code with nice evaluations, and use comments to pick up the slack to remind others (and yourself) of how it works if there's anything non-obvious about it.
精彩评论