I'm trying to get the largest sublist from a list using Common Lisp.
(defun maxlist (list)
(setq maxlen (loop for x in list maximize (list-length x)))
(loop for x in list (when (equalp maxlen (list-length x)) (return-from maxlist x)))
)
The idea is to iterate through the list twice: the first loop gets the size of the largest sublist and the second one retrieves the required list. But for some reason I keep getting an error in the return-from
line. What am I missing?
Short variant
Paul Graham's
most
Please, consider also Paul Graham's
most
function:This is the result of test (it also returns value that's returned by supplied function for the 'best' element):
extreme
utilityAfter a while I came up with idea of
extreme
utility, partly based on Paul Graham's functions. It's efficient and pretty universal:It takes comparison predicate
fn
, list of elements and (optionally)key
parameter. Object with the extreme value of specified quality can be easily found:Note that this thing is called
extremum
in alexandria. It can work with sequences too.Main problem with
loop
There are a few problems here. First, you can write the loop as the following. There are
return-from
andwhile
forms in Common Lisp, butloop
defines its own little language that also recognizeswhile
andreturn
, so you can just use those:A loop like this can actually be written more concisely with
find
though. It's justNote, however, that
list-length
should always return a number ornil
, soequalp
is overkill. You can just useeql
, and that's the default forfind
, so you can even writelist-length
andmaximize
list-length
is a lot likelength
, except that if a list has circular structure, it returnsnil
, whereas it's an error to calllength
with an improper list. But if you're using(loop ... maximize ...)
, you can't havenil
values, so the only case thatlist-length
handles thatlength
wouldn't is one that will still give you an error. E.g.,(Actually,
length
works with other types of sequences too, solist-length
would error if you passed a vector, butlength
wouldn't.) So, if you know that they're all proper lists, you can justIf they're not all necessarily proper lists (so that you do need
list-length
), then you need to guard like:A more efficient argmax
However, right now you're making two passes over the list, and you're computing the length of each sublist twice. Once is when you compute the maximum length, and the other is when you go to find one with the maximum value. If you do this in one pass, you'll save time.
argmax
doesn't have an obvious elegant solution, but here are implementations based onreduce
,loop
, anddo*
.They produce the same results:
Using recursion:
The
max-list
internal function gets the longest of two list.maxim-list
is getting the longest of the first list and themaxim-list
of the rest.