(cdaddr '(1 (2 (3 4 (5) (6 7) 8) 9) (10 1 3) 3)) => (1 3)
Without these oldies, you would have to write several combinations of first, second etc. and rest, or some combination of first/rest and nth n list.
(cdaddr '(1 (2 (3 4 (5) (6 7) 8) 9) (10 1 3) 3)) => (1 3)
Without these oldies, you would have to write several combinations of first, second etc. and rest, or some combination of first/rest and nth n list.
If your list structure is so complex that you are using cdaddr or anything like it, you did something wrong. You should use defstruct with human-readable slot names to create more readable and easier to work with code.
The term is at least 40 years old, standardized, still in use, has its own established pronunciation and isn't known to cause a lot of bugs. It is also at the periphery of the car/cdr body of terms -- i.e. programming tradition shows that the level of complexity it covers is needed (or the term would have disappeared), but any further is too rare to merit a shortcut. In addition, it is easy to remember what it does: each d is a cdr, each a is a car. So it isn't any harder to read than a chain of firsts, seconds, rests and nth n's.
I am a big fan of Lisp but it is an old language family that comes with some baggage. The "list processing" view of the language is part of that baggage. One of the greatest shortcomings of Common Lisp is the lack of standardized data structures -- no binary search trees, no priority queues, etc. The language encourages people to use lists as a one-size-fits-all structure, to the detriment of readability, maintainability, and often efficiency.
Sure, constructs like cdaddr have not created the mountain of bugs that we see from the "traditions" of C, but I suspect that has more to do with what you said: cdaddr is rarely used. Most professional Lisp programmers would find a better way to write their code, almost certainly using defstruct to define complex data structures and only using lists when they need list semantics.
Secondly, there is no contradiction between the usefulness of structs and the usefulness of nested lists and the ability to reach directly into them. But structs are no more a universal solution than lists are, so I don't know why you would be so hung up on them---in particular since we aren't discussing a particular use case.
I think the "unreadable monstrosity" thing in its various forms boils down to non-lispers not being used to car/cdr. That may be a fact, but it isn't an argument (and it takes about 5 mins to remedy).
I never said structs are a universal solution. It would make no sense to define lists using structs because lists already exist and are already useful as lists (and as stacks, which lists immediately give you). Sometimes a tree of cons cells works well and requires no further clarification. On the other hand, there are many cases where relying just on cons, car, cdr, and related functions results in hard-to-read, hard-to-change code. You are speaking of "usefulness" but I never doubted that you could get a working solution based on car/cdr, I said it is unreadable and I stand by that. You say that we are not talking about a specific use-case, but a function that accesses structured data in a very specific way without regard to any specific context or use-case screams "readability hazard."
Why should you dismiss someone who sees cdaddr and calls it an "unreadable monstrosity" as simply not knowing the language well enough? There is no need to get religious about a programming language. Common Lisp is a great language that I have been using for many years, both professionally and as my preferred language for personal projects. That does not mean that I should think everything in the language or everything that people commonly use are good ideas.
Only the pop operation has to know about the arrangement. We make it a macro which cleverly transfers nodes from one list to the other to keep the amortized cost low.
https://www.kylheku.com/cgit/lisp-snippets/tree/deque.lisp [oops, where are the test cases?]
> That does not mean that I should think everything in the language or everything that people commonly use are good ideas.
I, however, provide the "cadavers" down to one level deeper in TXR Lisp than Common Lisp, from caar to cdddddr. Therefore, I'm not in a position to turn around and disown them as a bad idea.
There are people genuinely invested in various aspects of this stuff.
> On the other hand, there are many cases where relying just on cons, car, cdr, and related functions results in hard-to-read, hard-to-change code.
That's why Common Lisp has structures and CLOS. One builds on top of those.
For understandability, what you might want to reach for is:
;; d <- (cdaddr obj)
(destructuring-bind (a b (c . d) . rest) obj
d)
or else some pattern matching macro that doesn't require dummy variables up to d. For a single, one-off retrieval of a buried value, it doesn't compete with cdaddr.But that particular ANSI CL construct isn't as forgiving; if the list doesn't have that (c . d) node, you get a destructuring error. Whereas cdaddr will only give you grief if any car or cdr step encounters a non-nil atom. You can't blindly substitute one for the other.
If you have to get several neighboring objects from the tree at that depth, using the cdaddr type function would be inefficient, because you're traversing the full path from the root multiple times. For that reason, efficient destructuring and pattern matching won't use them.
Still, in TXR Lisp, I provide these things to five levels deep.
Not only that, but I have provided numeric access to Lisp structure, which I borrowed from none other than ... Urbit!
Unlike Urbit, I have both big-bit-endian and little-bit-endian access; I call the functions cxr and cyr.
https://www.nongnu.org/txr/txr-manpage.html#N-01DA4F04
I seem to have under-documented this (probably also inspired by Urbit), but the test suite has a bit of coverage:
(mtest
(cxr 1 42) 42
(cxr #b11 '(a . b)) a
(cxr #b10 '(a . b)) b
(cxr #b11000 '(1 2 3 4 5)) 4
(cyr #b100001 '(1 2 3 4 5)) 5
(cyr #b1111 '(((a)))) a
(cyr #b111 '(a)) :error)
(let ((r (range* 0 100)))
(vtest (mapcar (op cyr (succ (expt 2 (succ @1))) r) 0..100) r)
(vtest (mapcar (op cxr (* 3 (expt 2 @1)) r) 0..100) r))