Home > Software engineering >  Can someone tell me why after returning a boolean of False my code goes backwards and chooses True a
Can someone tell me why after returning a boolean of False my code goes backwards and chooses True a

Time:05-02

Can someone tell me why after returning a boolean of False my code goes backwards and chooses True also even though the False was the right output I see this happening in the debugger, I only found this happening with this specific example. So the function of my code is to take a pattern from user and see if it matches the word given by the user, the format of a pattern is "?ead" which would match with the word lead, the question mark is just a placeholder for any character so it can be anything but every other character has to be the same.

n = 0
def match(pattern, word):
    global n
    if n != len(word): # setting a base case for the recurrsion so it stops at the end of the word
        
        if pattern == "" and word == "":
            pass
        elif pattern == '' or word == '':
            pass
        elif len(word) != len(pattern):
            return False 
        
        if word[n:n 1] == pattern[n:n 1] or pattern[n:n 1] == '?': # goes through each character and compares them for each
            n = n  1
            match(pattern, word)
            print("same")
            return True  # This is where the code goes back to and returns true after already returning false down below in the else statement.
            
        else:
            print("not same")
            return False
    
match("?ut","cat")

The pattern my code fails for "?ut" and the word is "cat", the code sees that the first character is fine since "?" is just a placeholder and it also sees that the second term does not match so it should evaluate to false which it does but then immediately exits and evaluates to true and all this needs to be done with recursion and no loops.

CodePudding user response:

You are trying to do recursion here. It can be done like this:

def match(pattern, word):
    #Little debug for you to see what is happening
    print (pattern, word)
    if len(pattern) != len(word):
        return False
    if not word and not pattern:
        # last round with empty strings, all same this far
        return True
    if pattern[0] != word[0] and pattern[0] != "?":
        return False
    # reduce both strings and go to next round
    return match(pattern[1:], word[1:])

So you reduce both pattern and word and if you find that they don't match return false. If you get to the end, return True.

CodePudding user response:

You do not return the result of match but return True. You should simplify your cases.

if word[n:n 1] == pattern[n:n 1] or pattern[n:n 1] == '?':
    n = n  1
    match(pattern, word)  # this can return True OR False
    print("same")
    return True           # You always return True
                          # return match(pattern, word)  instead

There is no need for a global "counter" - if you recurse, shorten your pattern/word until nothing is left:

def match(pattern, word):
    if len(pattern) != len(word):
        return False
    
    # done all I can do
    if pattern == "" and word == "":
        return True

    # no comparison if pattern is ?
    if pattern[0] == "?":
        # recurse with 1 shorter pattern/word
        return match(pattern[1:], word[1:])

    # same letters
    elif pattern[0] == word[0]:
        # recurse with 1 shorter pattern/word
        return match(pattern[1:], word[1:])

    # different
    return False
    
for pat,wrd in [("?ut","cat"), ("ok","ok"), ("?k","ok"), 
                ("o?","ok"), ("",""), ("tooShort","veryLongWord")]:
    print(pat,wrd, "=>", "same" if match(pat,wrd) else "different") 

Output:

?ut cat => different
ok ok => same
?k ok => same
o? ok => same
=> same
tooShort veryLongWord => different

Or use the "short" version that does not recurse:

def match(pattern, word):
    return len(pattern)==len(word) and all( 
        (p==w or p == "?" for p,w in zip(pattern, word)))

CodePudding user response:

You should return the result you get back from recursion. Currently you ignore it.

There are some other issues:

  • Don't use global variables. If you make another call to match, then n will still be the value it had after the first call, and so you'll get an unreliable result from the second call. Instead make it an argument to your function. You can give it a default value.

  • The condition if pattern == "" and word == "": can never be true, since you already had assured that n != len(word), and as n starts with 0, that if condition would have been false if word was the empty string.

  • When you pass in the first if blocks, the other code below is still executed, and so you'd make an invalid index reference in the string.

  • Adding another condition where you delimit strings with single quotes instead of double quotes is not changing anything. You still compare the same strings.

  • It is overkill to compare slices of one character. Just retrieve the single character you want to compare. So word[n] instead of word[n:n 1]

  • Don't print the result inside the function. Print at the caller's side.

Correction:

def match(pattern, word, n=0):
    if len(word) != len(pattern):
        return False 
    if n >= len(word):
        return True
    if word[n] != pattern[n] and pattern[n] != '?':
        return False
    return match(pattern, word, n   1)
    
print(match("?ut","cat"))

Not your question, but this kind of recursion is a case of tail recursion, and so it could be written with simple iteration:

def match(pattern, word):
    if len(word) != len(pattern):
        return False 
    for i, pat in enumerate(pattern):
        if word[i] != pat and pat != '?':
            return False
    return True

When you are confortable with comprehension syntax you can write that also like this:

def match(pattern, word):
    return len(word) == len(pattern) and all(
        pat in ('?', ch) for ch, pat in zip(word, pattern)
    )
  • Related