Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!uunet!seismo!rutgers.rutgers.edu!mit-eddie!uw-beaver!tektronix!reed!bart From: bart@reed.UUCP (Bart Massey) Newsgroups: comp.lang.lisp Subject: Re: EQ and EQUAL (Warning: *TOO LONG* :-) (Re: Importance of REPLACA, REPLACD, and the like?) Message-ID: <6638@reed.UUCP> Date: Sun, 19-Jul-87 16:06:44 EDT Article-I.D.: reed.6638 Posted: Sun Jul 19 16:06:44 1987 Date-Received: Tue, 21-Jul-87 00:44:23 EDT References: <22@citcom.UUCP> <13278@topaz.rutgers.edu> <6601@reed.UUCP> <3927@spool.WISC.EDU> Reply-To: bart@reed.UUCP (Bart Massey) Organization: Reed College, Portland OR Lines: 119 Keywords: too long In article <3927@spool.WISC.EDU> neves@ai.WISC.EDU (David M. Neves) writes: > In article <6601@reed.UUCP> bart@reed.UUCP (Bart Massey, me) writes: > >At Reed college we are currently doing almost all of our lisp > >programming in an in-house dialect which supports neither destructive > >list functions nor dotted pairs. > ... > >In case anyone is interested, the following major changes in the > >language were also implemented, and have proven useful: > > > >- Elimination of eq: Using a properly written version of equal, eq is > >the first thing to happen anyhow, and we've never encountered a > >situation where knowing that two lists were non-eq is "good enough". I've gotten several replies already to this statement, so I thought I'd try to give a little more background and detail about what I meant... There are three explicit Reed Lisp design goals that are relevant to this question. These are: -- Lisp should be easy for non-lisp-programmers, and non-programmers, to learn and to read. -- The internal operation of a lisp interpreter/compiler should, as much as possible, be hidden from the user, for the convenience of the implementor as well as the user himself. -- Efficiency is unimportant up to a constant. I'm not sure how to put this clearly... If the language is designed in such a way as to force certain algorithms to be higher "order" or to take more than an "order of magnitude" longer to run, the design should be reconsidered. If, on the other hand, a language design change would allow algorithms to run a "small constant" amount faster, it should not be implemented unless there are other compelling reasons for it. This still isn't very well said, but I think you can appreciate what I mean... > I can construct such a situation. Imagine a list of lists. You want > to find out if a list you have a pointer to is contained within that > list (i.e. MEMBER). You want to use EQ as the comparison function for > efficiency -- especially if the first few characters of the non eq > lists are the same as the one you are looking for. > > e.g. > look for (a b c) object in > List: ( (a b z) (a b a) (a b c d) (a b q) (a b r) (a b c) ) Yep, this is the classic case. Let's look at a little different one, just for the sake of clarity: look for (a b c) in target ( (a b c d) (a b c) (a b c d) (a b c) ) OK, here's what I'm thinking when I see this: Do I know that both instances of (a b c) in target are the same pointer? If not, is this a problem? (In other words, do I really want to look for (a b c) in target, or for a *specific* (a b c)?) There are two cases: I) I want *any* (a b c). This is by *far* the most common case in my (admittedly somewhat limited) experience. In this case, I can still use eq, but I have several problems: I must be very careful that I don't insert (a b c) into target in any way except by inserting the appropriate pointer. Anyone trying to read or debug my code must also be very careful that I have done so. This is generally non-trivial. I must understand the internals of my implementation *very well* to insure to myself that I am always putting the proper pointer in. Such details actually vary a great deal from implementation to implementation, so I must write for a specific interpreter. Case II) I am only interested in finding my pointer. I don't really care that it happens to be pointing to the list (a b c). This is the case which I have never seen occur in practice, and have a hard time thinking of an example of. Cases that *look* like this generally look that way because the programmer has spent time trying to figure out where his (a b c)s are. With respect to the three design goals stated above, I suspect it is now clear why we didn't implement eq. It makes lisp harder to understand, the code harder to read, is implementation dependent. Finally, note what kind of efficiency increase you get from its use in the above example. In compiled lisp, you get effectively a factor of 5, during this one line of code, because you are doing 5 comparisons (the list and four of its elements) in the equal version, to each single test (the list) in the eq version. But note that these 5 comparisons are approximately 20 machine instructions (5 compares, 5 cdrs, and a (machine-level) recursion), versus the single machine instruction of eq. The overhead of executing compiled lisp code effectively swamps this gain in all but the largest of cases, and thus you're not likely to be able to measure the speedup in a piece of real code. The interpreted case is even worse. Ours is a fast lisp interpreter -- approximately 2-10 times as fast as Franz. In our lisp, an equal on even a 1000 element deeply recursive list with all but the last element succeeding is completely swamped by the overhead of the eval associated with executing the equal, in spite of the fact that much of our speed advantage is due to a very fast eval. I hope this clarifies why we left eq out of our lisp interpreter. My friends and I are awfully fond of the concept of "fast enough", and "The Law Of Virtual Efficiency: A "Real Programmer" will spend a week to save the user 10 seconds over the life of the program." But then again, what do we know??? Precious little! Please send replies to me, I will summarize. BTW, thanks much for all the flattering and interested responses I've received so far! This was the first net article I'd posted in a long time that I didn't receive *a single flame* about!! Some useful criticisms, but no flames! How very pleasant!... Bart the Long-Winded :-)