1
i = SomeIndex()
while mylist[i] is not None:
   if mylist[i] == name:
       return
   foo()
   i+=1

I want foo() to always run on 1st iteration of loop, if mylist[i] isn't 'name', but never run if its any iteration but the first. I know I could the following, but I don't know if it's the most efficient and prettiest python code:

i = SomeIndex()
FirstIter = True
while mylist[i] is not None:
   if mylist[i] == name:
       return
   if FirstIter:
       foo()
       FirstIter = False
   i+=1
zenmonkey
  • 13
  • 2

4 Answers4

1

Personally I like mark_ends from the third party library more-itertools

from more_itertools import mark_ends

i = SomeIndex()
for first, last, elem in mark_ends(mylist[i:]):
    if elem == name:
        return
    if first:
        foo()

mark_ends gives you a 3-tuple for every element in your iterable, in this case the sliced mylist. The tuples are (True, False, elem_0), (False, False, elem_1), ..., (False, False, elem_n-2), (False, True, elem_n-1). In your use case you never use the middle element of the tuples.

If for some reason you can't or don't want to use the external library you can swipe the code from https://more-itertools.readthedocs.io/en/stable/_modules/more_itertools/more.html#mark_ends

Addendum:

In light of the OP's requirement to let foo change the list, here's a quick modification:

from more_itertools import mark_ends

i = SomeIndex()
for first, last, (j, elem) in mark_ends(enumerate(mylist[i:], start=i)):
    if elem == name:
        return
    if first:
        foo(mylist, j)

j now gives you the index that you need to tell foo what to change.

Jamie Deith
  • 706
  • 2
  • 4
  • I didnt make this clear in my question, but i want if clause (mylist[i] == name) to execute before foo(), bcs foo() alters the list elem. Im actually implementing some kind of hash table for a uni class, so what im doing here is handling collisions with open adressing. If the key is the same, i insert the new elem one way, but if its not i do it in another way, foo() is for recording the first collision. – zenmonkey May 19 '21 at 23:20
  • @zenmonkey see addendum - it might address this requirement – Jamie Deith May 20 '21 at 00:41
0

You are trying to emulate a do-while, take a look at this question if you want.

Since there is no do-while equivalent in Python, the simple idea is to move the first iteration out of the loop

i = SomeIndex()
foo()
while mylist[i] is not None:
   if mylist[i] == name:
       return
   foo()
   i+=1

Also, the way you iterate over the list is unnecessarily complex, you can simplify it like:

i = SomeIndex()
if myList[i] != name:
  foo()
for element in myList[i+1:]:
  if element == name:
    return    
  foo()

ozerodb
  • 543
  • 3
  • 13
  • Firsty, i want foo() to run only if the fi clause executes first. I dont want foo() to run for any iteration other than the first. Also my actual code isnt '==name', its around 7 lines of code modifying the list element, buts its inside a class so i thought itd be better to omit. – zenmonkey May 19 '21 at 23:12
  • Sorry, I'm having troubles following you, if you only need to call foo one time, can't you do it outside of a loop? – ozerodb May 19 '21 at 23:14
  • foo() is supposed to modify the initial list element. So the if clause 'mylist[i] == name' must run for that element first, and then, if mylist[i] != name, foo() modifies the elem. But this happens only in the first iteration of while – zenmonkey May 19 '21 at 23:23
0

Simplify your logic to independent steps. Yes, you will make a Boolean value check a second time. This takes far less time than you spent with your design problem.

start = SomeIndex()
if mylist[start] != name:
    foo()

for idx in range(start, len(mylist)): 
   if mylist[idx] == name:
       return
   # remainder of loop
Prune
  • 76,765
  • 14
  • 60
  • 81
  • 1
    Technically, this is not equivalent to the example posted. Though it is likely to be the right direction for OP to go towards. – Mateen Ulhaq May 19 '21 at 22:48
  • One example is where the `if mylist[i] is not None: break` is omitted, so this doesn't work in the same manner on lists containing `None`. Although I don't disagree that the omission is cleaner (there are likely better ways to write OP's code), it might be useful to note that it is not equivalent. – Mateen Ulhaq May 19 '21 at 23:02
  • I got that right after I dug into my own code, hence deleting my comment. :-) You're quite correct -- also that OP should work toward a cleaner data and control flow. – Prune May 19 '21 at 23:14
0

Let's "pythonize" your example, step by step.

1. Remove the first_index flag:

start_idx = SomeIndex()
i = start_idx

while mylist[i] is not None:
    if mylist[i] == name:
        return
    if i == start_idx:
        foo()
    i += 1

2. Convert to while True:

start_idx = SomeIndex()
i = start_idx

while True:
    if mylist[i] is not None:
        break
    if mylist[i] == name:
        return
    if i == start_idx:
        foo()
    i += 1

3. Convert to for i in range loop:

start_idx = SomeIndex()

for i in range(start_idx, len(mylist)):
    if mylist[i] is not None:
        break
    if mylist[i] == name:
        return
    if i == start_idx:
        foo()

Note that this also fixes a bug by bounding i < len(mylist).


Variations

These are not equivalent to your code sample, but they might be relevant patterns for you to use.

Variation 1:

Find the index where mylist[i] == name:

index = next(
    (
        i
        for i in range(start_idx, len(mylist))
        if mylist[i] == name
    ),
    None,  # If no index is found, set to None.
)
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
  • 1. and 3. I also thought of , but im actually incrementing i cyclically (++imodN) so 3. is just going to be trouble, i think. 1. is a line of code less, but I thought there would be a prettier way lol. Like some python keyword that never executes the 'if i==start_idx()' after the first iter. Its not really the performance (which would very slightly improve) but i just really like it better that way. Also i dont get why i would want to do 2. lol. But thanks for the input – zenmonkey May 19 '21 at 23:31