3

What is the best (fastest/most pythonic) way to reverse a part of an array in-place?

E.g.,

def reverse_loop(l,a,b):
    while a < b:
        l[a],l[b] = l[b],l[a]
        a += 1
        b -= 1

now after

l = list(range(10))
reverse_loop(l,2,6)

l is [0, 1, 6, 5, 4, 3, 2, 7, 8, 9] as desired.

Alas, looping in Python is inefficient, so a better way is needed, e.g.,

def reverse_slice(l,a,b):
    l[a:b+1] = l[b:a-1:-1]

and reverse_slice(l,2,6) restores l to its original value.

Alas, this does not work for the border cases: reverse_slice(l,0,6) truncates l to [7, 8, 9] because l[a:-1:-1] should be l[a::-1].

So, what is The Right Way?

sds
  • 58,617
  • 29
  • 161
  • 278
  • `l[0:6] = l[0:6][::-1]` should work. See also [here](https://stackoverflow.com/questions/17469012/reverse-part-of-an-array-using-numpy) – Cleb Apr 07 '19 at 22:17
  • @student It seems to me that that question is about `numpy` arrays, whereas this is about Python `lists`. – gmds Apr 07 '19 at 22:25
  • @gmds do you mean it does not work for array other than numpy? – niraj Apr 07 '19 at 22:32
  • @student There are subtle differences. In particular, slicing will work the same way, but the `reversed` method will not. – gmds Apr 07 '19 at 22:38
  • 2
    @gmds I think the **accepted** answer in the link above returns the expected result. `a[1:4] = a[1:4][::-1]` is same as `l[a:b] = l[a:b][::-1]` – niraj Apr 07 '19 at 22:53
  • @student It does indeed, but the semantics of arrays and `lists` are slightly different, as evinced in `reversed` working in one case but not the other. That, to me, distinguishes the two questions sufficiently (see [this](https://stackoverflow.blog/2010/11/16/dr-strangedupe-or-how-i-learned-to-stop-worrying-and-love-duplication/)); the accepted answer working in both cases seems to be neither here nor there. – gmds Apr 07 '19 at 22:58

2 Answers2

1

How about this?

def reverse_slice(l, a, b):
    l[a:b] = l[a:b][::-1]

l = list(range(10))
reverse_slice(l, 0, 6)  # excludes l[6]
print(l)

Output:

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

An alternative with the inbuilt function reversed:

def reverse_func(l, a, b):
    l[a:b] = reversed(l[a:b])

In my tests, slicing is faster than using reversed by a factor of 1.2x-1.5x.

gmds
  • 19,325
  • 4
  • 32
  • 58
0

[6::-1] can be written as [6:None:-1]:

def reverse_slice(l,a,b):
    a1 = None if a==0 else a-1
    l[a:b+1] = l[b:a1:-1]

In [164]: y=x.copy(); reverse_slice(y,1,6);y                                    
Out[164]: [0, 6, 5, 4, 3, 2, 1, 7, 8, 9]
In [165]: y=x.copy(); reverse_slice(y,0,6);y                                    
Out[165]: [6, 5, 4, 3, 2, 1, 0, 7, 8, 9]
hpaulj
  • 221,503
  • 14
  • 230
  • 353