Storing variable inside of recursive function (python) - python

I wrote a recursive function to find (0,0) tuples inside of a list.
def find_tuple(l, score = 0):
for i, item in enumerate(l):
try:
if item == 0 and l[i+1] == 0:
score = score + 1
print("Number of tuples: {}".format(score))
l = l[i+2:]
find_tuple(l, score)
except:
break
return score
When I've used it i noticed strange behaviour:
>>> find_tuple([0,0])
Number of tuples: 1
1
>>> find_tuple([0,0,0])
Number of tuples: 1
1
>>> find_tuple([0,0,0,0])
Number of tuples: 1
Number of tuples: 2
1
>>> find_tuple([0,0,0,0,0,0])
Number of tuples: 1
Number of tuples: 2
Number of tuples: 3
Number of tuples: 2
2
The function returned 1 instead of 2 and 2 instead of 3 in the last two examples. Why is this happening ?

You need to return the variable for the previous function call to catch it.
replace find_tuple(l, score) with return find_tuple(l, score) inside the if statement
def find_tuple(l, score = 0):
for i, item in enumerate(l):
try:
if item == 0 and l[i+1] == 0:
score = score + 1
print("Number of tuples: {}".format(score))
l = l[i+2:]
return find_tuple(l, score)
except:
break
return score

Your recursion logic doesn't seem correct to me. You don't need to use for loop in a recursive function. You handle a part of job, reduce the problem and call the same function again to rest of the problem. At the end, use the returned value from the function. Also, you need to put a condition to stop.
Something like this could work for your problem:
def find_tuple(l, score = 0):
if len(l)<=1:
return score
if l[0] == 0 and l[1] == 0:
score += 1
return find_tuple(l[2:], score)
print(find_tuple([0,0]))
print(find_tuple([0,0,0]))
print(find_tuple([0,0,0,0]))
print(find_tuple([0,0,0,0,0,0]))
Output:
1
1
2
3

Related

Cannot get += to work properly between functions in python

So my task is simple: I am to make two functions,
one which takes input from a user as a list of integers. (User enters how many entries, then begins entering them individually)
second function reads that list and returns how many times a chosen value was found in that list.
For some reason when combining these functions, the count does not stay at 0 and count whenever x is seen in the list; it just jumps to whatever the initial entry count was.
code is as follows:
def get_int_list_from_user():
list1 = []
numNums = int(input("Enter number count: "))
for x in range(numNums):
nextval = int(input("Enter a whole number: "))
list1.append(nextval)
return list1
def count_target_in_list(int_list):
target_val = int(input("Enter target value: "))
count = 0
for target_val in int_list:
count += 1
print("Target counted ", count, "time(s)")
return count
Over here is where I've tried different ways of calling the functions, but each time just ends up with the same result.
list1 = my_functions.get_int_list_from_user()
count = my_functions.count_target_in_list(int_list=list1)
I also tried this:
my_functions.count_target_in_list(int_list=my_functions.get_int_list_from_user())
This statement does not do what you think it does:
for target_val in int_list:
That essentially erases the original value passed into the function, and instead runs through the whole list, one element at a time. So, count will always be equal to the length of the list. You wanted:
for val in int_list:
if val == target_val:
count += 1
def count_target_in_list(int_list):
target_val = int(input("Enter target value: "))
count = 0
for target_val in int_list:
count += 1
print("Target counted ", count, "time(s)")
return count
instead, using this logic, you need to compare the loop values with the target value. in the code above the target value will be overridden by the iteration of the loop.
def count_target_in_list(int_list):
target_val = int(input("Enter target value: "))
count = 0
for value in int_list:
if target_val == value :
count += 1
print("Target counted ", count, "time(s)")
return count

Recursion function to count how many times a certain sequence appears

I am writing a recursive function that takes an integer as input and it will return the number of times 123 appears in the integer.
So for example:
print(onetwothree(123123999123))
Will print out 3 because the sequence 123 appears 3 times in the number I entered into the function.
Here is my code so far:
def onetwothree(x):
count = 0
while(x > 0):
x = x//10
count = count + 1
if (count < 3): #here i check if the digits is less than 3, it can't have the sequence 123 if it doesn't have 3 digits
return 0
if (x%10==1 and x//10%10 == 2 and x//10//10%10==3):
counter += 1
else:
return(onetwothree(x//10))
This keeps printing "0".
I think you are overthinking recursion. A solution like so should work:
def onetwothree(n, count=0):
if n <= 0:
return count
last_three_digits = n % 1000
n_without_last_number = n // 10
if last_three_digits == 123:
return onetwothree(n_without_last_number, count + 1)
else:
return onetwothree(n_without_last_number, count)
print(onetwothree(123123999123))
Outputs:
3
If you want to count how many times '123' occurs in a number, why not convert your number from an integer to a string and use str.count?
Then your function would look like this:
def onetwothree(x):
return str(x).count('123')
But it would also not be recursive anymore.
You could also just use the line print(str(123123999123).count('123')) which is pretty much the same as using it with the onetwothree function.
I hope this answer helps :)

Can't assign to literal with "for" loop

I am creating a Yahtzee program in Python. This function is meant to carry out the action that the user chooses (the user inputs a number, and it chooses the appropriate list item). I just got to the section about adding the total of one number set (the top part of a Yahtzee card with the ones, twos, etc.). I made a loop that adds one to the score for every 1 found in list dicevalues (a random list of "rolled dice" numbers; declared earlier in program).
I am getting the error on the for 1 in dicevalues: line. It says SyntaxError: cannot assign to literal. I looked up this error, but I'm not making sense of it. What I interpret here is that the program would run the code in the for block for every value 1 in dicevalues, but I'm not quite sure if you can use the for loop in that way.
def choiceAction():
if options[choice] == "Chance (score total of dice).":
global score
score += (a + b + c + d + e)
if options[choice] == "YAHTZEE!":
score += 50
if options[choice] == "Large straight":
score += 40
if options[choice] == "Small straight.":
score += 30
if options[choice] == "Four of a kind (total dice score).":
score += (a + b + c + d + e)
if options[choice] == "Three of a kind (total dice score).":
score += (a + b + c + d + e)
if options[choice] == "Full house.":
score += 25
if options[choice] == "Add all ones.":
for 1 in dicevalues: # <-- SyntaxError: can't assign to literal
score += 1
Is it possible that for some reason 1 cannot be in the for declaration?
If you don't want to use the items in dicevalues you can use a placeholder
for _ in dicevalues:
The error
When you write for x in dicevalues: you iterate over dicevalues and put each element in the variable x, so x can not be replaced with 1. This is why you get the error SyntaxError: can't assign to literal.
The solution(s)
Here are several solutions to perform what you want:
dicevalues = [2, 1, 3, 6, 4 ,1, 2, 1, 6]
# 1. Classic 'for' loop to iterate over dicevalues and check if element is equal to 1
score = 0
for i in dicevalues:
if i == 1:
score += 1
print(score) # 3
# 2. Comprehension to get only the elements equal to 1 in dicevalues, and sum them
score = 0
score += sum(i for i in dicevalues if i == 1)
print(score) # 3
# 3. The 'count()' method to count the number of elements equal to 1 in dicevalues
score = 0
score += dicevalues.count(1)
print(score) # 3

Division by values in a dictionary (Python)

I am building a prime generator (I know, another one and probably not a very good one at that, but that is for another question.) I am using a dictionary for my collection of primes and ignoring composites by dividing by the previous primes. However it doesn't seem to be iterating properly in the final stage of the function and I get a number of incorrect results. isWhole is a self explanatory call to another function. This is my code where x = the number of primes to be generated:
def prime_generator(x):
count = 2
counter = 2
p = {1: 2}
while len(p) <= x:
if count % 2 == 0:
count += 1
continue
test1 = (math.sqrt(count))
if isWhole(test1) == True:
count += 1
continue
for k, a in p.items():
if count % a == 0:
break
else:
p[ counter ] = count
counter += 1
break
count += 1
return p
Your design intent is not entirely clear, but you may be intending to have the else clause apply to the for loop rather than the if statement. Try un-indenting the entire else clause so that it only runs if the loop terminates without hitting a break.

string comparison in a while loop

I've written a small piece of code that should detect if there are any matching characters in the same place in the 2 strings. If there is , the score in incremented by 1, if there is 2 or more consecutive matching characters , the score is incremented by 3, if there is no matching character, the score is decremented by 1.
The problem is though , when i try to run the code, it gives me a error: string index out of range.
What might be wrong ? thank you very much.
def pairwiseScore(seqA, seqB):
count = 0
score = 0
while count < len(seqA):
if seqA[count] == seqB[count]:
score = score + 1
count = count + 1
while seqA[count] == seqB[count]: # This is the line the error occurs
score = score + 3
count = count + 1
elif seqA[count] != seqB[count]:
score = score - 1
count = count + 1
return score
Do both strings have the same lenght? otherwise you should consider using something like:
while count < min(len(seqA), len(seqB)):
Also, the zip function might come in handy here to pair off the letters in each word. It is a python builtin. e.g.
def letter_score(s1, s2):
score = 0
previous_match = False
z = zip(s1, s2)
for pair in z:
if pair[0] == pair[1]:
if previous_match:
score += 3
else:
score += 1
previous_match = True
else:
score -= 1
previous_match = False
return score
Indexes are numberd 0 to n.
len(someString)
will give you n + 1.
Let's say that your string is length 10, and the indexes are 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
Your while loop checks the condition that count is < 10. So far so good.
Ok now let's say that count is equal to 9. Immediately within the first while loop, you increment count.
So now count = 10.
Now attempting to access someString[count] will give you an IndexError because the indices only go up to 9.
The error message says it: You're trying to access a character beyond the boundaries of your string. Consider this:
>>> s = "hello"
>>> len(s)
5
>>> s[4]
'o'
Now when (at the start of the first while loop) count is 1 below len(seqA), then you're incrementing count and then you're doing seqA[count] which will throw this exception.
Let's assume you're calling pairwisescore("a", "a"):
score = 0
count = 0
while count < len(seqA): # 0 < 1 --> OK
if seqA[count] == seqB[count]: # "a" == "a" --> OK
score = score + 1 # score = 1
count = count + 1 # count = 1
while seqA[count] == seqB[count]: # seqA[1] doesn't exist!
In the second while loop you must test that count is less than len(seqA):
while count < len(seqA) and seqA[count] == seqB[count]:
...
and, also there's possibly other bug: If the length of seqB is less than length of seqA, you'll again see runtime exception. so, you should change every occurence of count < len(seqA) with count < min(len(seqA), len(seqB)).
def pairwiseScore(seqA, seqB):
count = 0
score = 0
isOne = False
isTwoOrMore = False
while count < min(len(seqA), len(seqB)):
if seqA[count] == seqB[count]:
if isTwoOrMore:
score = score + 3
count = count + 1
else:
if isOne:
isTwoOrMore = True
score = score + 1
count = count + 1
isOne = True
elif seqA[count] != seqB[count]:
score = score - 1
count = count + 1
isOne = False
isTwoOrMore = False
return score
a = 'apple'
b = 'aplle'
print(pairwiseScore(a, b))
I think this one solve the problem, I added a "counting" bool variable. And to respond to the question, your program didn't compare the length of the second string.
while count < min(len(seqA), len(seqB)):
The problem is that you do this:
count = count + 1
Both before the inner while loop and at the end of it. But you then continue using seqA[count] before checking it against len(seqA) again - so, once it goes too high, Python will try to read past the end of seqA, and you'll get that error (when, if the condition had been checked again after incrementing, the loop would have ended).
Using a Python for loop will get around bugs like this, since Python will manage count for you:
for a,b in zip(seqA, seqB):
if a == b:
score += 1
You can implement the extra-points bit easily in this by keeping track of whether the previous character is a match, rather than trying to work out how many after this one are. A boolean variable last_matched that you keep updated would help with this.

Categories