Simulating Python Zip in Lisp

Written by on September 4, 2023 in LISP, Programming, Python with 1 Comment

The zip() function in Python is a convenient mechanism for iterating over multiple “iterables” in parallel. Looping over lists is a common scenario.

Python Zip() Feature

Python Zip() Feature

Here is the output generated by the above code:

Python Output

Python Output

Common Lisp does not have such a feature built into the language or as part of the standard library. Of course, we have the expressive “loop” construct that allows us to iterate over multiple lists, etc. Here is an example that traverses multiple lists:

Common Lisp Loop Function

Common Lisp Loop Function

It is not difficult to program the zip() functionality in Lisp. In this article, I will show two ways to do this. For the sake of brevity, I am going to focus on lists as arguments to zip().

Using Function Closure

Our first implementation takes advantage of Lisp’s “closure”, where we return an anonymous function from a function. The anonymous function stores its state in the enclosing function’s variables. Here is the code:

Closure-based Implementation

Closure-based Implementation

The zip() function takes multiple lists as its arguments and returns a function that captures this state. Every time this function is called, it will return the next sequence from the list. For convenience and readability, I have also defined the next() function (as a macro) that returns the next element. It returns “nil” when the entire collection has been traversed.

The following shows an interactive session that uses the zip() function:

Using Closure-based Implementation

Using Closure-based Implementation

Note that the function correctly handles the case when not all lists are of the same size.

Using “struct” Abstraction

Yet another (in my view, better) approach is to wrap the zip() functionality around a class/struct. I have chosen struct because that is sufficient for our case. Here is the code:

Using Defstruct

Using Defstruct

Our struct contains just one instance variable called “elements” to keep track of the supplied collection. The main zip() method calls the constructor to instantiate a new object. As earlier, the next() method returns the next set of elements and return “nil” when the collection is fully traversed. I have also defined two convenience methods size() and remaining(). The former returns the number of pending items and the latter returns all remaining items from the sequence.

The following shows its usage:

Using Defstruct Version

Using Defstruct Version

It is possible to extend the implementation to cover other collections such as arrays, dictionaries, etc.

I have tested this implementation in LispWorks Enterprise 8.0.1, 64 bit edition on Windows 10. Here is the source code.

Have a nice week!

Tags: , ,


If you enjoyed this article, subscribe now to receive more just like it.

Subscribe via RSS Feed

1 Reader Comment

Trackback URL Comments RSS Feed

  1. Forest says:

    Zip is just called “map” in lisp languages:

    It’d be remiss for the lisp family to lack a higher order function to process multiple lists in parallel.

    Eg in your first example, in “Racket” (I’m sure it’s very similar in lisp):

    (curry format “The elements are: ~a, ~a, and ~a.”)
    ‘(10 20 30)
    ‘(a b c)
    ‘(good better best))

    There is one caveat: it requires all the input lists to be the same length. However, it’d be interesting to see the python version be more polymorphic like lisps map, and also to see lisps map accept lists of different lengths.

Leave a Reply

Your email address will not be published. Required fields are marked *