0
\$\begingroup\$

I wanted to split a list into 2 parts, as per the chosen size, first the size and then the list. Example:

(split 3'(1 3 5 7 9))

(defun split (list count) 
 (values (subseq list 0 count)
        (subseq list count)))
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Welcome to Code Review! Your post was translated to English, since Stack Exchanges sites are English sites. If something needs to be updated then please edit it. \$\endgroup\$ Commented Jun 1, 2022 at 16:18
  • \$\begingroup\$ it appears that your parameters are swapped? \$\endgroup\$
    – BRPocock
    Commented Jun 1, 2022 at 19:29

2 Answers 2

1
\$\begingroup\$

First, as already noted in a comment, the first argument of the function is the list, and the second is the number of elements. So in the examples below I assume that this is what you want.

Then, you do not specify if the the two returned values must be a copy of the original list or can (or must) share at least part of the elements of the original list. This is an important aspect of the meaning of the “split” operator.

  1. If we want two obtain two fresh lists from the original one, then your solution is the best possible. This is because subseq always make a copy of the subsequences of the original list. Another advantage of your solution is that it works for any sequence, not only for lists.

  2. If we dont want to modify the original list, but we allow (or require) the sharing of the second sublist with the original one, then here is a possible solution with the use of the function nthcdr that returns the tail of a list:

    (defun split2 (list count)
       (values (subseq list 0 count)
               (nthcdr count list)))
    
    
  3. Finally, if we require a “physical” split of the original list, so that it is modified, then we can use a third approach (note that in this case a count of 0 should not be allowed):

    (defun split3 (list count)
      (let* ((last-element (nthcdr (1- count) list))
             (second-sublist (cdr last-element)))
        (rplacd last-element nil)
        (values list second-sublist)))
    
    

Finally, note that in all the cases we do not check for the correctness of the input, which is an important aspect of a function like this. So, a check should be made before the main part of the function, taking the appropriate action if count is less than 0 or greater than the length of the list. Note that is also a good practice to put a comment at the beginning of the function specifying its meaning.

So, for instance:

(defun split2 (list count)
   "split a list into two sublists, the first one with count elements.
    Returns a new first list, while the second sublist is part of the original one."
   (if (or (< count 0) (> count (length list)))
       (error "invalid count ~a in split" count)
       (values (subseq list 0 count)
               (nthcdr count list))))

Note that the efficiency of this last version is less than optimal since it must compute the length of the list.

\$\endgroup\$
1
\$\begingroup\$
  1. values should be cl-values in Emacs Lisp (and use (require 'cl-lib) instead of (require 'cl))
  2. Any defun should have a docstring
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.