Rotating a two-dimensional array in Python

后端 未结 7 1357
被撕碎了的回忆
被撕碎了的回忆 2020-11-28 19:38

In a program I\'m writing the need to rotate a two-dimensional array came up. Searching for the optimal solution I found this impressive one-liner that does the job:

7条回答
  •  庸人自扰
    2020-11-28 20:03

    That's a clever bit.

    First, as noted in a comment, in Python 3 zip() returns an iterator, so you need to enclose the whole thing in list() to get an actual list back out, so as of 2020 it's actually:

    list(zip(*original[::-1]))
    

    Here's the breakdown:

    • [::-1] - makes a shallow copy of the original list in reverse order. Could also use reversed() which would produce a reverse iterator over the list rather than actually copying the list (more memory efficient).
    • * - makes each sublist in the original list a separate argument to zip() (i.e., unpacks the list)
    • zip() - takes one item from each argument and makes a list (well, a tuple) from those, and repeats until all the sublists are exhausted. This is where the transposition actually happens.
    • list() converts the output of zip() to a list.

    So assuming you have this:

    [ [1, 2, 3],
      [4, 5, 6],
      [7, 8, 9] ]
    

    You first get this (shallow, reversed copy):

    [ [7, 8, 9],
      [4, 5, 6],
      [1, 2, 3] ]
    

    Next each of the sublists is passed as an argument to zip:

    zip([7, 8, 9], [4, 5, 6], [1, 2, 3])
    

    zip() repeatedly consumes one item from the beginning of each of its arguments and makes a tuple from it, until there are no more items, resulting in (after it's converted to a list):

    [(7, 4, 1), 
     (8, 5, 2), 
     (9, 6, 3)]
    

    And Bob's your uncle.

    To answer @IkeMiguel's question in a comment about rotating it in the other direction, it's pretty straightforward: you just need to reverse both the sequences that go into zip and the result. The first can be achieved by removing the [::-1] and the second can be achieved by throwing a reversed() around the whole thing. Since reversed() returns an iterator over the list, we will need to put list() around that to convert it. With a couple extra list() calls to convert the iterators to an actual list. So:

    rotated = list(reversed(list(zip(*original))))
    

    We can simplify that a bit by using the "Martian smiley" slice rather than reversed()... then we don't need the outer list():

    rotated = list(zip(*original))[::-1]
    

    Of course, you could also simply rotate the list clockwise three times. :-)

提交回复
热议问题