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

Problem with a named let.

97 views
Skip to first unread message

Jordan Katz

unread,
Jul 17, 2001, 9:25:12 PM7/17/01
to
Hi,

I was thinking of the possible ways to define my own length function
(to get a better understanding of the language) and decided to use a
named let:

(defun my-length (lst)
(let cnt ((len 0))
(if (null lst)
len
(progn (setf lst (cdr lst))
(cnt (+ 1 len))))))

I get the error: CNT is not of type LIST. I don't understand what's
causing this; as far as I can tell my usage of the named let and the
rest of the constructs is correct. Can someone explain?

P.S.
I realize the best way to do this would be a tail-recursive function
using `do' but that's not the point.

Thanks a lot,
--
Jordan Katz <ka...@underlevel.net> | Mind the gap

Kaz Kylheku

unread,
Jul 17, 2001, 9:47:58 PM7/17/01
to
In article <m38zhnt...@underlevel.underlevel.net>, Jordan Katz wrote:
>Hi,
>
> I was thinking of the possible ways to define my own length function
> (to get a better understanding of the language) and decided to use a
> named let:
>
>(defun my-length (lst)
> (let cnt ((len 0))

^^^^^^^
Bad syntax here. You want

(let (cnt (len 0))


> (if (null lst)
> len
> (progn (setf lst (cdr lst))
> (cnt (+ 1 len))))))

^^^^^^

This is still a problem, because cnt is not a function.

Also, you are going to need some kind of iteration or recursion to
actually find the length of the list. ;) Easily done without any
local variables:

(defun my-length (lst)
(if (null lst)
0
(1+ (my-length (rest lst)))))



>I realize the best way to do this would be a tail-recursive function
>using `do' but that's not the point.

You mean either recursion, or iteration with do?

Jordan Katz

unread,
Jul 17, 2001, 10:10:57 PM7/17/01
to
k...@ashi.footprints.net (Kaz Kylheku) writes:

> In article <m38zhnt...@underlevel.underlevel.net>, Jordan Katz wrote:
> >Hi,
> >
> > I was thinking of the possible ways to define my own length function
> > (to get a better understanding of the language) and decided to use a
> > named let:
> >
> >(defun my-length (lst)
> > (let cnt ((len 0))
>
> ^^^^^^^
> Bad syntax here. You want
>
> (let (cnt (len 0))
>
>
> > (if (null lst)
> > len
> > (progn (setf lst (cdr lst))
> > (cnt (+ 1 len))))))
> ^^^^^^
>
> This is still a problem, because cnt is not a function.

I mistakingly thought that named leti is the same in Scheme and CL.
In Scheme it's valid to write:

(define (my-length (lst)
(let cnt ((len 0))
(if (null? lst)
len
(begin (set! lst (cdr lst))
(cnt (+ 1 len)))))))

and I thought this same concept would work in CL (after fixing all the
minor differences such as null?, begin, define, etc. of course.) I'll
look into some of my Lisp manuals for their explanation of named let.

> Also, you are going to need some kind of iteration or recursion to
> actually find the length of the list. ;) Easily done without any
> local variables:
>
> (defun my-length (lst)
> (if (null lst)
> 0
> (1+ (my-length (rest lst)))))
>
> >I realize the best way to do this would be a tail-recursive function
> >using `do' but that's not the point.

^^^^ I meant dolist



> You mean either recursion, or iteration with do?

An iterative version, as in:

(defun my-length (lst)
(let ((len 0))
(dolist (elem lst)
(setf len (1+ len)))
len))

Kent M Pitman

unread,
Jul 17, 2001, 10:14:48 PM7/17/01
to
Jordan Katz <ka...@underlevel.net> writes:

> I was thinking of the possible ways to define my own length function
> (to get a better understanding of the language) and decided to use a
> named let:
>
> (defun my-length (lst)
> (let cnt ((len 0))
> (if (null lst)
> len
> (progn (setf lst (cdr lst))
> (cnt (+ 1 len))))))
>
> I get the error: CNT is not of type LIST. I don't understand what's
> causing this; as far as I can tell my usage of the named let and the
> rest of the constructs is correct. Can someone explain?

Yes, you're apparently using Lisp and not Scheme.

Scheme has named LET, Lisp does not.

First, let's correct your use of Scheme. Then we'll translate to Lisp.
You should be recursing on both the list and the count, or neither. That
is, either
(let cnt ((len 0) (lst lst))
(if (null? lst) len
(cnt (1+ len) (cdr lst))))
or
(let ((len 0))
(let cnt ()
(if (null? lst) len
(begin (setf len (1+ len))
(setf lst (cdr lst))
(cnt)))))
Obviously, the latter is a little silly, but I point it out as an option.
The former would translate into Common Lisp as:

(defun my-length (list)
(flet ((count-length (list length-so-far)
(if (null list)
length-so-far
(count-length (cdr length) (1+ length-so-far)))))
(count-length list 0)))

> P.S.
> I realize the best way to do this would be a tail-recursive function
> using `do' but that's not the point.

No, that's not right. DO is certainly a good thing to use but in Common Lisp
it works by iteration, not "tail recursion". In general, Common Lisp
(unlike Scheme) does not guarantee to use constant stack even on tail calls,
so you don't want to use it for things that might iterate for an arbitrarily
large distance. Use iteration (DO, DOLIST, LOOP, etc.) or some lower level
operator (TAGBODY or PROG) that does not make new stack on every iteration.

Friedrich Dominicus

unread,
Jul 18, 2001, 2:51:34 AM7/18/01
to

>
> (defun my-length (list)
> (flet ((count-length (list length-so-far)
> (if (null list)
> length-so-far
> (count-length (cdr length) (1+ length-so-far)))))
> (count-length list 0)))

could it be that you have had a bad day? The above code does not work
what is (cdr length)? And what about the usage for flet?

So here's a hopefully working version:
(defun my-length (list)
(labels ((count-length


(list length-so-far)
(if (null list)
length-so-far

(count-length (cdr list) (1+ length-so-far)))))
(count-length list 0)))


Regards
Friedrich

Kent M Pitman

unread,
Jul 18, 2001, 8:46:10 AM7/18/01
to
"Friedrich Dominicus" <fr...@q-software-solutions.com> writes:

> > (defun my-length (list)
> > (flet ((count-length (list length-so-far)
> > (if (null list)
> > length-so-far
> > (count-length (cdr length) (1+ length-so-far)))))
> > (count-length list 0)))
>
> could it be that you have had a bad day? The above code does not work
> what is (cdr length)? And what about the usage for flet?

funny, i had labels in an earlier edit and it got changed. c'est la vie.
thanks for catching it, and the other typo too. sigh.

Marco Antoniotti

unread,
Jul 18, 2001, 11:38:01 AM7/18/01
to

Jordan Katz <ka...@underlevel.net> writes:

> Hi,
>
> I was thinking of the possible ways to define my own length function
> (to get a better understanding of the language) and decided to use a
> named let:
>
> (defun my-length (lst)
> (let cnt ((len 0))
> (if (null lst)
> len
> (progn (setf lst (cdr lst))
> (cnt (+ 1 len))))))
>
> I get the error: CNT is not of type LIST. I don't understand what's
> causing this; as far as I can tell my usage of the named let and the
> rest of the constructs is correct. Can someone explain?

You are using a "named let", a Scheme feature that is needed to deal
with the lacks of the Scheme standard.

Alas, the "named let" feature is nice. In CL a good approximation is

(defmacro named-let (name bindings &body forms)
`(labels ((,name ,(mapcar #'first bindings)
,@forms))
(,name ,@(mapcar #'second bindings))))

Hence your code becomes

(defun my-length (list) ; In CL you can use meaningful names :)
(named-let cnt ((len 0))
(if (null list)
len
(progn (setf list (rest list))
(cnt (1+ len))))))

This is also tail-recursive. If your underlying CL does tail-call
elimination you are home free,

Cheers

--
Marco Antoniotti ========================================================
NYU Courant Bioinformatics Group tel. +1 - 212 - 998 3488
719 Broadway 12th Floor fax +1 - 212 - 995 4122
New York, NY 10003, USA http://bioinformatics.cat.nyu.edu
"Hello New York! We'll do what we can!"
Bill Murray in `Ghostbusters'.

Thomas F. Burdick

unread,
Jul 18, 2001, 2:10:01 PM7/18/01
to
Kent M Pitman <pit...@world.std.com> writes:

> No, that's not right. DO is certainly a good thing to use but in Common Lisp
> it works by iteration, not "tail recursion". In general, Common Lisp
> (unlike Scheme) does not guarantee to use constant stack even on tail calls,
> so you don't want to use it for things that might iterate for an arbitrarily
> large distance. Use iteration (DO, DOLIST, LOOP, etc.) or some lower level
> operator (TAGBODY or PROG) that does not make new stack on every iteration.

However, it wouldn't hurt to find out if/when your implementation
optimizes tail calls. Once in a long while, I'll find a
tail-recursive version of a function to be easier to read than the
iterative version (mind you, this is quite infrequent). In those
cases, I'm glad to know how to ensure that my implementation will
optimize it correctly.

Kent M Pitman

unread,
Jul 18, 2001, 3:19:29 PM7/18/01
to

Well, IMO, then you are not programming in Common Lisp. You are
programming in a vendor-specific dialect since your code may not port.

I guess people can disagree on the value of doing this, but personally
I don't recommend it when there's an equally good portable solution
available.

Personally, I've never met a tail recursive definition that I felt was
any more clear than an iterative one, properly expressed.

Marco Antoniotti

unread,
Jul 18, 2001, 3:40:29 PM7/18/01
to

Marco Antoniotti <mar...@cs.nyu.edu> writes:

Incidentally.....

Using the above macro would produce much much much better and more
readable CL code as a result of the Scheme->CL translator that has
been floating around (and which does a terrible job at translating all
the 'let loop' appearing in the typical Scheme program).

Tim Bradshaw

unread,
Jul 18, 2001, 3:54:16 PM7/18/01
to
* I wrote:

> However, it wouldn't hurt to find out if/when your implementation
> optimizes tail calls. Once in a long while, I'll find a
> tail-recursive version of a function to be easier to read than the
> iterative version (mind you, this is quite infrequent). In those
> cases, I'm glad to know how to ensure that my implementation will
> optimize it correctly.

So long as you don't mind your code blowing up in exciting ways when
you port it, I guess.

--tim

Erik Naggum

unread,
Jul 18, 2001, 4:50:33 PM7/18/01
to
* Tim Bradshaw <t...@cley.com>

> So long as you don't mind your code blowing up in exciting ways when
> you port it, I guess.

Is there a portable way to determine whether you have a tail-recursive
function or one that allocates a new stack frame for each recursion? Or
is this intended to be a true, i.e., completely transparent, optimization
that is only semi-determinable by crashes? It would be nice if there
were a portably implemented at best, portably interfaced at least,
function that would return the set of optimize declarations that would
cause a class of function calls that are statically and portably
determinable to merit tail-recursive calls.

Allegro CL's features is known as compiler:tail-call-self-merge-switch
and it is easy to see when it is true.

#:Erik
--
Travel is a meat thing.

Christophe Rhodes

unread,
Jul 18, 2001, 6:38:22 PM7/18/01
to
Erik Naggum <er...@naggum.net> writes:

> * Tim Bradshaw <t...@cley.com>
> > So long as you don't mind your code blowing up in exciting ways when
> > you port it, I guess.
>
> Is there a portable way to determine whether you have a tail-recursive
> function or one that allocates a new stack frame for each recursion? Or
> is this intended to be a true, i.e., completely transparent, optimization
> that is only semi-determinable by crashes? It would be nice if there
> were a portably implemented at best, portably interfaced at least,
> function that would return the set of optimize declarations that would
> cause a class of function calls that are statically and portably
> determinable to merit tail-recursive calls.

At the risk of sounding like a broken record, please do `join' the
common-lisp-utilities process[1]. I suppose (still having failed to
check my reference library) that this is the kind of thing that was
being referred to in the CLtL2 environment functionality that didn't
make it into the specification.

Christophe

[1] Yeech. That sounds all bureaucratic. Basically, there are three
stages: "It would be nice"; "Here's a reference implementation";
"Here's a reference implementation and specification". Community
standards, you know you like them... of course, this particular "it
would be nice" is of the more introspective nature, and so might
involve grovelling in the internals of implementations.
--
Jesus College, Cambridge, CB5 8BL +44 1223 510 299
http://www-jcsu.jesus.cam.ac.uk/~csr21/ (defun pling-dollar
(str schar arg) (first (last +))) (make-dispatch-macro-character #\! t)
(set-dispatch-macro-character #\! #\$ #'pling-dollar)

0 new messages