From: Szabolcs Rozsnyai
Subject: DEFUN list argument
Date: 
Message-ID: <Xns94A6F3730473rozsnyaigmxat@195.34.132.18>
HI!

I'm new to Common Lisp and I have a question to a problem I couldn't figure 
out.

Is it possible to define a function which takes as arguments two lists, 
each of three numbers?

Something like: (DEFUN xxx (LIST(A B C) LIST(X Y Z))) 

Or do I have to count the length of each list before I proceed?


Thanks in advance

Szabolcs

From: Jeremy Yallop
Subject: Re: DEFUN list argument
Date: 
Message-ID: <slrnc4purd.13q.jeremy@kaje.cl.cam.ac.uk>
Szabolcs Rozsnyai wrote:
> Is it possible to define a function which takes as arguments two lists, 
> each of three numbers?
> 
> Something like: (DEFUN xxx (LIST(A B C) LIST(X Y Z))) 

Are you looking for something like

   (defun xxx (&rest args)
     (destructuring-bind ((a b c) (x y z))
       args
       ;; use a, b, &c.
      ))

?
 
Jeremy.
From: Szabolcs Rozsnyai
Subject: Re: DEFUN list argument
Date: 
Message-ID: <Xns94A725D2877Drozsnyaigmxat@195.34.132.18>
Jeremy Yallop <······@jdyallop.freeserve.co.uk> wrote in 
··························@kaje.cl.cam.ac.uk:


> Are you looking for something like
> 
>    (defun xxx (&rest args)
>      (destructuring-bind ((a b c) (x y z))
>        args
>        ;; use a, b, &c.
>       ))
>
ufff ... that's too high for me at this point 
is there a simpler way? :-)

I just started to learn Lisp and wanted to know why it is not possible to 
define two lists with limited length as arguments.

Szabolcs
From: Peter Seibel
Subject: Re: DEFUN list argument
Date: 
Message-ID: <m3n06qkjr3.fsf@javamonkey.com>
Szabolcs Rozsnyai <··········@gmx.at> writes:

> Jeremy Yallop <······@jdyallop.freeserve.co.uk> wrote in 
> ··························@kaje.cl.cam.ac.uk:
>
>
>> Are you looking for something like
>> 
>>    (defun xxx (&rest args)
>>      (destructuring-bind ((a b c) (x y z))
>>        args
>>        ;; use a, b, &c.
>>       ))
>>
> ufff ... that's too high for me at this point 
> is there a simpler way? :-)
>
> I just started to learn Lisp and wanted to know why it is not
> possible to define two lists with limited length as arguments.

Because Lisp is dynamically typed. To turn the question around, why do
you want to do this? Depending what your motivation is there is
probably some natural Lisp idiom to express it.

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Szabolcs Rozsnyai
Subject: Re: DEFUN list argument
Date: 
Message-ID: <Xns94A758CD6504rozsnyaigmxat@195.34.132.18>
> Because Lisp is dynamically typed. To turn the question around, why do
> you want to do this? Depending what your motivation is there is
> probably some natural Lisp idiom to express it.

I wanted to write a function and thought it can go easier 

Szabolcs
From: Peter Seibel
Subject: Re: DEFUN list argument
Date: 
Message-ID: <m3ishekih1.fsf@javamonkey.com>
Szabolcs Rozsnyai <··········@gmx.at> writes:

>> Because Lisp is dynamically typed. To turn the question around, why
>> do you want to do this? Depending what your motivation is there is
>> probably some natural Lisp idiom to express it.
>
> I wanted to write a function and thought it can go easier 

You're going to have to give us more than that. What was the function
supposed to do and why did you think this would make it easier?

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Szabolcs Rozsnyai
Subject: Re: DEFUN list argument
Date: 
Message-ID: <Xns94A78ED2986rozsnyaigmxat@195.34.132.18>
> You're going to have to give us more than that. What was the function
> supposed to do and why did you think this would make it easier?


I want to calculate the scalar product of 2 vectors with each of three 
coordinates.

Szabolcs
From: Steven E. Harris
Subject: Re: DEFUN list argument
Date: 
Message-ID: <q678yia5072.fsf@L75001820.us.ray.com>
Szabolcs Rozsnyai <··········@gmx.at> writes:

> I want to calculate the scalar product of 2 vectors with each of
> three coordinates.

You could try something simple like this for two vectors of any
similar dimension:

(defun dot-product (u v)
  (loop for elem-u across u
        for elem-v across v
        summing (* elem-u elem-v)))


> (dot-product (vector 1 2 3)
               (vector 4 5 6))
32

There are probably various optimizations one could make if the
dimensions and types are known ahead of time (loop unrolling, svref).

-- 
Steven E. Harris        :: ········@raytheon.com
Raytheon                :: http://www.raytheon.com
From: ··········@YahooGroups.Com
Subject: Re: DEFUN list argument
Date: 
Message-ID: <REM-2004mar08-001@Yahoo.Com>
> Date: Mon, 08 Mar 2004 23:52:40 GMT
> From: Szabolcs Rozsnyai <··········@gmx.at>
> I want to calculate the scalar product of 2 vectors with each of
> three coordinates.

Why go to all the trouble to write crippled code that can handle only
3-vectors, not any other size vectors, when it's easier to program the
more general case of any-length vectors? I suggest, when learning LISP,
to get in the habit of making your code as general as it naturally is
when you just program in the obvious way, i.e.:
(defun scalar-product (v w)
  (if (and (consp v) (consp w))
      (+ (* (first v) (first w)) (scalar-product (rest v) (rest w)))
    0))

With mapping/loop functions/specialForms, you can write it more
compactly, but since you're just getting started I recommend you try it
like above first and then learn the mapping/loop functions as an
alternative, so you understand both ways of doing it.
From: Barry Margolin
Subject: Re: DEFUN list argument
Date: 
Message-ID: <barmar-4F431E.22130508032004@comcast.ash.giganews.com>
In article <·················@Yahoo.Com>, ··········@YahooGroups.Com 
wrote:

> > Date: Mon, 08 Mar 2004 23:52:40 GMT
> > From: Szabolcs Rozsnyai <··········@gmx.at>
> > I want to calculate the scalar product of 2 vectors with each of
> > three coordinates.
> 
> Why go to all the trouble to write crippled code that can handle only
> 3-vectors, not any other size vectors, when it's easier to program the
> more general case of any-length vectors?

My guess is that these 3-vectors are the representation of some 
application-specific data, for which there will always be three 
components (e.g. he's modeling movement in space, so they're the x, y, 
and z components of a position, velocity, or acceleration vector), and 
generality isn't important.

In this case, it's often more appropriate to use structures rather than 
lists, so that the references to the components will indicate their 
meaning.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: Wade Humeniuk
Subject: Re: DEFUN list argument
Date: 
Message-ID: <Fem3c.174785$Hy3.125998@edtnps89>
Barry Margolin wrote:

>>Why go to all the trouble to write crippled code that can handle only
>>3-vectors, not any other size vectors, when it's easier to program the
>>more general case of any-length vectors?
> 
> 
> My guess is that these 3-vectors are the representation of some 
> application-specific data, for which there will always be three 
> components (e.g. he's modeling movement in space, so they're the x, y, 
> and z components of a position, velocity, or acceleration vector), and 
> generality isn't important.
> 
> In this case, it's often more appropriate to use structures rather than 
> lists, so that the references to the components will indicate their 
> meaning.
> 

This is the great thing about CL, you can have it both ways

(defstruct 3d-vector x y z)

(defmethod scalar-product ((u 3d-vector) (v 3d-vector))
   (+ (* (3d-vector-x u) (3d-vector-x v))
      (* (3d-vector-y u) (3d-vector-y v))
      (* (3d-vector-z u) (3d-vector-z v))))

(defmethod scalar-product ((u vector) (v vector))
   (loop for ui across u
         for vi across v
         summing (* ui vi)))

(defmethod scalar-product ((u cons) (v cons))
   (loop for ui in u
         for vi in v
         summing (* ui vi)))

CL-USER 5 > (scalar-product #S(3d-vector :x 1 :y 2 :z 3)
                             #S(3d-vector :x 10 :y 11 :z 12))
68

CL-USER 6 > (scalar-product #(1 2 3) #(10 11 12))
68

CL-USER 7 > (scalar-product '(1 2 3) '(10 11 12))
68

CL-USER 8 >

Wade
From: Marco Antoniotti
Subject: Re: DEFUN list argument
Date: 
Message-ID: <BC%3c.94$IJ5.76886@typhoon.nyu.edu>
Wade Humeniuk wrote:
>>
> 
> This is the great thing about CL, you can have it both ways
> 
> (defstruct 3d-vector x y z)
> 
> (defmethod scalar-product ((u 3d-vector) (v 3d-vector))
>   (+ (* (3d-vector-x u) (3d-vector-x v))
>      (* (3d-vector-y u) (3d-vector-y v))
>      (* (3d-vector-z u) (3d-vector-z v))))
> 
> (defmethod scalar-product ((u vector) (v vector))
>   (loop for ui across u
>         for vi across v
>         summing (* ui vi)))
> 
> (defmethod scalar-product ((u cons) (v cons))
>   (loop for ui in u
>         for vi in v
>         summing (* ui vi)))
> 

You actually can have it other ways as well

(defmethod scalar-product ((u vector) (v cons))
    (loop for ui across u
          for vi in v
          summing (* ui vi)))

(defmethod scalar-product ((u cons) (v vector))
    #| same code |# )


etc etc etc etc etc

It would be nice to have a way to specify when a dyadic method is 
commutative, to avoid extra coding.


Cheers
--
Marco
From: Alan Crowe
Subject: Re: DEFUN list argument
Date: 
Message-ID: <861xnz2erx.fsf@cawtech.freeserve.co.uk>
Marco Antoniotti commented:
> It would be nice to have a way to specify when a dyadic
> method is commutative, to avoid extra coding.

Does this do what you want?

(defmacro defcommute (method-name (left right) &rest body)
  `(progn (defmethod ,method-name (,left ,right) ,@body)
	 (defmethod ,method-name (,right ,left) ,@body)))

Sample use:

(defcommute x-len ((x cons)(y vector))
  (* (length x)
     (length y)))

(x-len #(1 2 3) '(a b)) => 6
(x-len '(a b) #(1 2 3)) => 6

Alan Crowe
Edinburgh
Scotland
From: Marco Antoniotti
Subject: Re: DEFUN list argument
Date: 
Message-ID: <YY54c.97$IJ5.77176@typhoon.nyu.edu>
Well, yes, something along those lines...  I was thinking of

	(defmethod x-len :commutative ((x cons) (y vector)) ...)



Alan Crowe wrote:
> Marco Antoniotti commented:
> 
>>It would be nice to have a way to specify when a dyadic
>>method is commutative, to avoid extra coding.
> 
> 
> Does this do what you want?
> 
> (defmacro defcommute (method-name (left right) &rest body)
>   `(progn (defmethod ,method-name (,left ,right) ,@body)
> 	 (defmethod ,method-name (,right ,left) ,@body)))
> 
> Sample use:
> 
> (defcommute x-len ((x cons)(y vector))
>   (* (length x)
>      (length y)))
> 
> (x-len #(1 2 3) '(a b)) => 6
> (x-len '(a b) #(1 2 3)) => 6
> 
> Alan Crowe
> Edinburgh
> Scotland
From: Wade Humeniuk
Subject: Re: DEFUN list argument
Date: 
Message-ID: <Lu93c.40369$n17.26370@clgrps13>
Szabolcs Rozsnyai wrote:
>>You're going to have to give us more than that. What was the function
>>supposed to do and why did you think this would make it easier?
> 
> 
> 
> I want to calculate the scalar product of 2 vectors with each of three 
> coordinates.
> 
> Szabolcs

or

(defun scalar-product (u v)
   (map 'vector #'* u v))

CL-USER 4 > (scalar-product #(1 2 3) #(10 13 17))
#(10 26 51)

CL-USER 5 >

Wade
From: Wade Humeniuk
Subject: Re: DEFUN list argument
Date: 
Message-ID: <ez93c.40372$n17.8505@clgrps13>
Wade Humeniuk wrote:

> Szabolcs Rozsnyai wrote:
> 
>>> You're going to have to give us more than that. What was the function
>>> supposed to do and why did you think this would make it easier?
>>
>>
>>
>>
>> I want to calculate the scalar product of 2 vectors with each of three 
>> coordinates.
>>
>> Szabolcs
> 
> 
> or
> 
> (defun scalar-product (u v)
>   (map 'vector #'* u v))
> 
> CL-USER 4 > (scalar-product #(1 2 3) #(10 13 17))
> #(10 26 51)
> 
> CL-USER 5 >
> 
> Wade
> 
  Oops, is scalar product sum of products??  If it is should be

(defun scalar-product (u v)
   (apply #'+ (map 'list #'* u v)))

CL-USER 7 > (scalar-product #(1 2 3) #(10 13 17))
87


Wade
From: Lars Brinkhoff
Subject: Re: DEFUN list argument
Date: 
Message-ID: <85wu5ul8tz.fsf@junk.nocrew.org>
Wade Humeniuk <····································@telus.net> writes:
> > Szabolcs Rozsnyai wrote:
> >> I want to calculate the scalar product of 2 vectors with each of
> >> three coordinates.
> (defun scalar-product (u v)
>    (apply #'+ (map 'list #'* u v)))

This may not work for large vectors.  Here's an alternative:

(defun scalar-product (u v)
  (reduce #'+ (map 'list #'* u v)))

-- 
Lars Brinkhoff,         Services for Unix, Linux, GCC, HTTP
Brinkhoff Consulting    http://www.brinkhoff.se/
From: Andreas Eder
Subject: Re: DEFUN list argument
Date: 
Message-ID: <m3d67mxq7o.fsf@banff.eder.de>
Lars Brinkhoff <·········@nocrew.org> writes:

> This may not work for large vectors.  Here's an alternative:
>
> (defun scalar-product (u v)
>   (reduce #'+ (map 'list #'* u v)))
>
Why make the intermediate a list? I would have thought a vector to be
the obvious choice here?

Andreas
-- 
Wherever I lay my .emacs, there's my $HOME.
From: Joe Marshall
Subject: Re: DEFUN list argument
Date: 
Message-ID: <r7w2jmsg.fsf@comcast.net>
Andreas Eder <············@t-online.de> writes:

> Lars Brinkhoff <·········@nocrew.org> writes:
>
>> This may not work for large vectors.  Here's an alternative:
>>
>> (defun scalar-product (u v)
>>   (reduce #'+ (map 'list #'* u v)))
>>
> Why make the intermediate a list? I would have thought a vector to be
> the obvious choice here?

Do it with series and there is no intermediate value:

  (defun scalar-product (u v)
    (collect-sum (#m * (scan 'vector u) (scan 'vector v))))

-- 
~jrm
From: Rahul Jain
Subject: Re: DEFUN list argument
Date: 
Message-ID: <87r7vx8imr.fsf@nyct.net>
Joe Marshall <·············@comcast.net> writes:

> Do it with series and there is no intermediate value:

Steal my thunder, why don't you?

pfft.

-- 
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
From: Lars Brinkhoff
Subject: Re: DEFUN list argument
Date: 
Message-ID: <85smgil1wk.fsf@junk.nocrew.org>
Andreas Eder <············@t-online.de> writes:
> Lars Brinkhoff <·········@nocrew.org> writes:
> > (defun scalar-product (u v)
> >   (reduce #'+ (map 'list #'* u v)))
> Why make the intermediate a list? I would have thought a vector to be
> the obvious choice here?

It wasn't a conscious choise, I just copied (map 'list ...) from the
previous post.

-- 
Lars Brinkhoff,         Services for Unix, Linux, GCC, HTTP
Brinkhoff Consulting    http://www.brinkhoff.se/
From: Frode Vatvedt Fjeld
Subject: Re: DEFUN list argument
Date: 
Message-ID: <2hwu5vdiwy.fsf@vserver.cs.uit.no>
Szabolcs Rozsnyai <··········@gmx.at> writes:

> I just started to learn Lisp and wanted to know why it is not
> possible to define two lists with limited length as arguments.

This is almost like saying "I'm just learning to write english, and I
want to know why I can't write `blurghzupphy'?" It's most likely
better to start out by learning the basic concepts before you wander
off asking "how do I ..?" questions.

-- 
Frode Vatvedt Fjeld
From: Kaz Kylheku
Subject: Re: DEFUN list argument
Date: 
Message-ID: <cf333042.0403091455.2e774c39@posting.google.com>
Szabolcs Rozsnyai <··········@gmx.at> wrote in message news:<····························@195.34.132.18>...
> Jeremy Yallop <······@jdyallop.freeserve.co.uk> wrote in 
> ··························@kaje.cl.cam.ac.uk:
> 
> 
> > Are you looking for something like
> > 
> >    (defun xxx (&rest args)
> >      (destructuring-bind ((a b c) (x y z))
> >        args
> >        ;; use a, b, &c.
> >       ))
> >
> ufff ... that's too high for me at this point 
> is there a simpler way? :-)

What could be simpler than cutting and pasting the above?
DESTRUCTURING-BIND  matches the structure of the list, and extracts
its constituents into variable bindings.

> I just started to learn Lisp and wanted to know why it is not possible to 
> define two lists with limited length as arguments.

What programming language were you using before that has this feature?

By default, Lisp has *weaker* argument checking than many languages,
because of its dynamically typed nature. What you are asknig for here
is for *stronger* checking on arguments than most languages have: you
are not just looking for checking type, but other properties such as
length.

One way is to define ``list of two elements'' as a type:

  ;;; This is a predicate function which tests whether
  ;;; the argument is a list of two integers.

  (defun list-of-two-integers-p (list)
    (and (listp list)
         (= (length list) 2)
         (every #'integerp list)))

  ;;; This DEFTYPE defines a new type whose domain is the set of
  ;;; all objects which satisfy the above predicate.

  (deftype list-of-two-integers () `(satisfies
list-of-two-integers-p))

Now you can use the standard CHECK-TYPE function to validate:

  (check-type '(1 2) list-of-two-integers) -> NIL  ; good, no error

  (check-type '(1 2 3) list-of-two-integers) --> <ERROR>

  (check-type '(a b) list-of-two-integers) --> <ERROR>

Now you can write a function like this:

  (defun xxx (list-1 list-2)
    (check-type list-1 list-of-two-integers)
    (check-type list-2 list-of-two-integers)
     ...)
From: Joe Marshall
Subject: Re: DEFUN list argument
Date: 
Message-ID: <eks1k2ul.fsf@comcast.net>
···@ashi.footprints.net (Kaz Kylheku) writes:

> One way is to define ``list of two elements'' as a type:
>
>   ;;; This is a predicate function which tests whether
>   ;;; the argument is a list of two integers.
>
>   (defun list-of-two-integers-p (list)
>     (and (listp list)
>          (= (length list) 2)
>          (every #'integerp list)))

Ouch!  What if you pass it a list of 100,000 items?  It traverses the
entire list and then discovers that 100,000 isn't equal to 2.


(defun list-of-two-integers-p (list)
  (and (consp list)
       (integerp (car list))
       (let ((tail (cdr list)))
         (and (consp tail)
              (integerp (car tail))
              (null (cdr tail))))))

-- 
~jrm
From: Barry Margolin
Subject: Re: DEFUN list argument
Date: 
Message-ID: <barmar-EDE2E5.01082010032004@comcast.ash.giganews.com>
In article <············@comcast.net>,
 Joe Marshall <·············@comcast.net> wrote:

> >   (defun list-of-two-integers-p (list)
> >     (and (listp list)
> >          (= (length list) 2)
> >          (every #'integerp list)))
> 
> Ouch!  What if you pass it a list of 100,000 items?  It traverses the
> entire list and then discovers that 100,000 isn't equal to 2.

So?  How often do you expect someone to pass the wrong length list?  Is 
it really important to handle rare, exceptional situations so 
efficiently?

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: Joe Marshall
Subject: Re: DEFUN list argument
Date: 
Message-ID: <ad2pj7f9.fsf@comcast.net>
Barry Margolin <······@alum.mit.edu> writes:

> In article <············@comcast.net>,
>  Joe Marshall <·············@comcast.net> wrote:
>
>> >   (defun list-of-two-integers-p (list)
>> >     (and (listp list)
>> >          (= (length list) 2)
>> >          (every #'integerp list)))
>> 
>> Ouch!  What if you pass it a list of 100,000 items?  It traverses the
>> entire list and then discovers that 100,000 isn't equal to 2.
>
> So?  How often do you expect someone to pass the wrong length list?  Is 
> it really important to handle rare, exceptional situations so 
> efficiently?

Good point.  If you are using it just for error checking I guess it
isn't that big a deal.

-- 
~jrm
From: Frode Vatvedt Fjeld
Subject: Re: DEFUN list argument
Date: 
Message-ID: <2hishcd9bx.fsf@vserver.cs.uit.no>
Joe Marshall <·············@comcast.net> writes:

[About (= (length list) 2)]

> Ouch!  What if you pass it a list of 100,000 items?  It traverses
> the entire list and then discovers that 100,000 isn't equal to 2.

Not strictly related to this thread, but:

Would it be allowed for a compiler to reduce (= (length list) 2) to
e.g. (and (cdr list) (not (cddr list))) ?

It seems to me that the (= (length list) 2) idiom is common enough
that it would be worthwhile for a compiler to do something like this.

-- 
Frode Vatvedt Fjeld
From: Christophe Rhodes
Subject: Re: DEFUN list argument
Date: 
Message-ID: <sqishcr8n7.fsf@lambda.dyndns.org>
Frode Vatvedt Fjeld <······@cs.uit.no> writes:

> Not strictly related to this thread, but:
>
> Would it be allowed for a compiler to reduce (= (length list) 2) to
> e.g. (and (cdr list) (not (cddr list))) ?

I think it's allowed.  (at least in regimes where LENGTH isn't
required to signal errors on malformed lists).

> It seems to me that the (= (length list) 2) idiom is common enough
> that it would be worthwhile for a compiler to do something like this.

Here, though, I'm more ambivalent.  The (= (length list) 2) is an O(N)
operation; its proposed replacement is O(1).  While this is going in
the right direction, it's not clear that protecting the user from
misunderstandings about asymptotic complexity is necessarily a win.
(I'd be slightly more inclined to signal a style-warning, because that
would at least educate the user :-)

Cheers,

Christophe
-- 
http://www-jcsu.jesus.cam.ac.uk/~csr21/       +44 1223 510 299/+44 7729 383 757
(set-pprint-dispatch 'number (lambda (s o) (declare (special b)) (format s b)))
(defvar b "~&Just another Lisp hacker~%")    (pprint #36rJesusCollegeCambridge)
From: Frode Vatvedt Fjeld
Subject: Re: DEFUN list argument
Date: 
Message-ID: <2h7jxsd6fn.fsf@vserver.cs.uit.no>
> Frode Vatvedt Fjeld <······@cs.uit.no> writes:

>> It seems to me that the (= (length list) 2) idiom is common enough
>> that it would be worthwhile for a compiler to do something like
>> this.

Christophe Rhodes <·····@cam.ac.uk> writes:

> Here, though, I'm more ambivalent.  The (= (length list) 2) is an
> O(N) operation; its proposed replacement is O(1).  While this is
> going in the right direction, it's not clear that protecting the
> user from misunderstandings about asymptotic complexity is
> necessarily a win.  (I'd be slightly more inclined to signal a
> style-warning, because that would at least educate the user :-)

These are pretty much my feelings too. But I also want to consider
that (= (length list) 2) is a very natural way to express "is this
list of length 2?", while (and (cdr list) (not (cddr list))) is not
easily readable or even writable.

-- 
Frode Vatvedt Fjeld
From: Russell McManus
Subject: Re: DEFUN list argument
Date: 
Message-ID: <874qswmsem.fsf@thelonious.dyndns.org>
Frode Vatvedt Fjeld <······@cs.uit.no> writes:

>> Frode Vatvedt Fjeld <······@cs.uit.no> writes:
>
>>> It seems to me that the (= (length list) 2) idiom is common enough
>>> that it would be worthwhile for a compiler to do something like
>>> this.
>
> Christophe Rhodes <·····@cam.ac.uk> writes:
>
>> Here, though, I'm more ambivalent.  The (= (length list) 2) is an
>> O(N) operation; its proposed replacement is O(1).  While this is
>> going in the right direction, it's not clear that protecting the
>> user from misunderstandings about asymptotic complexity is
>> necessarily a win.  (I'd be slightly more inclined to signal a
>> style-warning, because that would at least educate the user :-)
>
> These are pretty much my feelings too. But I also want to consider
> that (= (length list) 2) is a very natural way to express "is this
> list of length 2?", while (and (cdr list) (not (cddr list))) is not
> easily readable or even writable.

Here is a useful function from my personal library:

(defun list-of-length (n list)
  "Predicate which tests whether LIST is a list
of length N."
  (loop for i from 0 below n
        when (null list) do (return nil)
        do (pop list)
        finally (return (null list))))

-russ
From: Joe Marshall
Subject: Re: DEFUN list argument
Date: 
Message-ID: <65dck651.fsf@comcast.net>
Frode Vatvedt Fjeld <······@cs.uit.no> writes:

>> Frode Vatvedt Fjeld <······@cs.uit.no> writes:
>
>>> It seems to me that the (= (length list) 2) idiom is common enough
>>> that it would be worthwhile for a compiler to do something like
>>> this.
>
> Christophe Rhodes <·····@cam.ac.uk> writes:
>
>> Here, though, I'm more ambivalent.  The (= (length list) 2) is an
>> O(N) operation; its proposed replacement is O(1).  While this is
>> going in the right direction, it's not clear that protecting the
>> user from misunderstandings about asymptotic complexity is
>> necessarily a win.  (I'd be slightly more inclined to signal a
>> style-warning, because that would at least educate the user :-)
>
> These are pretty much my feelings too. But I also want to consider
> that (= (length list) 2) is a very natural way to express "is this
> list of length 2?", while (and (cdr list) (not (cddr list))) is not
> easily readable or even writable.

Use a macro.  (list-length= list 2)


-- 
~jrm
From: Pascal Bourguignon
Subject: Re: DEFUN list argument
Date: 
Message-ID: <87oer4zilw.fsf@thalassa.informatimago.com>
Joe Marshall <·············@comcast.net> writes:
> Frode Vatvedt Fjeld <······@cs.uit.no> writes:
> > Christophe Rhodes <·····@cam.ac.uk> writes:
> >
> >> Here, though, I'm more ambivalent.  The (= (length list) 2) is an
> >> O(N) operation; its proposed replacement is O(1).  While this is
> >> going in the right direction, it's not clear that protecting the
> >> user from misunderstandings about asymptotic complexity is
> >> necessarily a win.  (I'd be slightly more inclined to signal a
> >> style-warning, because that would at least educate the user :-)

Note that (length list) is also O(infinity)  (for circular lists).

> > These are pretty much my feelings too. But I also want to consider
> > that (= (length list) 2) is a very natural way to express "is this
> > list of length 2?", while (and (cdr list) (not (cddr list))) is not
> > easily readable or even writable.
> 
> Use a macro.  (list-length= list 2)

Use a compiler macro on = !



-- 
__Pascal_Bourguignon__                     http://www.informatimago.com/
There is no worse tyranny than to force a man to pay for what he doesn't
want merely because you think it would be good for him.--Robert Heinlein
http://www.theadvocates.org/
From: Marcin 'Qrczak' Kowalczyk
Subject: Re: DEFUN list argument
Date: 
Message-ID: <pan.2004.03.10.18.54.52.359126@knm.org.pl>
On Wed, 10 Mar 2004 16:40:11 +0000, Joe Marshall wrote:

> Use a macro.  (list-length= list 2)

It can easily be a function. The only reason for it to be a macro is
when a compiler is unable to optimize it (which it theoretically can).

-- 
   __("<         Marcin Kowalczyk
   \__/       ······@knm.org.pl
    ^^     http://qrnik.knm.org.pl/~qrczak/
From: Joe Marshall
Subject: Re: DEFUN list argument
Date: 
Message-ID: <oer4v686.fsf@comcast.net>
Marcin 'Qrczak' Kowalczyk <······@knm.org.pl> writes:

> On Wed, 10 Mar 2004 16:40:11 +0000, Joe Marshall wrote:
>
>> Use a macro.  (list-length= list 2)
>
> It can easily be a function. The only reason for it to be a macro is
> when a compiler is unable to optimize it (which it theoretically can).

In *theory*, yes, but it would involve unrolling a loop to some depth.

-- 
~jrm
From: a
Subject: Re: DEFUN list argument
Date: 
Message-ID: <e%K3c.70103$vn.205725@sea-read.news.verio.net>
"Joe Marshall" <·············@comcast.net> wrote in message
·················@comcast.net...
...
> Ouch!  What if you pass it a list of 100,000 items?  It traverses the
> entire list and then discovers that 100,000 isn't equal to 2.
>
>
> (defun list-of-two-integers-p (list)
>   (and (consp list)
>        (integerp (car list))
>        (let ((tail (cdr list)))
>          (and (consp tail)
>               (integerp (car tail))
>               (null (cdr tail))))))
>
> --
> ~jrm

Or

(defun list-of-n-types (list n type)
  (labels ((rec (list count)
             (cond ((= n count)
                    (if (null list)
                        t
                      nil))
                   ((consp list)
                    (if (typep (car list) type)
                        (rec (cdr list) (1+ count))
                      nil))
                   (t nil))))
    (if (consp list)
        (rec list 0)
      nil)))

CL-USER 76 > (list-of-n-types '(23 34) 2 'integer)
T

CL-USER 77 > (list-of-n-types '(23 34) 2 'character)
NIL

CL-USER 78 > (list-of-n-types '(23 34.0) 2 'integer)
NIL

CL-USER 79 > (list-of-n-types '(23 34) 2 'integer)
T

CL-USER 80 > (list-of-n-types '(23) 2 'integer)
NIL

CL-USER 81 > (list-of-n-types '(23 24 56) 2 'integer)
NIL

CL-USER 82 > (list-of-n-types 23 2 'integer)
NIL

CL-USER 83 > (list-of-n-types '(#\a #\b #\c #\d) 4 'character)
T
From: Kaz Kylheku
Subject: Re: DEFUN list argument
Date: 
Message-ID: <cf333042.0403111140.1877a1a@posting.google.com>
Joe Marshall <·············@comcast.net> wrote in message news:<············@comcast.net>...
> ···@ashi.footprints.net (Kaz Kylheku) writes:
> 
> > One way is to define ``list of two elements'' as a type:
> >
> >   ;;; This is a predicate function which tests whether
> >   ;;; the argument is a list of two integers.
> >
> >   (defun list-of-two-integers-p (list)
> >     (and (listp list)
> >          (= (length list) 2)
> >          (every #'integerp list)))
> 
> Ouch!  What if you pass it a list of 100,000 items?  It traverses the
> entire list and then discovers that 100,000 isn't equal to 2.

What, your compiler doesn't have a macro for

  (= (length <expr>) <constant-integer>)

?

(define-compiler-macro = (left right &whole form)
  (if (and (consp left)
           (eq (first left) 'length)
           (integerp right)
           (> right 0))
    (let ((obj-expr (second left))
          (obj-var (gensym)))
      `(let ((,obj-var ,obj-expr))
         (if (consp ,obj-var)
           (let ((tail (nthcdr ,(1- right) ,obj-var)))
             (and tail (null (cdr tail))))
           (= ,right (length ,obj-var)))))
    form))


:)

By the way the (= ,right (length ...)) is a hack to prevent infinite
recursion; just swap the arguments to change the syntax.
From: Duane Rettig
Subject: Re: DEFUN list argument
Date: 
Message-ID: <4smgf6q8u.fsf@franz.com>
···@ashi.footprints.net (Kaz Kylheku) writes:

> Joe Marshall <·············@comcast.net> wrote in message news:<············@comcast.net>...
> > ···@ashi.footprints.net (Kaz Kylheku) writes:
> > 
> > > One way is to define ``list of two elements'' as a type:
> > >
> > >   ;;; This is a predicate function which tests whether
> > >   ;;; the argument is a list of two integers.
> > >
> > >   (defun list-of-two-integers-p (list)
> > >     (and (listp list)
> > >          (= (length list) 2)
> > >          (every #'integerp list)))
> > 
> > Ouch!  What if you pass it a list of 100,000 items?  It traverses the
> > entire list and then discovers that 100,000 isn't equal to 2.
> 
> What, your compiler doesn't have a macro for
> 
>   (= (length <expr>) <constant-integer>)
> 
> ?
> 
> (define-compiler-macro = (left right &whole form)
>   (if (and (consp left)
>            (eq (first left) 'length)
>            (integerp right)
>            (> right 0))
>     (let ((obj-expr (second left))
>           (obj-var (gensym)))
>       `(let ((,obj-var ,obj-expr))
>          (if (consp ,obj-var)
>            (let ((tail (nthcdr ,(1- right) ,obj-var)))
>              (and tail (null (cdr tail))))
>            (= ,right (length ,obj-var)))))
>     form))
> 
> 
> :)

Don't know what the :) is for; the compiler-macro you wrote is
perfectly good, with the following notes:

 1. It would have to be on an = operator not in the common-lisp
package, since defining a compiler-macro for a CL symbol which
has a function binding is not allowed in portable code.

 2.  Be sure to add a (plusp right) to the initial and clause,
otherwise you'll be getting strange behavior from nth for the
form (= (length <expr>) -1)

 3. There is no reason not to define this compiler-macro in
a commutative manner, as long as argument evaluation orders
are preserved.

 4. (shameless plug): This compiler-macro would be a good candidate
for a &environment argument, to check out the types and ranges
of integers/conses at compile-time with variable-information...

> By the way the (= ,right (length ...)) is a hack to prevent infinite
> recursion; just swap the arguments to change the syntax.

-- 
Duane Rettig    ·····@franz.com    Franz Inc.  http://www.franz.com/
555 12th St., Suite 1450               http://www.555citycenter.com/
Oakland, Ca. 94607        Phone: (510) 452-2000; Fax: (510) 452-0182   
From: Christophe Rhodes
Subject: Re: DEFUN list argument
Date: 
Message-ID: <sq3c8fxe96.fsf@lambda.dyndns.org>
Duane Rettig <·····@franz.com> writes:

>  4. (shameless plug): This compiler-macro would be a good candidate
> for a &environment argument, to check out the types and ranges
> of integers/conses at compile-time with variable-information...

Ah, I've been meaning to ask you about that.  Consider the contrived
example

(defun foo (x list)
  (declare (type (unsigned-byte 32) x))
  (let ((y (ash x -32)))
    (= (length list) y)))

Does your model of environments and compiler-macros say anything about
optimization of the calls to = and LENGTH in FOO to (NULL LIST)?  

Christophe
-- 
http://www-jcsu.jesus.cam.ac.uk/~csr21/       +44 1223 510 299/+44 7729 383 757
(set-pprint-dispatch 'number (lambda (s o) (declare (special b)) (format s b)))
(defvar b "~&Just another Lisp hacker~%")    (pprint #36rJesusCollegeCambridge)
From: Duane Rettig
Subject: Re: DEFUN list argument
Date: 
Message-ID: <4k71qamxt.fsf@franz.com>
Christophe Rhodes <·····@cam.ac.uk> writes:

> Duane Rettig <·····@franz.com> writes:
> 
> >  4. (shameless plug): This compiler-macro would be a good candidate
> > for a &environment argument, to check out the types and ranges
> > of integers/conses at compile-time with variable-information...
> 
> Ah, I've been meaning to ask you about that.  Consider the contrived
> example
> 
> (defun foo (x list)
>   (declare (type (unsigned-byte 32) x))
>   (let ((y (ash x -32)))
>     (= (length list) y)))
> 
> Does your model of environments and compiler-macros say anything about
> optimization of the calls to = and LENGTH in FOO to (NULL LIST)?  

Yes, eventually, although you woudld also have to declare the list
variable to be of type list, as well.  If that is done, and if
type-propagation can be done at macroexpand/compiler-macrowexpand
time, then not only is the environment for x already noting that
it has (unsigned-byte 32) as its type, but it can also infer that
y has to be (integer 0 0), and the resultant body becomes (null list).
A compiler-macro for = could make this transformation, based on
the type information about the explicit or inferred declarations
of list and y, and based on its knowledge of what cl:length
does.

However, it's dangerous to do this across the board, because CL
programmers (including myself) sometimes tend to be sloppy in
declarations, calling out types that aren't really precise, and
thus "lying" to the compiler.  When the direction of the lie is
conservative, such as specifying "fixnum" when I really mean
"a very small integer", then the worst harm done might be to
generate too-generic code which might not be as fast.  However,
if the "lie" is in the wrong direction, such as specifying a
fixnum for a result that might, in very rare circumstances,
overflow to bignum, then such analyses and code collapsing
might make surprising assumptions that seem to the programmer
like a gigantic bug (when the only thing the compiler was doing
was to take your declarations literally).  So at least for
the present, we don't intend to do such optimizations.

-- 
Duane Rettig    ·····@franz.com    Franz Inc.  http://www.franz.com/
555 12th St., Suite 1450               http://www.555citycenter.com/
Oakland, Ca. 94607        Phone: (510) 452-2000; Fax: (510) 452-0182   
From: Peter Seibel
Subject: Re: DEFUN list argument
Date: 
Message-ID: <m3d67jumvj.fsf@javamonkey.com>
···@ashi.footprints.net (Kaz Kylheku) writes:

> Joe Marshall <·············@comcast.net> wrote in message news:<············@comcast.net>...
>> ···@ashi.footprints.net (Kaz Kylheku) writes:
>> 
>> > One way is to define ``list of two elements'' as a type:
>> >
>> >   ;;; This is a predicate function which tests whether
>> >   ;;; the argument is a list of two integers.
>> >
>> >   (defun list-of-two-integers-p (list)
>> >     (and (listp list)
>> >          (= (length list) 2)
>> >          (every #'integerp list)))
>> 
>> Ouch!  What if you pass it a list of 100,000 items?  It traverses the
>> entire list and then discovers that 100,000 isn't equal to 2.
>
> What, your compiler doesn't have a macro for
>
>   (= (length <expr>) <constant-integer>)
>
> ?
>
> (define-compiler-macro = (left right &whole form)
>   (if (and (consp left)
>            (eq (first left) 'length)
>            (integerp right)
>            (> right 0))
>     (let ((obj-expr (second left))
>           (obj-var (gensym)))
>       `(let ((,obj-var ,obj-expr))
>          (if (consp ,obj-var)
>            (let ((tail (nthcdr ,(1- right) ,obj-var)))
>              (and tail (null (cdr tail))))
>            (= ,right (length ,obj-var)))))
>     form))
>
>
> :)

Of course that puts you into undefined behavior territory since = is a
function in the COMMON-LISP package.

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Duane Rettig
Subject: Re: DEFUN list argument
Date: 
Message-ID: <4oer2ao0v.fsf@franz.com>
Peter Seibel <·····@javamonkey.com> writes:

> ···@ashi.footprints.net (Kaz Kylheku) writes:
> 
> > Joe Marshall <·············@comcast.net> wrote in message news:<············@comcast.net>...
> > What, your compiler doesn't have a macro for
> >
> >   (= (length <expr>) <constant-integer>)
> >
> > ?
> >
> > (define-compiler-macro = (left right &whole form)
> >   (if (and (consp left)
> >            (eq (first left) 'length)
> >            (integerp right)
> >            (> right 0))
> >     (let ((obj-expr (second left))
> >           (obj-var (gensym)))
> >       `(let ((,obj-var ,obj-expr))
> >          (if (consp ,obj-var)
> >            (let ((tail (nthcdr ,(1- right) ,obj-var)))
> >              (and tail (null (cdr tail))))
> >            (= ,right (length ,obj-var)))))
> >     form))
> >
> >
> > :)
> 
> Of course that puts you into undefined behavior territory since = is a
> function in the COMMON-LISP package.

But how do you know that that's what you're seeing here?  I didn't
see an in-package form in this example, so we really don't know
what package the #'= is in

:-)

-- 
Duane Rettig    ·····@franz.com    Franz Inc.  http://www.franz.com/
555 12th St., Suite 1450               http://www.555citycenter.com/
Oakland, Ca. 94607        Phone: (510) 452-2000; Fax: (510) 452-0182   
From: Kaz Kylheku
Subject: Re: DEFUN list argument
Date: 
Message-ID: <cf333042.0403121338.6b290a3@posting.google.com>
Peter Seibel <·····@javamonkey.com> wrote in message news:<··············@javamonkey.com>...
> ···@ashi.footprints.net (Kaz Kylheku) writes:
> > What, your compiler doesn't have a macro for
> >
> >   (= (length <expr>) <constant-integer>)
> >
> > ?
> >
> > (define-compiler-macro = (left right &whole form)

[snip]

> Of course that puts you into undefined behavior territory since = is a
> function in the COMMON-LISP package.

That's why I was careful to say ``your compiler'' not ``you''. :)
From: Lars Brinkhoff
Subject: Re: DEFUN list argument
Date: 
Message-ID: <85brn5kv03.fsf@junk.nocrew.org>
···@ashi.footprints.net (Kaz Kylheku) writes:
>   ;;; This DEFTYPE defines a new type whose domain is the set of
>   ;;; all objects which satisfy the above predicate.
>   (deftype list-of-two-integers () `(satisfies list-of-two-integers-p))

Or you could do something like

    (deftype list-of (length &optional (type '*))
      (if (zerop length)
          'null
          `(cons ,type (list-of ,(1- length) ,type))))

    (deftype list-of-two-integers () '(list-of 2 integer))

-- 
Lars Brinkhoff,         Services for Unix, Linux, GCC, HTTP
Brinkhoff Consulting    http://www.brinkhoff.se/
From: Kalle Olavi Niemitalo
Subject: Re: DEFUN list argument
Date: 
Message-ID: <87r7vzf09c.fsf@Astalo.kon.iki.fi>
Lars Brinkhoff <·········@nocrew.org> writes:

> Or you could do something like
>
>     (deftype list-of (length &optional (type '*))
>       (if (zerop length)
>           'null
>           `(cons ,type (list-of ,(1- length) ,type))))

Add (check-type length (integer 0 *)) to be safe.

>     (deftype list-of-two-integers () '(list-of 2 integer))

I made a function that used (typep list 'list-of-two-integers),
compiled it with CMU Common Lisp CVS release-18e-branch + minimal
debian patches and compared the disassembly with Joe Marshall's
version from <·················@comcast.net>.  They were pretty
much equivalent, except branches were arranged differently.

One part of the code caught my eye.  CMUCL on x86 tags NIL as a
CONS, so CONSP cannot just rely on the tag bits; it must also
check separately for NIL:

     3AD:       CMP   ECX, #x2800000B        ; NIL
     3B3:       JEQ   L13

     3B5:       MOV   EAX, ECX
     3B7:       AND   AL, 7
     3B9:       CMP   AL, 3
     3BB:       JNE   L13

In this particular case however, the NIL check could be omitted,
as (car nil) is not an INTEGER.  In Joe Marshall's function,
this can easily be done by changing both CONSP calls to LISTP.
I don't think a similar change is possible in the TYPEP version.

I suppose such a change would make the function a little slower
for NIL and possibly faster for the successful cases.  Code size
would certainly go down, so it might be reasonable for a compiler
to do this when SPACE is important.
From: Joe Marshall
Subject: Re: DEFUN list argument
Date: 
Message-ID: <3c8eqpfi.fsf@comcast.net>
Kalle Olavi Niemitalo <···@iki.fi> writes:

> Lars Brinkhoff <·········@nocrew.org> writes:
>
>> Or you could do something like
>>
>>     (deftype list-of (length &optional (type '*))
>>       (if (zerop length)
>>           'null
>>           `(cons ,type (list-of ,(1- length) ,type))))
>
> Add (check-type length (integer 0 *)) to be safe.
>
>>     (deftype list-of-two-integers () '(list-of 2 integer))
>
> I made a function that used (typep list 'list-of-two-integers),
> compiled it with CMU Common Lisp CVS release-18e-branch + minimal
> debian patches and compared the disassembly with Joe Marshall's
> version from <·················@comcast.net>.  They were pretty
> much equivalent, except branches were arranged differently.
>
> One part of the code caught my eye.  CMUCL on x86 tags NIL as a
> CONS, so CONSP cannot just rely on the tag bits; it must also
> check separately for NIL:
>
>      3AD:       CMP   ECX, #x2800000B        ; NIL
>      3B3:       JEQ   L13
>
>      3B5:       MOV   EAX, ECX
>      3B7:       AND   AL, 7
>      3B9:       CMP   AL, 3
>      3BB:       JNE   L13

Interesting.  Allegro and Lispworks do better with CONSP.

-- 
~jrm
From: Szabolcs Rozsnyai
Subject: Re: DEFUN list argument
Date: 
Message-ID: <Xns94A9CF2FDAEF9rozsnyaigmxat@195.34.132.18>
> What could be simpler than cutting and pasting the above?
> DESTRUCTURING-BIND  matches the structure of the list, and extracts
> its constituents into variable bindings.

Well ... I want to know what the code does exactly  before I start to use 
it. Thank you for the long explanations 

Meanwhile I did it this way:

(DEFUN x (X Y)
  (IF (= (LENGTH X) 3)
      (IF (= (LENGTH Y) 3)
	 (+ (+ (* (CAR X) (CAR Y)) (*(CAR (CDR X)) (CAR (CDR Y)))) 
	    (*(CAR (CDR (CDR X))) (CAR (CDR (CDR Y)))))
	 NIL
      )
      NIL
  )
)

> What programming language were you using before that has this feature?

Mostly OO languages...

Thanks again 

Szabolcs 
From: Coby Beck
Subject: Re: DEFUN list argument
Date: 
Message-ID: <c2qmqg$77u$1@otis.netspace.net.au>
"Szabolcs Rozsnyai" <··········@gmx.at> wrote in message
··································@195.34.132.18...
> Meanwhile I did it this way:
>
> (DEFUN x (X Y)
>   (IF (= (LENGTH X) 3)
>       (IF (= (LENGTH Y) 3)
> (+ (+ (* (CAR X) (CAR Y)) (*(CAR (CDR X)) (CAR (CDR Y))))
>     (*(CAR (CDR (CDR X))) (CAR (CDR (CDR Y)))))
> NIL
>       )
>       NIL
>   )
> )

How about:

(defun x (arg1 arg2)
  (assert (= 3 (length arg1) (length arg2)))
  (+ (* (first  arg1) (first  arg2))
     (* (second arg1) (second arg2))
     (* (third  arg1) (third  arg2))))

which is a short hop to:

(defun x (arg1 arg2)
  (assert (= 3 (length arg1) (length arg2)))
  (apply #'+ (mapcar #'* arg1 arg2)))


> > What programming language were you using before that has this feature?
>
> Mostly OO languages...

Make sure you don't miss the good OO solutions posted to this thread!

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