[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: ASSOC-RASSOC-IF-KEY (Version 1)



    Date: 15 Jun 87 13:23 PDT
    From: Masinter.pa@Xerox.COM

    I've been unable to find any other implementation (outside of symbolics)
    that has ASSOC-IF-KEY.  I looked at Spice, Xerox and VaxLisp. I'm uneasy
    saying that the others are "split down the middle" if they aren't. 

I think I didn't have access to other lisps in order to try it on the day
I sent that message. If none of the other lisps you know do it, then I'm
happy to say under current practice that only we are known to have implemented
the extension.

    I tried to generate a test case. Can you think of something more
    illuminating than

    (ASSOC-IF #'ZEROP '(("ABC" . 3) ("E"  . 4) ("" . 7)) :KEY #'LENGTH)

      => ("" . 7)

Well, I don't like to think of a non-accessor as the :KEY, though this
is technically fine.  We should probably give some advice about this in
the sample cleanup template; my feeling is that the test case just has
to illustrate the feature, not motivate it. Under this criterion, the
case above is fine.

The problem with a simple test case is that the need for this only comes up
in real program situations where objects with structured parts appear in
alists. In that case, you might have something like:

(DEFSTRUCT FOO X)
(ASSOC-IF #'IDENTITY '((#S(FOO :X NIL) 3 4) (#S(FOO :X T) 7)) :KEY #'FOO-X)

or

(ASSOC-IF #'PROBE-FILE '((#S(FOO :X "FOO.LSP") 3 4) (#S(FOO :X "BAR.LSP") 7))
	  :KEY #'FOO-X)

for that matter. In a real program, the #S(...) forms would presumably not be
constant -- they'd be pushed on by programs calling constructors -- and this
latter example doesn't return a constant value across implementations, but it
hopefully gives you a sense of what you can do. Another case might be:

(DEFVAR *KNOWN-PACKAGES* (MAPCAR #'FIND-PACKAGE '("LISP" "KEYWORD")))

(DEFUN KNOWN-PACKAGE-P (PACKAGE) (MEMBER PACKAGE *KNOWN-PACKAGES*))

(ASSOC-IF #'KNOWN-PACKAGE-P '((FROB . FUNCTION) (IF . SPECIAL-FORM))
	  :KEY #'SYMBOL-PACKAGE)
 => (IF . SPECIAL-FORM)

A situation that seems most familiar to me takes more work to set up:

(DEFUN FULL-DIRECTORY-LIST (WILD-PATHNAME)
  "Returns (wild-path (path1 :WRITE-DATE date1 :AUTHOR author1) ...)."
  (LET* ((PATHS (DIRECTORY WILD-PATHNAME))
         (DATES   (MAPCAR #'FILE-WRITE-DATE PATHS))
	 (AUTHORS (MAPCAR #'FILE-WRITE-DATE PATHS)))
    (CONS FILES
	  (MAPCAR #'(LAMBDA (PATH DATE AUTHOR)
		      (LIST PATH :WRITE-DATE DATE :AUTHOR AUTHOR))
		  PATHS DATES AUTHORS))))

In this case, you could imagine a variety of ASSOC-type operations that you
might want to do on the pathname in the car of each list in the cdr of the
result. In general, it's true that you can always write:

(ASSOC-IF #'(LAMBDA (P) (MEMBER (PATHNAME-TYPE P) '("LSP" "L" "LISP")
			     :TEST #'STRING-EQUAL))
	  (CDR DIRECTORY-LISTING))

but somehow I find it more aesthetic to see:

(ASSOC-IF #'(LAMBDA (TYPE) (MEMBER TYPE '("LSP" "LISP" "L")))
	  (CDR DIRECTORY-LISTING)
	  :KEY #'PATHNAME-TYPE)

so that the key extraction and the predication are separate. This increases
the likelihood that the function in the predicate position will already exist
with some known name. For example, going back to your original example,
there is no function which does (ZEROP (LENGTH x)) but there are functions
ZEROP and LENGTH which work together well in the place where :KEY is provided.

Feel free to extract any part of this message that moves you and add it to
the proposal if you feel it's warranted. Doubtless this text is too long
and rambly to be included as a whole.