From: Dave
Subject: [newbie] about passing list/array to a function
Date: 
Message-ID: <89_5b.293531$lK4.9145152@twister1.libero.it>
Hello, world! :-)

I have some doubt about passing lists or array to a function.
for example,

(setf arry (make-array 1000 :initial-element NIL))
(setf list (list "a1" "a2" "a3" "a4" "a5"))

(defun myfun1 (myarray)
    (....))

(defun myfun2 (mylist)
    (....))

(myfun1 arry)
(myfun2 list)

This is a short example to give you an idea.
Array come passed by copying the full value into paramenter 
or it's passed as pointer?
Same thing about Lists..
Does exists the way to passing the pointer in place of value?
You look the real case below.

Thanks in advance for your help.
Dave

---------------------------------------------------------------
[THE REAL CASE]
This code below seems to be slow when running for more times and
I don't know the reason..
Does it depends on the passing of array?

(DEFVAR *arraydiz* (make-array 10000 :initial-element NIL))  ; array

(DEFUN lookup (cerca keys)
    (LET* ((count 0))
          (SETQ count (POSITION cerca keys :test 'EQUALP))
          (RETURN-FROM lookup (IF count (1+ count) 0))))

(DEFUN find-all (start keys array cols)
    (LET* ((ipos start)
           (alist NIL)
           (cerca "")
           (numcpo 0)
           (ppos 0))
        (LOOP
            (SETF alist (AREF array ipos))
            (IF (NOT alist)
                (RETURN))
            (DOLIST (sym cols)
                (SETF cerca (NTH (1- sym) alist))
                (IF (EQUAL cerca "")
                    (SETQ numcpo 0)
                    (SETQ numcpo (lookup cerca keys)))
                (WHEN (> numcpo 0)
                    (SETQ ppos sym
                          numcpo ipos)
                    (RETURN)))
            (IF (> numcpo 0) 
                (RETURN))
            (SETQ ipos (1+ ipos)))
        (LIST numcpo ppos)))

(load-array ....)

(SETQ result 
   (find-all 1 (LIST "key1" "key2") *arraydiz* '(1 4 7 8)))

(...)
(...)

From: Kent M Pitman
Subject: Re: [newbie] about passing list/array to a function
Date: 
Message-ID: <sfwd6efb8pf.fsf@shell01.TheWorld.com>
"Dave" <·········@iol.it> writes:

> Hello, world! :-)

Since I'm the one who's always posting from TheWorld.com, I suppose it's
my duty to say "hello" back. :)

Hello, Dave.
 
> I have some doubt about passing lists or array to a function. [...]
> Array come passed by copying the full value into paramenter 
> or it's passed as pointer?

In Lisp, every value and is an object addressed by pointer, so much so
that we do not ever call them pointers, since nothing is not a pointer.
We just call them all objects.

This is true for arrays, lists, symbols, etc. ... even numbers.

The operations on these "pointers to objects" are not what you would
classically think of as pointer operations in other languages, though.
One cannot do pointer arithmetic in Lisp. 

All Lisp operations indirect automatically through the pointer in
order to maintain the appropriate abstractions; no dereferencing is
required.  An operation like addition, for example, takes two pointers
to numbers and returns a pointer to the result number, so we usually
just say it takes numbers and returns numbers.

But when passing arguments, which was your question, just a pointer is
passed.  So in the classical vocabulary, Lisp is a call-by-value
language since copying occurs, but since only pointers are ever
passed, no pointed-to object is ever copied during function call.  We
usually prefer to think of it as the object's "identity" having been
passed, rather than a pointer.  It gives a more abstract feel, since
you don't need to know or care whether it's a machine pointer for the
object or the driver's license number of the object that is passed;
all you know is that it is a canonical reference that is passed.

Does that help?
From: Steven M. Haflich
Subject: Re: [newbie] about passing list/array to a function
Date: 
Message-ID: <ag36b.10568$vD1.3621@newssvr27.news.prodigy.com>
Kent M Pitman wrote:

> In Lisp, every value and is an object addressed by pointer, so much so
> that we do not ever call them pointers, since nothing is not a pointer.
> We just call them all objects.
> 
> This is true for arrays, lists, symbols, etc. ... even numbers.

This notion is correct in the intended spirit, but is in fact incorrect in
ways that are detectible but don't much matter in any practical sense.

To state that all lisp objects are passed by pointer (the nonimplementational
is "passed by reference") is to say that all objects are first class.  But
this is in fact not true for numbers and characters.  An implementation is
never allowed to make an `equivalent' copy of a lisp object -- _except_ for
numbers and characters.

This is actually an efficiency issue.  Some kinds of numbers and characters
have small, native machine-type representation that the system can sometimes
optimize if it doesn't need to preserve identity.  This is a reasonable
freedom for numbers and characters because these objects are immutable.
There is no way to modify a character or number.  The only way that the
non-first-class-ness of a number of character can be detected is that
(eq x y) is required always to be true if x and y are the same first-class
object.  But if x and y are bound to the same number or character, the
predicate _might_ be false.  The system is allowed to make non-eq copies
of numbers and characters whenever it likes.  The predicate (eql x y) will
always true, even for a number or character that may have been copied.

Here is a plausible situation where this could occur.  Assume we have an
implementation with 30-bit fixnums and a typical native 32-bit machine.

(defun my-identity (x) x)

(defun eq-test (x)
   (declare (type (unsigned-byte 31) x))
   (let* ((y (1+ x))
          (z (my-identity y)))
     (list (eq y z) (eql y z))))

Assume the compiler knows nothing about my-identity when it compiles eq-test.
The compiler might decide to use native unsigned 32-bit arithmetic for
the calculations on y, but needs to make a boxed bignum or fixnum copy of y
in order to pass it to my-identity.  It knows the value of y is a native
unsigned-byte integer, and it knows that z will be tagged and/or boxed.
The compilation of eql is responsible for performing the test that z is an
integer and comparing its magnitude with the different internal representation
of y, but the eq test can just punt and always return nil.  It knows the values
will never be the `same'.  Therefore, the result of calling this function with
an appropriate (unsigned-byte 31) argument may be either (nil t) or (t t).

The situation is probably more important to performance with floats, but
float comparisons have other issues that muddy the water.
From: Kent M Pitman
Subject: Re: [newbie] about passing list/array to a function
Date: 
Message-ID: <sfwptif2k99.fsf@shell01.TheWorld.com>
"Steven M. Haflich" <·················@alum.mit.edu> writes:

> To state that all lisp objects are passed by pointer (the nonimplementational
> is "passed by reference") is to say that all objects are first class.  But
> this is in fact not true for numbers and characters.  An implementation is
> never allowed to make an `equivalent' copy of a lisp object -- _except_ for
> numbers and characters.

Sure.  But I prefer to think of these as pointers which have no backing store.
So it's just a point of view thing.
From: Dave
Subject: Re: [newbie] about passing list/array to a function
Date: 
Message-ID: <Xg86b.295451$lK4.9206571@twister1.libero.it>
"Kent M Pitman" <······@world.std.com> ha scritto nel messaggio ····················@shell01.TheWorld.com...

[cut]

> ...
> But when passing arguments, which was your question, just a pointer is
> passed.  So in the classical vocabulary, Lisp is a call-by-value
> language since copying occurs, but since only pointers are ever
> passed, no pointed-to object is ever copied during function call.  We
> usually prefer to think of it as the object's "identity" having been
> passed, rather than a pointer.  It gives a more abstract feel, since
> you don't need to know or care whether it's a machine pointer for the
> object or the driver's license number of the object that is passed;
> all you know is that it is a canonical reference that is passed.

Yes!

Thanks you again.
Dave
From: Dave
Subject: Re: [newbie] about passing list/array to a function
Date: 
Message-ID: <Bm86b.295377$Ny5.9134736@twister2.libero.it>
I did forgot to rewrite "Does that help?".

Yes!

:-))
From: Marco Gidde
Subject: Re: [newbie] about passing list/array to a function
Date: 
Message-ID: <lz4qzr8lzf.fsf@tristan.br-automation.de>
"Dave" <·········@iol.it> writes:

> Hello, world! :-)
> 
> I have some doubt about passing lists or array to a function.
> for example,
> 
> (setf arry (make-array 1000 :initial-element NIL))
> (setf list (list "a1" "a2" "a3" "a4" "a5"))
> 
> (defun myfun1 (myarray)
>     (....))
> 
> (defun myfun2 (mylist)
>     (....))
> 
> (myfun1 arry)
> (myfun2 list)
> 
> This is a short example to give you an idea.
> Array come passed by copying the full value into paramenter 
> or it's passed as pointer?

Argument passing in CL is done by "lisp value", that means lists and
arrays and things like that are "passed by reference" (pointer) - they
are not copied! Maybe you should google around for a better
explanation.

> [THE REAL CASE]
> This code below seems to be slow when running for more times and
> I don't know the reason..
> Does it depends on the passing of array?
> 
> (DEFVAR *arraydiz* (make-array 10000 :initial-element NIL))  ; array
> 
> (DEFUN lookup (cerca keys)
>     (LET* ((count 0))
>           (SETQ count (POSITION cerca keys :test 'EQUALP))
>           (RETURN-FROM lookup (IF count (1+ count) 0))))
> 
> (DEFUN find-all (start keys array cols)
>     (LET* ((ipos start)
>            (alist NIL)
>            (cerca "")
>            (numcpo 0)
>            (ppos 0))
>         (LOOP
>             (SETF alist (AREF array ipos))
>             (IF (NOT alist)
>                 (RETURN))
>             (DOLIST (sym cols)
>                 (SETF cerca (NTH (1- sym) alist))
>                 (IF (EQUAL cerca "")
>                     (SETQ numcpo 0)
>                     (SETQ numcpo (lookup cerca keys)))
>                 (WHEN (> numcpo 0)
>                     (SETQ ppos sym
>                           numcpo ipos)
>                     (RETURN)))
>             (IF (> numcpo 0) 
>                 (RETURN))
>             (SETQ ipos (1+ ipos)))
>         (LIST numcpo ppos)))
> 
> (load-array ....)
> 
> (SETQ result 
>    (find-all 1 (LIST "key1" "key2") *arraydiz* '(1 4 7 8)))
> 
> (...)
> (...)

Since I did not write too much lisp code in my life it's not
appropriate to citicize your code, but nevertheless it looks a bit
strange.  

I did not test it, but the reason for its slowness are probably the
three nested loops:

1. LOOP 
2. DOLIST
3. LOOKUP, which uses POSITION on a large array; it has to iterate
   over every single element until one element satisfies the test 


Cheers,

Marco
From: Dave
Subject: Re: [newbie] about passing list/array to a function
Date: 
Message-ID: <Xg86b.295450$lK4.9206863@twister1.libero.it>
"Marco Gidde" <···········@tiscali.de> wrote:

[cut]

> > 
> > (load-array ....)
> > 
> > (SETQ result 
> >    (find-all 1 (LIST "key1" "key2") *arraydiz* '(1 4 7 8)))
> > 
> > (...)
> > (...)
> 
> Since I did not write too much lisp code in my life it's not
> appropriate to citicize your code, but nevertheless it looks a bit
> strange.  

It's not soo complex.
The "find-all" function simply scans an array of lists (italian grammar).
Each list is an element of the array.
Array is pre-ordered by group (3rd column)
for example:

....
....
("mio" "mio" "ap" "mia" "" "" "miei" "mie" "1") 
("tuo" "tuo" "ap" "tua" "" "" "tuoi" "tue" "2") 
("suo" "suo" "ap" "sua" "" "" "suoi" "sue" "3") 
("nostro" "nostro" "ap" "nostra" "" "" "nostri" "nostre" "4") 
("vostro" "vostro" "ap" "vostra" "" "" "vostri" "vostre" "5") 
("loro" "loro" "ap" "" "" "" "loro" "" "6") 
("il" "il" "ad" "la" "" "" "i" "le" "") 
("lo" "lo" "ad" "la" "" "" "gli" "le" "") 
....
....

> 
> I did not test it, but the reason for its slowness are probably the
> three nested loops:
> 
> 1. LOOP 
> 2. DOLIST
> 3. LOOKUP, which uses POSITION on a large array; it has to iterate
>    over every single element until one element satisfies the test 
> 

This nested loops are necessary for the scanning of the array.
Exists the way in order to make it more quickly?

Greetings
Dave
From: Marco Gidde
Subject: Re: [newbie] about passing list/array to a function
Date: 
Message-ID: <lz1xus2y6r.fsf@tristan.br-automation.de>
"Dave" <·········@iol.it> writes:

> > Since I did not write too much lisp code in my life it's not
> > appropriate to citicize your code, but nevertheless it looks a bit
> > strange.  
> 
> It's not soo complex.

I did not mean that it is too complex - it just does not look like
Lisp :-)

I try to rewrite it a bit, do a few general comments on the changes
and send it to your private mail address. Quasi from new newbie to
newbie :-)


Cheers,

Marco
From: Dave
Subject: Re: [newbie] about passing list/array to a function
Date: 
Message-ID: <0rO6b.300596$Ny5.9340068@twister2.libero.it>
>"Marco Gidde" <···········@tiscali.de> writes:
> "Dave" <·········@iol.it> writes:
> 
> > > Since I did not write too much lisp code in my life it's not
> > > appropriate to citicize your code, but nevertheless it looks a bit
> > > strange.  
> > 
> > It's not soo complex.
> 
> I did not mean that it is too complex - it just does not look like
> Lisp :-)

I have misunderstood, excuse me.
Bye.
Dave
From: Marco Antoniotti
Subject: Re: [newbie] about passing list/array to a function
Date: 
Message-ID: <3F58F049.1050301@cs.nyu.edu>
Dave wrote:
> Hello, world! :-)
> 
> I have some doubt about passing lists or array to a function.
> for example,
> 
> (setf arry (make-array 1000 :initial-element NIL))
> (setf list (list "a1" "a2" "a3" "a4" "a5"))
> 
> (defun myfun1 (myarray)
>     (....))
> 
> (defun myfun2 (mylist)
>     (....))
> 
> (myfun1 arry)
> (myfun2 list)
> 
> This is a short example to give you an idea.
> Array come passed by copying the full value into paramenter 
> or it's passed as pointer?
> Same thing about Lists..
> Does exists the way to passing the pointer in place of value?

You always pass an "invisible pointer" in Common Lisp (or you can call 
it an "object").  Anyway.  Whatever. :)





> You look the real case below.
> 
> Thanks in advance for your help.
> Dave
> 
> ---------------------------------------------------------------
> [THE REAL CASE]
> This code below seems to be slow when running for more times and
> I don't know the reason..
> Does it depends on the passing of array?
> 
> (DEFVAR *arraydiz* (make-array 10000 :initial-element NIL))  ; array
> 
> (DEFUN lookup (cerca keys)
>     (LET* ((count 0))
>           (SETQ count (POSITION cerca keys :test 'EQUALP))
>           (RETURN-FROM lookup (IF count (1+ count) 0))))

(defun lookup (key keys)
    (or (position key keys :test #'string-equal) 0))

Note that if your most important operation is LOOKUP, you may want to 
optimize it.  Searching linearly in an array is not the best way to do 
these things.  A HASH-TABLE or a tree structure has much better search 
characteristics than an array or a list given the use that you imply in 
your post.


> (DEFUN find-all (start keys array cols)
>     (LET* ((ipos start)
>            (alist NIL)
>            (cerca "")
>            (numcpo 0)
>            (ppos 0))
>         (LOOP
>             (SETF alist (AREF array ipos))
>             (IF (NOT alist)
>                 (RETURN))
>             (DOLIST (sym cols)
>                 (SETF cerca (NTH (1- sym) alist))
>                 (IF (EQUAL cerca "")
>                     (SETQ numcpo 0)
>                     (SETQ numcpo (lookup cerca keys)))
>                 (WHEN (> numcpo 0)
>                     (SETQ ppos sym
>                           numcpo ipos)
>                     (RETURN)))
>             (IF (> numcpo 0) 
>                 (RETURN))
>             (SETQ ipos (1+ ipos)))
>         (LIST numcpo ppos)))

What is 'find-all' supposed to do?  It is unclear what kind of data 
structure you are using and why

Cheers
--
Marco
From: Marco Antoniotti
Subject: Re: [newbie] about passing list/array to a function
Date: 
Message-ID: <3F58F0F5.3090503@cs.nyu.edu>
Marco Antoniotti wrote:
> 
> 
> Dave wrote:
> 
>> Hello, world! :-)
>>
>> I have some doubt about passing lists or array to a function.
>> for example,
>>
>> (setf arry (make-array 1000 :initial-element NIL))
>> (setf list (list "a1" "a2" "a3" "a4" "a5"))
>>
>> (defun myfun1 (myarray)
>>     (....))
>>
>> (defun myfun2 (mylist)
>>     (....))
>>
>> (myfun1 arry)
>> (myfun2 list)
>>
>> This is a short example to give you an idea.
>> Array come passed by copying the full value into paramenter or it's 
>> passed as pointer?
>> Same thing about Lists..
>> Does exists the way to passing the pointer in place of value?
> 
> 
> You always pass an "invisible pointer" in Common Lisp (or you can call 
> it an "object").  Anyway.  Whatever. :)
> 
> 
> 
> 
> 
>> You look the real case below.
>>
>> Thanks in advance for your help.
>> Dave
>>
>> ---------------------------------------------------------------
>> [THE REAL CASE]
>> This code below seems to be slow when running for more times and
>> I don't know the reason..
>> Does it depends on the passing of array?
>>
>> (DEFVAR *arraydiz* (make-array 10000 :initial-element NIL))  ; array
>>
>> (DEFUN lookup (cerca keys)
>>     (LET* ((count 0))
>>           (SETQ count (POSITION cerca keys :test 'EQUALP))
>>           (RETURN-FROM lookup (IF count (1+ count) 0))))
> 
> 
> (defun lookup (key keys)
>    (or (position key keys :test #'string-equal) 0))

Sorry.  The above is wrong, wrt your post.  The following has the same 
behavior.

(defun lookup (key keys)
    (let ((pos (position key keys :test #'string-equal)))
      (if pos
          (1+ pos)
          0)))


Cheers
--
Marco
From: Dave
Subject: Re: [newbie] about passing list/array to a function
Date: 
Message-ID: <Yg86b.295452$lK4.9206460@twister1.libero.it>
"Marco Antoniotti" <·······@cs.nyu.edu> ha scritto nel messaggio ·····················@cs.nyu.edu...

> Note that if your most important operation is LOOKUP, you may want to 
> optimize it.  Searching linearly in an array is not the best way to do 
> these things.  A HASH-TABLE or a tree structure has much better search 
> characteristics than an array or a list given the use that you imply in 
> your post.
> 
> What is 'find-all' supposed to do?  It is unclear what kind of data 
> structure you are using and why

Please read the first answer of this thread. :-)
Thanks you.
Dave
From: Martti Halminen
Subject: Re: [newbie] about passing list/array to a function
Date: 
Message-ID: <3F589AE5.3199368F@kolumbus.fi>
Dave wrote:
> 
> Hello, world! :-)
> 
> I have some doubt about passing lists or array to a function.
> for example,

> Array come passed by copying the full value into paramenter
> or it's passed as pointer?
> Same thing about Lists..
> Does exists the way to passing the pointer in place of value?

As a first approximation you can think of everything in CL being passed
as a pointer; on the other hand, the language doesn't have explicitely
accessible pointers at all... (This is not exactly correct, google the
group for occasional nitpicking on this.)



> (DEFUN find-all (start keys array cols)
>     (LET* ((ipos start)
>            (alist NIL)
>            (cerca "")
>            (numcpo 0)
>            (ppos 0))
>         (LOOP
>             (SETF alist (AREF array ipos))
>             (IF (NOT alist)
>                 (RETURN))
>             (DOLIST (sym cols)
>                 (SETF cerca (NTH (1- sym) alist))
>                 (IF (EQUAL cerca "")
>                     (SETQ numcpo 0)
>                     (SETQ numcpo (lookup cerca keys)))
>                 (WHEN (> numcpo 0)
>                     (SETQ ppos sym
>                           numcpo ipos)
>                     (RETURN)))
>             (IF (> numcpo 0)
>                 (RETURN))
>             (SETQ ipos (1+ ipos)))
>         (LIST numcpo ppos)))

Regardless of any other problems you may have, I think this may not do
what you thought. Seems to me this only returns a list of two numbers,
the indices for the last key found, instead of something for all, like
the name suggests.
(Or the first one, I'm not quite sure how all your RETURNs interact...
generally, anything with this many explicit returns could use a little
rewriting...)

--
From: Alan S. Crowe
Subject: Re: [newbie] about passing list/array to a function
Date: 
Message-ID: <86brty3jcy.fsf@cawtech.freeserve.co.uk>
Dave laments:

    This code below seems to be slow when running for more times and
    I don't know the reason..
    Does it depends on the passing of array?

I'm fairly new to Lisp myself. I'll leave the pointer
question to the experts on the list, and share my
experiences of when my code seems to be slow. Usually it is
because I forgot to compile. For example

* (defun logistic(coefficient repetitions)
    (let ((x 0.5f0))
      (dotimes (counter repetitions x)
	 (setq x (* coefficient x (- 1 x))))))
LOGISTIC

* (time (logistic 3 100000))
Compiling LAMBDA NIL: 
Compiling Top-Level Form: 

Evaluation took:
  10.66 seconds of real time
  10.280391 seconds of user run time
  0.100671 seconds of system run time
  [Run times include 0.21 seconds GC run time]
  23 page faults and
  14393936 bytes consed.
0.6648012

* (compile 'logistic)
Compiling LAMBDA (COEFFICIENT REPETITIONS): 
Compiling Top-Level Form: 

LOGISTIC
NIL
NIL
* (time (logistic 3 100000))
Compiling LAMBDA NIL: 
Compiling Top-Level Form: 

Evaluation took:
  0.26 seconds of real time
  0.245679 seconds of user run time
  7.0000005e-5 seconds of system run time
  0 page faults and
  2400256 bytes consed.
0.6648012

There, forty times faster, that is more like it :-)

Hang on, that is still only about 1 Million floating point
operations per second, and why is it consing 240 bytes every
time round the loop? :-P

I'm still using Lisp in rapid prototype mode. I need to
tune:

* (defun logistic(coefficient repetitions)
    (declare (type fixnum repetitions)
	     (type single-float coefficient))
    (let ((x 0.5f0))
      (declare (type single-float x))
      (dotimes (counter repetitions x)
	 (setq x (* coefficient x (- 1 x))))))
LOGISTIC

* (compile 'logistic)
Compiling LAMBDA (COEFFICIENT REPETITIONS): 
Compiling Top-Level Form: 
LOGISTIC


* (time (logistic 3 100000))
Compiling LAMBDA NIL: 
Compiling Top-Level Form: 


Type-error in KERNEL::OBJECT-NOT-SINGLE-FLOAT-ERROR-HANDLER:
   3 is not of type SINGLE-FLOAT

Oh god, that has made Lisp go all picky on me.

* (time (logistic 3.0 100000))
Compiling LAMBDA NIL: 
Compiling Top-Level Form: 

Evaluation took:
  0.01 seconds of real time
  0.010963 seconds of user run time
  0.0 seconds of system run time
  0 page faults and
  0 bytes consed.
0.66592056

That is using CMUCL and is about the same as unoptimised C
on my 166MHz Pentium. Whoops, that example dragged on a bit.

The other point I wanted to make is that I fear that you are
missing out on the fun. In most languages when you want to
write a routine to search an array you end up with a dilemma
about what to return. The most straight forward thing is to
return a structure with a bit to say whether you found what
you were looking for, and a number to say where it is if you
found it. But most languages make this a pain to do, so
you end up designing a code, such as -1 means not found,
non-negative means found at that zero-indexed location. That
is OK (though god help you if you have a memory lapse, and
elsewhere assume you had decided on 0 for not found and a
positive number for the index of the location (counting from
1)).

Well Lisp is good on this little detail. It uses the atom
nil for false, and anything else, including all the numbers,
represents true. So you don't really want your wrapper on
position at all. Instead of

(DEFUN lookup (cerca keys)
    (LET* ((count 0))
          (SETQ count (POSITION cerca keys :test 'EQUALP))
          (RETURN-FROM lookup (IF count (1+ count) 0))))

use (position ... ) raw. Rewrite

	(DOLIST (sym cols)
                (SETF cerca (NTH (1- sym) alist))
                (IF (EQUAL cerca "")
                    (SETQ numcpo 0)
                    (SETQ numcpo (lookup cerca keys)))
                (WHEN (> numcpo 0)
                    (SETQ ppos sym
                          numcpo ipos)
                    (RETURN)))
            (IF (> numcpo 0)=20
                (RETURN))
to read
	(DOLIST (sym cols)
                (SETF cerca (NTH (1- sym) alist))
                (IF (EQUAL cerca "")
                    (SETQ numcpo nil)
                    (SETQ numcpo (position cerca keys :test #'equalp)))
                (WHEN numcpo
                    (SETQ ppos sym
                          numcpo ipos)
                    (RETURN)))
            (IF numcpo
                (RETURN))

See what has happened? The built-in coding of nil for not
found and a number for where it was found works nicely with
the predicates (when ..) and (if ..)

I'm sure your code could be simplified very much more, but
I've not managed to understand it. Using all those RETURNS
just has to be wrong. You didn't say what you intended the
code to do, perhaps it seems slow because it is doing
something else.

Alan Crowe