5

I see this a lot in examples I read in books and articles:

(caddr *something*)

Or the many variants of c***r commands.

It seems a bit ridiculous to me, when you can more clearly just pull things out with elt:

(elt *something* 2)

But I don't see this technique used as much.

Is there a convention I don't understand that prefers the c***r functions?

johnbakers
  • 24,158
  • 24
  • 130
  • 258
  • I think that the answers include this, but very concisely: Linked lists aren't great for random access. `elt` (and `nth`) are random access functions. Most of the code that you'd write for list processing will be more concerned with, e.g., the `first` and `rest` of a list, not arbitrary elements. It's rare to see `elt` or `nth` for lists because it's not used in much typical code. It's also not very common to see `c[ad]+r` functions in typical code, because it's not typically the way we work with lists. Unfortunately, books and articles don't always exemplify typical code. – Joshua Taylor Nov 14 '13 at 12:07

2 Answers2

5

Functions like caddr, etc can extract components from lists nested inside other lists. But elt only operates on the top-level list, thus it could return a nested list in its entirety, but you'd need to also nest elt commands to extract components, which is what caddr is ultimately doing, so they are not really so comparable.

In some cases, you could interchange them, as they might return the same results if your list has no lists inside it.

johnbakers
  • 24,158
  • 24
  • 130
  • 258
  • 2
    Somewhat similarly and perhaps worthy of mention in this context, `(CADR SOME-LIST)` is simpler and clearer than `(FIRST (REST SOME-LIST))`. – Aaron Miller Nov 14 '13 at 03:37
  • 4
    I like `(SECOND SOME-LIST)` and `(NTH 1 SOME-LIST)`. – deadghost Nov 14 '13 at 03:55
  • 1
    Another way of putting what @OpenLearner is saying is that `elt` gives you (directly) only `car`, `cadr`, `caddr`, `cadddr`,... It does not directly give you `cdr`, `cddr`, `cdar`,... The `c(a|d)+r` functions paint pictures by their names of just how they destructure list structure. – Drew Nov 14 '13 at 04:48
  • 1
    @deadghost `second` and `nth` still fail to get at components of a nested list. like `elt` they operate only the top-most level. – johnbakers Nov 14 '13 at 05:28
  • 1
    I mentioned this in a comment, but to reiterate: it's also not all that common to extract arbitrary indexed elements from lists or cons-trees. List processing code favors `first`/`rest` (or `car`/`cdr`), and code for structured lists/trees (e.g., in macro writing or home grown structures) will typically define domain-specific accessors (e.g., `(defun name (x) (first x)) (defun address (x) (second x))`) or use destructuring-bind (e.g., `(destructuring-bind (name address) x ...)`). – Joshua Taylor Nov 14 '13 at 12:11
5

elt is a generic function that works on both lists and arrays. It is needed when you want to write a generic algorithm which works in the same way on both data types. But there aren't many such algorithms because this would usually disadvantage lists.

The general convention seems to be that:

  • If you write a general-purpose function to work on lists you would use c(a|d)+r function (these cases are exceedingly rare because most of the time there is a library function for that). This usually happens in Stackoverflow questions code / code for class assignments etc :)

  • Seasoned Lisp programmers would use first, second etc. if possible. This is also some times mentioned in best practices. The best practices would also usually mention that one should create appropriate data structures instead of dealing with non-trivial lists.

  • nth or elt are indeed rare because it's hard to think of a good use case for them. I can imagine how both can be used in macros, where the performance isn't important, but some kind of generality is desirable, for instance if someone would want to deal with strings and lists of characters in the same way. Maybe in some prototyping code, where the programmer isn't yet sure of what data type they are going to use, but that's about it.

  • what is an example of a library function you might refer to in your first point? – johnbakers Nov 14 '13 at 09:47
  • @OpenLearner here's one typical example: http://stackoverflow.com/questions/10465096 where there's already `alexandria:flatten` –  Nov 14 '13 at 10:05
  • note that "generic function" here doesn't mean the Common Lisp generic function that's a part of CLOS, but rather "works with *sequences*, i.e., both lists and vectors". – Joshua Taylor Apr 28 '15 at 14:22