Skip to content

Instantly share code, notes, and snippets.

@vseloved
Last active May 30, 2020 04:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vseloved/4432594 to your computer and use it in GitHub Desktop.
Save vseloved/4432594 to your computer and use it in GitHub Desktop.
List comprehensions with a syntax very close to set-theoretic notation
(defun read-listcomp (stream char)
(declare (ignore char))
(let (rezs srcs conds state)
(dolist (item (read-delimited-list #\} stream))
(if (eql '|| item)
(setf state (if state :cond :src))
(case state
(:src (push item srcs))
(:cond (push item conds))
(otherwise (push item rezs)))))
(setf rezs (reverse rezs)
srcs (reverse srcs)
conds (reverse conds))
(let ((binds (mapcar (lambda (group) (cons (first group) (third group)))
(group 3 srcs))))
`(mapcan (lambda ,(mapcar #'car binds)
(when (and ,@conds)
(list ,(if (rest rezs)
(cons 'list rezs)
(first rezs)))))
,@(mapcar #'cdr binds)))))
(set-macro-character #\{ #'read-listcomp)
(set-macro-character #\} (get-macro-character #\)))
;; Exmaples:
;; CL-USER> { x || x <- (loop :for i :upto 10 :collect i) }
;; (0 1 2 3 4 5 6 7 8 9 10)
;; CL-USER> {x || x <- '(1 nil 2) || x}
;; (1 2)
;; CL-USER> {x y || x <- '(1 2 3) y <- '(5 6 7) || (oddp x) (> y 5)}
;; ((3 7))
;; CL-USER> { (+ x y) || x <- '(1 2 3) y <- '(5 6 7) || (oddp x) (> y 5) }
;; (10)
;; GROUP is borrowed from Paul Graham's On Lisp
(defun group (n list)
"Split LIST into a list of lists of length N."
(declare (integer n))
(when (zerop n)
(error "Group length N shouldn't be zero."))
(labels ((rec (src acc)
(let ((rest (nthcdr n src)))
(if (consp rest)
(rec rest (cons (subseq src 0 n) acc))
(nreverse (cons src acc))))))
(when list
(rec list nil))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment