Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Common lisp: loop through pairs of a list

I have a list who's length is divisible by two, and I'm looking for something similar to the answer to this question:

(loop for (a b) on lst while b
      collect (+ a b))

However there is overlap between elements:

(1 2 3 4 5) -> (3 5 7 9)

adding 1 and 2 and then 2 and 3 etc.

Where as I have a list like (1 2 3 4) and am looking for something like

((1 2) (3 4))

as output. Is there a way to make loop step correctly over the list? Another solution.

like image 620
cheshirecatalyst Avatar asked Dec 11 '22 14:12

cheshirecatalyst


2 Answers

Something like this should work:

(let ((list '(1 2 3 4)))
  (loop :for (a b) :on list :by #'cddr :while b 
        :collect (cons a b)))

Also a more verbose variant:

(let ((list '(1 2 3 4)))
  (loop :for a :in list :by #'cddr
        :for b :in (cdr list) :by #'cddr
        :collect (cons a b)))
like image 130
cybevnm Avatar answered Dec 26 '22 16:12

cybevnm


Another approach using the SERIES package. See also the user manual from Richard C. Waters.

Setup

(ql:quickload :series)
(defpackage :stackoverflow (:use :series :cl))
(in-package :stackoverflow)

Code

(defun pairs (list)
  (collect 'list
    (mapping (((odd even) (chunk 2 2 (scan 'list list))))
      (list odd even))))
  • scan the content of list as a "serie"
  • chunk it with M=2 and N=2:

    This function has the effect of breaking up the input series items into (possibly overlapping) chunks of length m. The starting positions of successive chunks differ by n. The inputs m and n must both be positive integers.

    More precisely, (chunk 2 2 (scan '(1 2 3 4))) produces #Z(1 3) and #Z(2 4)

  • mapping in parallel over each odd and even element of those series, produce a series of couples, as done by (list odd even).

  • finally, collect the result, as a list.

Compilation

All the intermediate "series" are compiled away thanks to a stream-fusion mechanism. Here is the macro expansion when pointing at collect:

(LET* ((#:OUT-1120 LIST))
  (LET (#:ELEMENTS-1117
        (#:LISTPTR-1118 #:OUT-1120)
        (#:COUNT-1113 0)
        #:CHUNK-1114
        #:CHUNK-1115
        #:ITEMS-1123
        (#:LASTCONS-1106 (LIST NIL))
        #:LST-1107)
    (DECLARE (TYPE LIST #:LISTPTR-1118)
             (TYPE FIXNUM #:COUNT-1113)
             (TYPE CONS #:LASTCONS-1106)
             (TYPE LIST #:LST-1107))
    (SETQ #:COUNT-1113 1)
    (SETQ #:LST-1107 #:LASTCONS-1106)
    (TAGBODY
     #:LL-1124
      (IF (ENDP #:LISTPTR-1118)
          (GO SERIES::END))
      (SETQ #:ELEMENTS-1117 (CAR #:LISTPTR-1118))
      (SETQ #:LISTPTR-1118 (CDR #:LISTPTR-1118))
      (SETQ #:CHUNK-1114 #:CHUNK-1115)
      (SETQ #:CHUNK-1115 #:ELEMENTS-1117)
      (COND ((PLUSP #:COUNT-1113) (DECF #:COUNT-1113) (GO #:LL-1124))
            (T (SETQ #:COUNT-1113 1)))
      (SETQ #:ITEMS-1123
              ((LAMBDA (ODD EVEN) (LIST ODD EVEN)) #:CHUNK-1114 #:CHUNK-1115))
      (SETQ #:LASTCONS-1106
              (SETF (CDR #:LASTCONS-1106) (CONS #:ITEMS-1123 NIL)))
      (GO #:LL-1124)
     SERIES::END)
    (CDR #:LST-1107)))
like image 45
coredump Avatar answered Dec 26 '22 16:12

coredump