2

Hi I was wondering if it was possible to assign a value in the while comparison section of the code.

Here is an example of the code currently

startIndex = find(target, key, startIndex)
while( startIndex != -1):
    matchesFound += 1
    startIndex = find(target, key, startIndex + 1)
return matchesFound

What I want to do is move the startIndex = find(target, key, startIndex) into the while comparison expresion so it would look something like this

while( (startIndex = find(target, key, startIndex)) != -1):
    matchesFound += 1
    startIndex + 1
return matchesFound

if not, what would a better refactor be?

Thanks

edit I'm working through the MIT Open courseware 6.00 before I try out the famous 6.001 module

Sam
  • 2,427
  • 2
  • 23
  • 26
  • The answer is still no. Duplicate: http://stackoverflow.com/questions/1663995/python-variable-assignment-and-if-statement – S.Lott Jan 14 '10 at 01:04
  • Are those two even the same? In the first case, you call `find` first with `startIndex`, whereas in the latter you call it with `startIndex+1`. – Peter Hansen Jan 14 '10 at 01:13
  • yeah you're right Peter I've corrected it – Sam Jan 14 '10 at 01:29
  • @Sam, with that correction, I'd call that code ugly even in C. :) Changing startIndex in two places is going to trip up the next programmer to touch the code. – Peter Hansen Jan 14 '10 at 01:33

6 Answers6

5

If for some reason you can't use a more appropriate method from whatever you're searching, this is equivalent to your working code from the question:

start = 0
count = 0
for match in iter(lambda: find(target, key, start), -1):
  count += 1
  start = match + 1
return count

However, you'll get the most mileage out of writing an iterator for finding these matches, "transforming" your current find function:

def findall(target, key, start=0):
  for match in iter(lambda: find(target, key, start), -1):
    yield match
    start = match + 1

Then count from that:

count = sum(1 for m in findall(target, key))
  • +1 for first that's both Pythonic *and* (more importantly) actually matches the OP's first example in behaviour. – Peter Hansen Jan 14 '10 at 01:17
  • I'm not sure about more pythonic, the loop-and-a-half is a good candidate, but the question appears to be just example code and this form of iter() might work better in what he's really doing. :) (I also made the assumption startIndex starts at 0 which wasn't stated, though obviously that's easy enough to change.) –  Jan 14 '10 at 01:20
  • I'm doing the assignments in the MIT 6.00 course, this assignment was just write two functions(one iterativly and one recursivly) to count how many times a key string appeared in the target string – Sam Jan 14 '10 at 01:26
  • Let's say "elegant" rather than merely "Pythonic" since, as you say, even the loop-and-a-half is definitely Pythonic. Even were it not, I'd still give you the +1 for nice use of the sentinel argument! :) – Peter Hansen Jan 14 '10 at 01:29
  • Loved the answer, however the find function is string.find(key,start) instead. Still awesome answer. – Marcelo Pacheco Sep 22 '19 at 17:31
1

You're writing C in Python.

Try:

startIndex = -1
while True:
    startIndex = find(target, key, startIndex + 1)
    if startIndex < 0:
        break
    matchesFound += 1
return matchesFound

Or perhaps even:

return target.count(key)
retracile
  • 12,167
  • 4
  • 35
  • 42
  • 1
    This doesn't match the OP's code. `startIndex` needs to be incremented by 1 on calls after the first one, apparently. – Peter Hansen Jan 14 '10 at 01:09
  • +1 for recognizing as a C convention...though I'm sure it's legal in other languages. – AJ. Jan 14 '10 at 01:11
  • I actually did a 14 month placement were I was programming in C too – Sam Jan 14 '10 at 01:15
  • You need the +1 outside of the parens, take a look at my answer. –  Jan 14 '10 at 01:25
  • No, the first iteration you pass 1 (0 + 1) as find's 3rd parameter, where I pass 0. –  Jan 14 '10 at 01:40
  • @Roger: *doh* You are correct, sir. Thanks for pointing that out again. (Ok, fixed to pass 0 (-1 + 1) on the first iteration.) – retracile Jan 14 '10 at 01:48
1

With the PEP 572 being accepted, it will be possible to do this:

while (startIndex := find(target, key, startIndex)) != -1:
    matchesFound += 1
    startIndex + 1
    return matchesFound

It will be included in Python 3.8.

This change will make python code even smaller. Consider the following standart library code:

while True:
    line = fp.readline()
    if not line:
        break
    m = define_rx.match(line)
    if m:
        n, v = m.group(1, 2)
        try:
            v = int(v)
        except ValueError:
            pass
        vars[n] = v
    else:
        m = undef_rx.match(line)
        if m:
            vars[m.group(1)] = 0

It will be improved like this:

while line := fp.readline():
    if m := define_rx.match(line):
        n, v = m.group(1, 2)
        try:
            v = int(v)
        except ValueError:
            pass
        vars[n] = v
    elif m := undef_rx.match(line):
        vars[m.group(1)] = 0
ramazan polat
  • 7,111
  • 1
  • 48
  • 76
0

No, you can't do that in Python. I think the main reason Python disallows this, is to avoid the frequent errors resulting from confusing assignment and equality checking.

Python claims to have readable code as a main guideline, so I think your original code is fine. No need to refactor...

3lectrologos
  • 9,469
  • 4
  • 39
  • 46
0

i would do it like this

startIndex=0
while 1:
    startIndex = find(target, key, startIndex+1)
    if startIndex == -1: break
    matchesFound += 1

you can put in more conditions inside the while loop like that.

Edit: @OP, in future, to count match of string, just use count

>>> mystring = "abc defabc fgh ijkabc blah"
>>> mystring.count("abc")
3
>>>
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
  • Also apparently doesn't match the OP's code, where `startIndex` is passed in advanced by 1 on calls after the first. – Peter Hansen Jan 14 '10 at 01:10
0

Edit.

We refactor it like this.

matches = [ k for k in range(len(target)-len(key)) if target[k:].startswith(key) ]
matchesFound = len(matches)

We don't need C-style condition and assignment conflation.

Rarely do you simply want the count; the actual locations are available for free.

S.Lott
  • 384,516
  • 81
  • 508
  • 779