Subject: Re: position and test
From: rpw3@rpw3.org (Rob Warnock)
Date: Sat, 16 Dec 2006 20:58:50 -0600
Newsgroups: comp.lang.lisp
Message-ID: <y46dnR7mf9j3LxnYnZ2dnUVZ_rmdnZ2d@speakeasy.net>
Matthew D Swank <akopa-is-very-much-like-my-address@c.net> wrote:
+---------------
| Pascal Bourguignon <pjb@informatimago.com> writes:
| > Matthew D Swank <akopa-is-very-much-like-my-address@c.net> writes:
| >> Are there any guarantees w/regard to argument order when you pass a
| >> test to a function like position?
| >
| > Yes, there are. They are explicited in:
| > http://www.lispworks.com/documentation/HyperSpec/Body/17_b.htm
| 
| Ah... So it's always going to be-- :test #'(lambda (o zi) ...)
+---------------

Yes, so to do what you were trying to do in your original question
you would have to do this:

    > (position '(#\space #\tab #\newline)
		"This is	a very
		silly string"
	        :test (lambda (o zi) (member zi o)))

    4
    >

But note that now you can also supply a :TEST to MEMBER as well,
which lets you do things like this:

    > (position '("FIRST" "SECOND" "THIRD")
		'("This" "is" "the" "second" "silly" "arglist")
		:test (lambda (o zi) (member zi o :test #'equalp)))

    3
    >

And you can continue to stack tests to a quite amazing degree, e.g.:

    > (position '("FIRST" "SECOND" "THIRD")
		'("This" "is" "the" "*Second*" "silly" "arglist")
		:test (lambda (o zi)
			(member zi o :test (lambda (o zi)
					     (search zi o :test #'equalp)))))

    3
    >

Though when things get this complex, for readability I tend to
pre-define the tests with FLET or LABELS [since there's no FLET*]:

    > (labels ((ci-search (o zi) (search zi o :test #'equalp))
	       (cis-member (o zi) (member zi o :test #'ci-search)))
	(position '("FIRST" "SECOND" "THIRD")
		  '("This" "is" "the" "*Second*" "silly" "arglist")
		  :test #'cis-member))

    3
    >

Finally, note that all of the above have N^2 (well, M*N) performance
when the target-value set and the argument lists are long, so in
those cases you might be better off using a hash table for the
target-value set [though in that case you can't use the SEARCH hack].

    > (let ((keys (make-hash-table :test #'equalp)))
	(dolist (i '("FIRST" "SECOND" "THIRD"))
	  (setf (gethash i keys) t))
	(position keys '("This" "is" "the" "second" "silly" "arglist")
		  :test (lambda (o zi) (gethash zi o))))  ; Note reversal

    3
    >


-Rob

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607