Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

-zip to "merge" two lists together #14

Closed
Fuco1 opened this issue Jan 26, 2013 · 5 comments
Closed

-zip to "merge" two lists together #14

Fuco1 opened this issue Jan 26, 2013 · 5 comments

Comments

@Fuco1
Copy link
Collaborator

Fuco1 commented Jan 26, 2013

I'm pretty surprised this isn't here already! :D I just had a need for this so here's the code:

(defun -zip (list1 list2 &optional init)
  "Zip the two lists together.  Return the list where elements
are cons pairs with car being element from LIST1 and cdr being
element from LIST2.  The length of the returned list is the
length of the shorter one.

Optionally, LIST2 can be a function. Then the values that it
generates are taken instead.  The function should take one
argument which is the last generated value.  INIT is the initial
value for this function.  The first value inserted into the
zipped list is INIT, the second is (list2 INIT),
third (list2 (list2 INIT)) etc."
(let ((r nil))
    (if (not (functionp list2))
        (while (and list1 list2)
          (!cons (cons (car list1) (car list2)) r)
          (!cdr list1)
          (!cdr list2))
      (let ((v init))
        (while list1
          (!cons (cons (car list1) v) r)
          (setq v (funcall list2 v))
          (!cdr list1))))
    (nreverse r)))

Examples:

(-zip '(1 2 3) '(a b c d)) ;; => ((1 . a) (2 . b) (3 . c))
(-zip '((1 x) (2 y) (3 z)) '(a b)) ;; => (((1 x) . a) ((2 y) . b))
;; index the list
(-zip '(a b c d e) #'1+ 1) ;; => ((a . 1) (b . 2) (c . 3) (d . 4) (e . 5))

The last example is in fact so cool that it maybe even deserves its own function. Up to you.

@magnars
Copy link
Owner

magnars commented Jan 26, 2013

This is neat, thanks. :-) In clojure, there is zipmap, but of course, this function does not create a map. -zip-alist? I guess zip is a nice name for it.

I certainly think that the function taking two lists, and the function taking one list, a function and an initial value, should be separate. Any thoughts on naming the second one?

@magnars
Copy link
Owner

magnars commented Jan 26, 2013

The #' is a common lisp function quote that is not necessary or idiomatic in Emacs Lisp, by the way.

@Fuco1
Copy link
Collaborator Author

Fuco1 commented Jan 26, 2013

Maybe it can be called -index-list or something along those lines. It basically index the list with whatever sequence you generate. It's unfortuante that "index" is both a verb and a noun in this case, it might make someone believe that it creates "index list", whatever that might be. But I can't come up with anything better. (indexify-list? sounds a bit too much :P)

Didn't know about #', cool.

About the name, -zip comes form haskell. Which actually reminds me that we should add -zipWith (http://zvon.org/other/haskell/Outputprelude/zipWith_f.html) as well.

(defun -zipWith (list1 list2 fn)
  "Zip the two lists LIST1 and LIST2 using a function FN.  This
function is applied pairwise taking as first argument element of
LIST1 and as second argument element of LIST2 at corresponding
position."
  (let ((r nil))
    (while (and list1 list2)
      (!cons (funcall fn (car list1) (car list2)) r)
      (!cdr list1)
      (!cdr list2))
    (nreverse r)))
(-zipWith '(1 1 1) '(1 2 3) '+) ;; => (2 3 4)

then -zip is simply

(defun -zip (list1 list2)
  "Zip the two lists together.  Return the list where elements
are cons pairs with car being element from LIST1 and cdr being
element from LIST2.  The length of the returned list is the
length of the shorter one."
  (-zipWith list1 list2 'cons))

The version with generator function is something to basically simulate "infinite lists", so you can for example zip a list with (1, 2, ...) or (1, 1, ...) (as known from haskell). Obviously in elisp it would only work if one list is finite, that's why it can't take two functions.

@magnars
Copy link
Owner

magnars commented Jan 26, 2013

It would be possible to write a lib with lazy sequences in elisp too, but that's a job for someone else I think. Got enough on my plate. :-) I really like zip-with. This is going in tonight.

I can understand the wish to simulate lazy sequences with a function+initial value pair. I'll think on that some more.

Thanks!

@magnars
Copy link
Owner

magnars commented Jan 26, 2013

I also added an anaphoric version of -zip-with

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants