3

I am trying to write a function like zip. I am not good at explaining what I mean, so i will just show 'code' of what i'm trying to do.

a = [1,2,3,[4,5]]
b = a[:]
zip(a, b) == [(1,1), (2,2), (3,3), ([4,5],[4,5])]
myzip(a, b) == [(1,1), (2,2), (3,3), [(4,4), (5,5)]]

I am so stuck on this it's not even funny. I am trying to write it in a simple functional way with recursive lambdas, to make my code prettier. I want myzip like this because i want to use its output with another function I wrote which maps a function to a tree

def tree_map(func, tree):
    return map(lambda x: func(x) if not isinstance(x, list) else tree_map(func, x), 
               tree)

I have been trying to do something similar to this with zip, but I can't seem to wrap my head around it. Does anyone have any ideas on how i could write myzip?

Edit: Look at tree_map! isn't that pretty! i think so at least, but my mother tongue is Scheme :P and also, I want myzip to go as deep as it needs to. basically, I want myzip to retain the structure of the trees i pass it. Also, myzip will only handle trees that are the same shape.

Broseph
  • 1,655
  • 1
  • 18
  • 38
  • "I am trying to write it in a simple functional way with recursive lambdas, to make my code prettier." That's like.. well, no, I won't try to come up with some comedic simile. But recursion with lambdas is very unlikely to make your code prettier. – DSM Sep 27 '12 at 21:46
  • is just 2-levels okay, or does it have to be infinite? – jterrace Sep 27 '12 at 21:46
  • 1
    Have you thought about checking [`itertools`](http://docs.python.org/library/itertools.html#itertools.izip) ? There's some Python pseudocode presenting the logic that could inspire you... – Pierre GM Sep 27 '12 at 21:49

1 Answers1

7

I think the following should work:

import collections

def myzip(*args):
    if all(isinstance(arg, collections.Iterable) for arg in args):
        return [myzip(*vals) for vals in zip(*args)]
    return args

Result:

>>> a = [1,2,3,[4,[5,6]]]
>>> b = [1,2,3,[4,[5,6]]]
>>> myzip(a, b)
[(1, 1), (2, 2), (3, 3), [(4, 4), [(5, 5), (6, 6)]]]

Note that I use collections.Iterable instead of list in the type checking so that the behavior is more like zip() with tuples and other iterables.

Andrew Clark
  • 202,379
  • 35
  • 273
  • 306
  • AMAZING!!!!!! This is PERFECT!!! It would have taken me HOURS if not days to think of that, I think. Thanks! – Broseph Sep 27 '12 at 21:55
  • +1. You might want to insist that _all_ of the args are lists, not just the first one, so you can do something reasonable (like a flat zip) instead of an exception… unless of course that's not supposed to be valid, in which case an exception is perfect. Also, you might want to use `collections.Iterable` instead of list (so it can deeply zip tuples, etc.). Or even write the whole thing as an iterable instead of a list. But this works, and it's nice and simple. – abarnert Sep 27 '12 at 21:58
  • @abarnert - Thanks for the suggestions, now `myzip([[1]], [1])` returns `[([1], 1)]` instead of raising an exception. – Andrew Clark Sep 27 '12 at 22:09