Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Can lisp functions have more than one point of return?

18 views
Skip to first unread message

Zachary Turner

unread,
Mar 30, 2002, 10:22:57 PM3/30/02
to
I'm new to LISP and I'm wondering if there is any way to make a LISP
function return from multiple locations within the functions? Or does it
always simply return the result of the last statement in the function?
Consider the following basic function:

(defun basic-function()
(if (> 4 0)
nil)
(if (> 4 0)
t))

This function will return t. I would like it to return nil however.
Clearly I can rewrite the function as:

(defun basic-function()
(if (> 4 0)
nil
(if (> 4 0)
t)))

but now I've added a seemingly unnecessary level of nesting. The reason I
ask about this is because it seems like there may be cases where you may
want to do some complex parameter validation and immediately return if the
parameters fail to be validated. Without multiple points of return I think
you will be forced to either write a function to do your validation and
simply call that function (not a big deal, I admit), or have many many
levels of nesting before you ever get to the meat of the function.

Obviously there are many other reasons besides just parameter validation
where multiple points of return would be useful, but this is the one that
led me to ask the question. If it is the case that LISP does not allow
this, would somebody mind explaining the motivation behind this? It must
have some advantages which I am not aware of.

Thanks very much
Zachary Turner

Jon Allen Boone

unread,
Mar 30, 2002, 11:33:40 PM3/30/02
to

Zachary,

You wrote:

> I'm new to LISP

welcome..

> Or does it always simply return the result of the last statement in the
> function?

I think you want to look at (cond ...)

> Clearly I can rewrite the function as:
>
> (defun basic-function()
> (if (> 4 0)
> nil
> (if (> 4 0)
> t)))
>
> but now I've added a seemingly unnecessary level of nesting.

(defun basic-function (x)
"This is a basic function that uses (cond ...)."
(cond ((> x 0) nil)
((< x 0)
(format t "x: ~S~%" x)
t)))


-jon
--
------------------
Jon Allen Boone
ipmo...@delamancha.org

Takehiko Abe

unread,
Mar 30, 2002, 11:51:00 PM3/30/02
to
In article <livp8.55592$J53.1...@typhoon.austin.rr.com>,
"Zachary Turner" <ztu...@bindview.com> wrote:

> I'm new to LISP and I'm wondering if there is any way to make a LISP
> function return from multiple locations within the functions?

check RETURN-FROM special form.

>
> (defun basic-function()
> (if (> 4 0)
> nil)
> (if (> 4 0)
> t))

(defun basic-function (n)
(when (> n 0)
(return-from basic-function nil))
...
...)

--
"What we hear constantly is that after September 11th, everything changed.
There is a good rule of thumb: if something is repeated over and over as
obvious, the chances are that it is obviously false." -- Chomsky
<http://www.zmag.org/content/ForeignPolicy/chomsky_march26.cfm>

Erik Naggum

unread,
Mar 31, 2002, 12:04:43 AM3/31/02
to
* "Zachary Turner" <ztu...@bindview.com>

See the index entry on "return" in the HyperSpec. If you do not have
access to the HyperSpec, you cannot learn Common Lisp well. Kent Pitman,
the editor of the standard graciously made it all available on the Web,
and you can (and should) download your own copy to install locally.
Several Emacs Lisp interfaces to it exist, among them mine, which I have
not updated since the online version moved and changed randmoly, so
search the Xanalys web site. Others have made some changes to my
hyperspec.el that loads its symbol->page association from a file that
Xanalys seems to have added to hide their overly short filenames. Franz
Inc also has a version of the standard from what I believe are the same
sources, and which they ship with every release. The online address is
http://franz.com/support/documentation/6.1/ansicl/ansicl.htm

///
--
In a fight against something, the fight has value, victory has none.
In a fight for something, the fight is a loss, victory merely relief.

Christopher C. Stacy

unread,
Mar 31, 2002, 3:18:38 AM3/31/02
to
>>>>> On Sun, 31 Mar 2002 03:22:57 GMT, Zachary Turner ("Zachary") writes:
Zachary> I'm new to LISP and I'm wondering if there is any way to make a LISP
Zachary> function return from multiple locations within the functions?

Zachary> (defun basic-function()
Zachary> (if (> 4 0)
Zachary> nil
Zachary> (if (> 4 0)
Zachary> t)))

Zachary> but now I've added a seemingly unnecessary level of nesting.

You could simplify the above as follows:

(defun basic-function ()
(> 4 0))

which will always return NIL.

Lisp does allow you to return from arbitrary points in the function,
by calling RETURN and especially RETURN-FROM. But doing that is a
little bit unusual: normally you would return the last value as you
are doing above. Your example code above is the normal and tasteful
way to do what you want, but you're right about the extra nesting.
So you were on the right track, before you started looking for an
unusual way to exit the function.

Before we go any further, though, first let's change the example
around to take an argument X so that we can have a more useful
function to play with.

Here's a simple way to write it:

(defun basic-function (x)
(> x 4)) ;Return T if X is more than 4, else return NIL.

Now let's have it do something useful in two branches:

(defun basic-function (x)
(if (> x 4)
nil
(if (< x 10)
nil)))

So BASIC-FUNCTION and checks to see whether X is between 4 and 10,
noninclusive. But it will still always return NIL for all X,
because if there is no "else" clause for an IF, that's NIL.

(defun basic-function (x)
(if (> x 4) ;If X is more than 4
nil ; return NIL
(if (< x 10) ;Else if X is less than 10
nil))) ; return NIL
;Else return NIL.


A more interesting function would return T, instead:

(defun basic-function (x)
(if (> x 4) ;If X is more than 4
t ; return T
(if (< x 10) ;Else if X is less than 10
t))) ; return T
;Else return NIL.

Of course, the bug here is that the function will return T
if X is *either* more than 4 *or* less than 10.
That's kind of a silly thing to write, but at least we're
back to your original question about the excess nesting.

When you have several conditionals that you want to test consecutively
until one of them succeeds, rather than nesting IFs you should consider
using COND. The following function does the same thing as the code above:

(defun basic-function (x)
(cond ((> x 4) t)
((< x 10) t)))

Of course, that returns T for any number that's greater than 4,
since all numbers below 4 are also below 10.

Here's a function that returns T for all negative numbers,
and for positive numbers more than 4.

(defun basic-function (x)
(cond ((> x 4) t)
((< x 0) t)))

In this example, the only thing we're doing in the "then" part of the
COND clauses is returning the value T. You can put as many expressions
there as you want. Here's a function that increases positive numbers
that are larger than 4 by an order of magnitude, but just returns the
number X if it's negative. Note that either of those results would be
considered "true" values in Lisp. Otherwise the function will return
NIL, which is the "false" value.

(defun basic-function (x)
(cond ((> x 4)
(format t "~&Accentuate the (sufficiently) positive...")
(* 10 x))
((< x 0)
(format t "~&Don't mess with Mister In-Between.")
x)))

By the way, there are other control forms in Lisp besides the family
of IF, COND, WHEN, and UNLESS. You can also use AND, OR, and NOT,
as in the following example:

(defun basic-function (x)
(or (> x 4)
(minusp x)))

That function returns T if the conditions are met, else NIL.

Hope these examples help!

Paolo Amoroso

unread,
Mar 31, 2002, 11:59:15 AM3/31/02
to
On Sun, 31 Mar 2002 05:04:43 GMT, Erik Naggum <er...@naggum.net> wrote:

[about the HyperSpec]


> Several Emacs Lisp interfaces to it exist, among them mine, which I have
> not updated since the online version moved and changed randmoly, so
> search the Xanalys web site. Others have made some changes to my
> hyperspec.el that loads its symbol->page association from a file that
> Xanalys seems to have added to hide their overly short filenames. Franz

That modified version is included with ILISP. By the way, CLISP has been
providing HyperSpec access via EXT:CLHS for the last couple of versions.


Paolo
--
EncyCMUCLopedia * Extensive collection of CMU Common Lisp documentation
http://www.paoloamoroso.it/ency/README
[http://cvs2.cons.org:8000/cmucl/doc/EncyCMUCLopedia/]

Zachary Turner

unread,
Mar 31, 2002, 2:00:13 PM3/31/02
to

"Erik Naggum" <er...@naggum.net> wrote in message
news:32265398...@naggum.net...

Thanks for the link, looks like the HyperSpec will come in handy. My
question now is how natural is it to use return? One poster mentioned it is
a bit unusual to use return and then a few people mentioned cond being the
generally accepted way to test multiple conditions and that in the case of
parameter validation cond would be a good choice. So I'm wondering overall
how much is return actually used?

Thanks


Coby Beck

unread,
Mar 31, 2002, 3:34:21 PM3/31/02
to

"Zachary Turner" <ztu...@bindview.com> wrote in message
news:11Jp8.105423$Vl.36...@typhoon.austin.rr.com...

> Thanks for the link, looks like the HyperSpec will come in handy. My
> question now is how natural is it to use return? One poster mentioned it
is
> a bit unusual to use return and then a few people mentioned cond being the
> generally accepted way to test multiple conditions and that in the case of
> parameter validation cond would be a good choice. So I'm wondering
overall
> how much is return actually used?

In terms of parameter validation, another common idiom would be to throw an
exception.

(defun foo (arg)
(when (whacky? arg)
(error 'whacky-args :arg arg :problem "it's whacky"))
(normal code here ....))

If you don't want that and a nil returned is what callers expect as
exceptional then return-from seems appropriate though it is definately
simply a matter of style:

(defun foo (arg)
(when (sane arg) ;; same as (unless (not (whacky? arg))..)
(normal code here ...)))
vs.
(defun foo (arg)
(when (whacky? arg)
(return-from foo nil))
(normal code here...))

I would use the latter if the depth of nesting started to interfere with
readability and to emphasis the exceptional nature of a whacky arg. People
have differing opinions on whether it is proper to use WHEN and UNLESS for
the return values.

--
Coby Beck
(remove #\Space "coby 101 @ bigpond . com")


Erik Naggum

unread,
Mar 31, 2002, 7:45:31 PM3/31/02
to
* Zachary Turner

| My question now is how natural is it to use return?

Well, there are a few hints. Many forms set up a block named nil from
which you can return with a simple return instead of return-from. This
is particularly prevalent for the iteration constructs do, do*, dolist,
and loop, so you can break out of a loop early. This is much cleaner
than using an extra variable of heavier mechanisms locally.

| So I'm wondering overall how much is return actually used?

I think you should worry about this later and focus on learning the
language and finding uses of the constructs you learn. Knowing when to
use a construct is knowledge. Knowing when not to use a construct is
wisdom. If you merely collect knowledge without much understanding, you
will at least be skillful. If you merely collect wisdom without much
understanding, you will only be immobilized.

Joe Marshall

unread,
Mar 30, 2002, 10:59:45 PM3/30/02
to
You can use RETURN or RETURN-FROM

(defun basic-function ()
(when (> 4 0) (return t))
....)

Another idiom that is handy is using AND.
AND executes the subforms in turn and returns
NIL as soon as any subform returns NIL. If none
of them return NIL, then the value of the last
subform is returned.

(defun foo (x)
(and (satisfies-condition-1 x)
(satisfies-condition-2 x)
(compute-something x)))

This will return NIL if x doesn't satisfy the
conditions, but will call compute-something if it
does.

If you are doing parameter validation, consider
CHECK-TYPE.

(defun foo (x)
(check-type x (integer 3 8))
....)

Not only will this barf if x is not in [3,8], but
it will enter the error handler and allow you to
fix it and proceed.


> If it is the case that LISP does not allow
> this, would somebody mind explaining the motivation behind this? It must
> have some advantages which I am not aware of.

Lisp allows just about anything you can come up with.
On the other hand, there might be good reason for not
using a non-local exit. It might not be immediately
obvious that control could `jump out'.

(progn
(validate-something)
(really hairy calculation
( many levels deep
(.....)))
(print "Hey, it's done!"))

If you didn't spot the RETURN statement buried in the
hairy code, you might be surprised to find that function
returned without printing anything.

Takehiko Abe

unread,
Apr 1, 2002, 9:24:42 PM4/1/02
to
"Joe Marshall" wrote:

> You can use RETURN or RETURN-FROM
>
>
> (defun basic-function ()
> (when (> 4 0) (return t))
> ....)

RETURN requires block nil.

Steve Long

unread,
Apr 1, 2002, 12:55:30 PM4/1/02
to

Zachary Turner wrote:

> I'm new to LISP

Yes...

> This function will return t. I would like it to return nil however.
> Clearly I can rewrite the function as:
>
> (defun basic-function()
> (if (> 4 0)
> nil
> (if (> 4 0)
> t)))
>
> but now I've added a seemingly unnecessary level of nesting.

Not really. This is generally the way things are done in Lisp.

> you may want to do some complex parameter validation and immediately return
> if the
> parameters fail to be validated. Without multiple points of return I think
> you will be forced to either write a function to do your validation and
> simply call that function

The main problem is that you are thinking like a C or Java programmer and
using a language that frees you from curly braces. You can handle the
multi-case scenario just like you' ve stated using Lisp functions, macros and
special forms, and even pop out of your function with things like ERROR and
RETURN.


sal


0 new messages