2

Using Python, I'm trying to read a list or strings backwards. When finding the item of interest, I want to print all of those items from that point to the end of the list. I can do this without recursion and it works fine, but I feel like there's a nicer way to do this with recursion. :)

Example without recursion:

items = ['item1', 'item2', 'item3', 'item4', 'item5']

items_of_interest = []
items.reverse()
for item in items:
    items_of_interest.append(item)
    if item == 'item3':
        break
    else:
        continue

items_of_interest.reverse()
print items_of_interest
['item3', 'item4', 'item5']

Update:

To add clarity to the question, the list is actually the output of a grep of a set of strings from a log file. The set of strings may be repeating and I only want the last set.

agf
  • 171,228
  • 44
  • 289
  • 238
user16738
  • 1,223
  • 8
  • 10
  • 2
    You should just use the example without recursion! Adding recursion will just make Python less efficient and the code harder to read. :) – Brandon Rhodes Sep 15 '11 at 02:44
  • 1
    This question is based on a faulty idea of what represents good coding style, but otherwise it's a perfectly fine question. I don't think it really deserves to be a +1, but certainly doesn't deserve to be at -1. – Omnifarious Sep 15 '11 at 03:14
  • I simplified the question, so I think somebody may have voted it down since they thought it was homework. – user16738 Sep 15 '11 at 03:16
  • Y U WANT RECURSION IN PYTHON? – dcolish Sep 15 '11 at 03:19
  • @Omnifarious — unexplained demands for recursive solutions *typically* indicate a homework problem. :) – Brandon Rhodes Sep 15 '11 at 03:20
  • @Brandon - Lol about the homework comment. I forget many students are on here trying to get free answers. :) – user16738 Sep 15 '11 at 03:26
  • Could you explain _why_ you thought recursion would be a good fit for this? The recursive versions I can think of are still just reverse iteration disguised. I think that `reversed` rather than `reverse` is the piece to solve the problem, not recursion -- it gives you O(k) performance (where k is the length of the part of the list you want) instead of O(n) performance (where n is the length of the list). – agf Sep 15 '11 at 03:44
  • @agt - Reversed was what I was looking. I should remember that often times recursion is no longer the right solution. – user16738 Sep 15 '11 at 03:58

2 Answers2

5

Recursion wouldn't make this simpler, it would make it more complicated.

for i, item in enumerate(reversed(items), 1):
    if item == 'item3':
        items_of_interest = items[-i:]
        break
else:
    # 'item3' wasn't found

seems to be the simplest efficient way to do this to me. You only have to iterate over the list from the end to 'item3', since reversed returns an iterator.

Edit: if you don't mind iterating over the whole list to create a reversed version, you can use:

i = list(reversed(items)).index('item3')
items_of_interest = items[-i-1:]

which is even simpler. It raises an error if 'item3' isn't in the list. I'm using list(reversed()) instead of [:] then reverse() because it's one iteration over the list instead of two.

Edit 2: Based on your comment to the other answer, my first version does what you want -- searches for the item from the end without iterating over the whole list. The version in the question has to iterate the list to reverse it, as does my second version.

A minimally modified, but more efficient, version of your original would be:

items_of_interest = []
for item in reversed(items):
    items_of_interest.append(item)
    if item == 'item3':
        break
items_of_interest.reverse()
tzot
  • 92,761
  • 29
  • 141
  • 204
agf
  • 171,228
  • 44
  • 289
  • 238
1

A recursive solution is not called for here. To the problem of finding the slice of a list from the last occurrence of a item to the end of the list, one approach is to define an auxiliary function

>>> def rindex(s, x):
...     for i, y in enumerate(reversed(s)):
...         if x == y:
...             return -i-1
...     raise ValueError
... 
>>> items[rindex(items, "b"):]
['b', 'f']

The auxiliary function can be called rindex because Python has a rindex method to find the last occurrence of a substring in a string.

If you must do it recursively (perhaps it is homework) then think about it as in this pseudocode (not yet worked out completely)

def tail_from(list, x):
    return tail_from_aux(list, x, [])

def tail_from_aux(list, element, accumulated):
    if list is empty:
        return []
    elif list ends with element
        return element::accumulated
    else:
        last = list[-1]
        return tail_from_aux(list[:-1], element, last::accumulated)

But, this is memory-intensive, goes through the whole list (inefficient), and is not Pythonic. It may be appropriate for other languages, but not Python. Do not use.

Since your actual question refers to files, and log files at that, you may not be able to reduce this problem to an array search. Therefore, check out Read a file in reverse order using python, there are some interesting answers there as well as some links to follow.

Consider mixing tac with awk and grep if you are able to as well.

Community
  • 1
  • 1
Ray Toal
  • 86,166
  • 18
  • 182
  • 232
  • If your list is very long, and you know the item is near the end, then this requires iterating over almost the whole list. The version in the question requires even more iterating, because it uses `items.reverse()` twice plus a reverse iteration, but finding the item can be done without iterating over the whole list using `reversed` which returns an iterator. – agf Sep 15 '11 at 03:01
  • 1
    that gives the wrong answer if there's more than one `item3` in the list. – JBernardo Sep 15 '11 at 03:02
  • Oh, oh, nice. Duh! I will need to fix the answer. Too bad there is no `rindex` on lists. – Ray Toal Sep 15 '11 at 03:07
  • I'm actually reading the contents of a log file backwards that have been read into a list. The file can be long and I know what I'm looking for is towards the end, so there's value in reading it backwards. I simplified the questions to make it easier to discuss. – user16738 Sep 15 '11 at 03:07
  • Thanks, that does add clarity. If the log is in a file, though, it seems you would want to attack the problem a little differently. I will work on a solution. – Ray Toal Sep 15 '11 at 03:09
  • @user16738 - You know, your actual problem is far, far more interesting than the toy problem you gave as an example. *grin* – Omnifarious Sep 15 '11 at 03:27