# Emulating Python's zip function with variadic input

**URL:** https://racket.discourse.group/t/emulating-pythons-zip-function-with-variadic-input/4135
**Category:** Questions & Answers
**Created:** [March 7, 2026, 6:44pm UTC](https://racket.discourse.group/t/emulating-pythons-zip-function-with-variadic-input/4135 "2026-03-07T18:44:07Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![gritty](https://avatars.discourse-cdn.com/v4/letter/g/74df32/32.png) [@gritty](https://racket.discourse.group/u/gritty)
#### Post date: [March 7, 2026, 6:44pm UTC](https://racket.discourse.group/t/emulating-pythons-zip-function-with-variadic-input/4135/1 "2026-03-07T18:44:07Z")

</div>

Hello, first post here, and I'm quite new to Racket. I searched the above question in previous answers but didn't find anything.

What I'm trying to achieve: basically a for/list with a variadic input.

Example:

```scheme
(define (a '(1 2 3)))
(define (b '("x" "y")))
(define (zip . rest)
    ...)
> ((1 "x") (2 "y"))

```

But with a variable amount of inputs.

I've tried rolling my own recursive functions that emulate for/list and seeing if map or apply could help, but none seem to work.

I'm sure there's a package out there that can solve my problem, but I'd like to figure this out.

Feel free to correct me on any posting mistakes I may have made

---

<div class="post-metadata">

### Author: ![EmEf](https://avatars.discourse-cdn.com/v4/letter/e/53a042/32.png) [@EmEf](https://racket.discourse.group/u/EmEf)
#### Post date: [March 7, 2026, 7:16pm UTC](https://racket.discourse.group/t/emulating-pythons-zip-function-with-variadic-input/4135/2 "2026-03-07T19:16:04Z")

</div>

```scheme
#lang racket

#; {∀ (X ...) [Listof X] ... -> [Listof [Listof X]]}
(define (zip . rest)
  (cond
    [(ormap empty? rest) '()]
    [else (cons (map first rest) (apply zip (map cdr rest)))]))

(module+ test
  (require rackunit)

  (define a '(1 2 3))
  (define b '("x" "y"))
  (define expected '((1 "x") (2 "y")))
  (check-equal? (zip a b) expected))

```

---

<div class="post-metadata">

### Author: ![LiberalArtist](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/liberalartist/32/151_2.png) [@LiberalArtist](https://racket.discourse.group/u/LiberalArtist)
#### Post date: [March 7, 2026, 7:21pm UTC](https://racket.discourse.group/t/emulating-pythons-zip-function-with-variadic-input/4135/3 "2026-03-07T19:21:48Z")

</div>

> [@gritty](#):
>
> I've tried rolling my own recursive functions that emulate for/list and seeing if map or apply could help, but none seem to work.
> 
> I'd like to figure this out

It would be easier to give suggestions if you could share some of your non-working attempts, so we could suggest changes for you to consider.

Since you mentioned `apply`, one thing I noticed when working out a solution for myself was that the somewhat “magic” way multiple arguments are transformed into a list adds an extra dimension to think about. It might simplify things to break down the problem this way:

```scheme
(define (zip . rest)
  (zip-list-of-lists rest))
(define (zip-list-of-lists list-of-lists)
  ...)

```

> **SPOILER: Here is one solution—but do try working it out yourself.**
>
> ``` #lang racket (define (zip . lsts) (if (ormap null? lsts) null (cons (map car lsts) (apply zip (map cdr lsts))))) ```

---

<div class="post-metadata">

### Author: ![gritty](https://avatars.discourse-cdn.com/v4/letter/g/74df32/32.png) [@gritty](https://racket.discourse.group/u/gritty)
#### Post date: [March 7, 2026, 7:39pm UTC](https://racket.discourse.group/t/emulating-pythons-zip-function-with-variadic-input/4135/4 "2026-03-07T19:39:48Z")

</div>

> It would be easier to give suggestions if you could share some of your non-working attempts, so we could suggest changes for you to consider.

Having come from imperative langugages I started using the for loops when I couldn't figure out the apply with recursion. Here's the non working imperative version. for/list was really close to what I wanted so I figured I could somehow inject what I needed into it.

```scheme
(define (variadic2 . in)
  (define out empty)
  (for ([z (in-range (length (car in)))])
    (for ([x (in-range (length in))])
      (append (list (list-ref (list-ref in x) z)) out )))
  out)

```

I deleted my recursive attempts, but will try again.

Thank you for the lead-up bite to think about.

---

<div class="post-metadata">

### Author: ![LiberalArtist](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/liberalartist/32/151_2.png) [@LiberalArtist](https://racket.discourse.group/u/LiberalArtist)
#### Post date: [March 7, 2026, 11:33pm UTC](https://racket.discourse.group/t/emulating-pythons-zip-function-with-variadic-input/4135/5 "2026-03-07T23:33:38Z")

</div>

> [@gritty](#):
>
> Here's the non working imperative version.

One problem with this code is that Racket's `append` is very different from Python's `append`. In Python, `x.append(y)` mutates list `x` by inserting the contents of list `y` at the end, returning `None`. In Racket, lists are immutable, and `(append x y)` returns a new list containing the contents of `x` followed by the contents of `y`: it is a pure function, with no side-effects. That means your `variadic2` will always return the initial `out`, i.e. `empty`: the `for` loops are just dead code.

Everyone in this thread has stumbled over this irritating difference! From an old mailing-list thread, [Python's append vs Racket's append and helping novices understand the implications](https://groups.google.com/g/racket-users/c/N3Ke2ORZZ_Q/):

> [@Philip McGrath](#):
>
> I am certain people coming from Python are confused by this, since I was bitten by this very difference when I had to write some Python for the first time in a while. (What do you mean `append` has side-effects?!?)

> [@Matthias Felleisen](#):
>
> For my very first Python program (a couple of days before meeting with GvR), I used Python’s `append` and was annoyed beyond belief.

Another thing to note is that `list-ref` is often a sign of poorly structured code, because lists are not random-access: `list-ref` must traverse from the beginning, taking `O(n)` time. That's all the more true when used in a pattern like:

```scheme
(for ([x (in-range (length in))])
  ... (list-ref in x) ...)

```

which could be written instead as:

```scheme
(for ([v (in-list in)])
  ... v ...)

```

It makes your code clearer to directly iterate through the elements of a sequence, rather than accessing them indirectly through an index, and in many cases it will perform better, too.

If you want a data structure that works more like Python lists, take a look at [`racket/treelist`](https://docs.racket-lang.org/reference/treelist.html).
