1

Consider the following snippet of C code; the actual values for M and the content of arr don't actually matter.

int arr[M] = {...};

for (int i=0; i<M; ++i) {
    for (int j=i+1; j<M; ++j) {
        /* do something */
    }
}

What would be the (idiomatic and efficient) Common Lisp analog using the loop macro (or if loop is not right tool, any other construct)?

I know that comparing array access and list access is not correct but could not come up with a better example.

One possibility I tried is the following:

(defvar l '(1 2 ...))

(loop :for n :on l :do
   (loop :for x :in (cdr n) :do
      ;; do something
      ))

But this seems rather clunky.

Please note, that other similar questions deal with ranges not with lists.

1 Answer 1

2

Not sure what you actually want to compute, but I don't think it is clunky.

Example:

CL-USER 6 > (loop for (head . tail) on '(1 2 3 4)
                  do (loop for item in tail
                           do (print (list head item))))

(1 2) 
(1 3) 
(1 4) 
(2 3) 
(2 4) 
(3 4) 
NIL

If you want to keep pairs based on some test:

CL-USER 36 > (defun mapcan-pairs (fn list)
               (loop for (head . tail) on list
                     nconc (loop for item in tail
                                 nconc (funcall fn head item))))
MAPCAN-PAIRS

CL-USER 37 > (defun keep-pairs (test list)
               (mapcan-pairs (lambda (a b)
                               (when (funcall test a b)
                                 (list (cons a b))))
                             list))
KEEP-PAIRS

CL-USER 38 >  (keep-pairs (lambda (a b)
                             (= 13 (+ a b)))
                          '(1 2 3 7 1 4 5 6 3 5 10 15 3))
((3 . 10) (7 . 6) (3 . 10) (10 . 3))
Sign up to request clarification or add additional context in comments.

6 Comments

Well, that was quick, thanks @Rainer Joswig. The destructuring certainly looks nicer than using cdr. On what I actually want to compute: assume, that there is some condition on the elements of the list (e.g. the sum is equal to 42). I want to find all pairs of values in the list that satisfy the condition.
@Aroob like (loop :for (head . tail) :on list :nconc (loop :for item :in tail :if (= target (+ head item)) :collect (list head item)))?
@Sylwester Yes, like that; RainerJoswig has already incorporated this into his answer. I guess what I really want is something like list comprehensions. But for now, this serves my purpose.
@Sylwester Now that I look more closely, you have a collect keyword in your inner loop whereas RainerJoswig uses nconc in the inner loop. Why is that?
@Aroob: because he has the IF in the inner LOOP, while my function is more general and the WHEN is outside. You can use the NCONC variant as a more general building block, demonstrated by my MAPCAN-PAIRS, KEEP-PAIRS functions.
|

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.