1. 18
  1.  

  2. 6

    This works because (apply zip lis) is like a matrix transpose, so it undoes itself. For example, if lis is '((1 2 3) (a b c)) then you get (zip '(1 2 3) '(a b c)), which is '((1 a) (2 b) (3 c)).

    It’s surprising because normally I think of zip as converting (list, list) to list-of-tuples. But in Scheme, the cons/nil structures are playing both roles: tuples and lists.

    1. 3

      Here is a matrix representation of the data:

      1 2 3
      a b c
      

      Yeah, I see what you mean. Thank you. Given that that’s what it is, the names zip and unzip becomes kinda unhelpful. It’s also annoying that one works on a list and returns vals, and the other works on args and returns a list. Some monomorphism there would’ve been nice. So compose zip zip would’ve become equal?-identity.

    2. 3

      Is there a blog post/manual/book I can reference to get myself the background required for this, because it seems very cool, but I have no idea what’s going on?

      1. 3
        1. 1

          This is my understanding.

          Imagine a function zip which takes a tuple of lists and returns a list of tuples, for example:

          zip
            ([1, 2, 3, 4, 5], ['a', 'b', 'c', 'd', 'e']) =
            [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')]
          

          unzip just does the opposite:

          unzip
            [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')] =
            ([1, 2, 3, 4, 5], ['a', 'b', 'c', 'd', 'e'])
          

          Now, imagine zip takes a list of lists instead:

          zip
            [[1, 2, 3, 4, 5], ['a', 'b', 'c', 'd', 'e']] =
            [[1, 'a'], [2, 'b'], [3, 'c'], [4, 'd'], [5, 'e']]
          

          You can represent the argument list of lists as a 5x2 matrix, like so:

          1 2 3 4 5
          a b c d e
          

          and the returned list of lists as a 2x5 matrix, like so:

          1 a
          2 b
          3 c
          4 d
          5 e
          

          (I’ll treat matrix and list of lists as interchangeable in meaning.)

          That makes it clear that a zip which takes an arbitrary length list of lists instead of a fixed-size tuple (or triple, &c) of lists can simply be considered equivalent to transpose. transpose applied to a matrix returns a new matrix which returns the original matrix when applied to transpose again; i.e.

          (transpose . transpose) matrix = matrix
          

          Or, for an example:

          transpose
            [[1, 2, 3, 4, 5], ['a', 'b', 'c', 'd', 'e']] =
            [[1, 'a'], [2, 'b'], [3, 'c'], [4, 'd'], [5, 'e']]
          
          # and
          
          transpose
            [[1, 'a'], [2, 'b'], [3, 'c'], [4, 'd'], [5, 'e']] =
            [[1, 2, 3, 4, 5], ['a', 'b', 'c', 'd', 'e']]
          

          So, when you think about it, you realise that unzip is simply zip, because zip is simply transpose and transpose applied to a once-transposed matrix simply returns the original matrix, i.e. transpose is equivalent to untranspose (which doesn’t exist named as such because it’s just transpose).

          Unfortunately, while this is the case mathematically, unzip is still needed because in practice, (AFAIK) most (statically typed?) programming language’s zip implementations (at least in stdlib) act on tuples of lists and return lists of tuples instead of heterogenous lists of lists.