Why my loop is not exiting when after if condition I return
Question:
In this code I need to exit loop on certain condition. if position + 1 == len(triangle)
Maybe I am not good at Python and don’t understand clearly its behaviour.
It is not listening to my command and keep calling same function instead of leaving the loop.
The only other thing I tried is to call break in the loop itself when same condition is met but it is not working as well.
def max_value(list, index):
for _ in range(len(list)):
dictionary = dict()
maximum = max(list[index], list[index + 1])
dictionary['max'] = maximum
if maximum == list[index]:
dictionary['next_index'] = index
else:
dictionary['next_index'] = index + 1
return dictionary
total = 0
index = 0
skip = False
position = 0
def sliding_triangle(triangle):
global total
global index
global skip
global position
if not skip:
skip = True
total += triangle[0][0]
total += max_value(triangle[1], index).get("max")
index = max_value(triangle[1], index).get("next_index")
position = 2
sliding_triangle(triangle)
if position + 1 == len(triangle): return <-----------------HERE I AM EXPECTING IT TO EXIT
for row in range(position, len(triangle)):
values = max_value(triangle[row], index)
total += values.get("max")
index = values.get("next_index")
print(position, int(len(triangle)), index, values.get("max"), total)
position += 1
sliding_triangle(triangle)
return total
print(sliding_triangle([
[75],
[95, 64],
[17, 47, 82],
[18, 35, 87, 10],
[20, 4, 82, 47, 65],
[19, 1, 23, 75, 3, 34],
[88, 2, 77, 73, 7, 63, 67],
[99, 65, 4, 28, 6, 16, 70, 92],
[41, 41, 26, 56, 83, 40, 80, 70, 33],
[41, 48, 72, 33, 47, 32, 37, 16, 94, 29],
[53, 71, 44, 65, 25, 43, 91, 52, 97, 51, 14],
[70, 11, 33, 28, 77, 73, 17, 78, 39, 68, 17, 57],
[91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48],
[63, 66, 4, 68, 89, 53, 67, 30, 73, 16, 69, 87, 40, 31],
[ 4, 62, 98, 27, 23, 9, 70, 98, 73, 93, 38, 53, 60, 4, 23],
]))
Answers:
You can try using an else
and a pass
, like so:
def max_value():
# code
def sliding_triangle():
if not skip:
# code
if position + 1 == len(triangle):
pass
else:
for row in range(position, len(triangle)):
# code
return total
print sliding_triangle()
As far as I know, you can’t interrupt a def by throwing a return in two or more different points of the script just like in Java. Instead, you can just place a condition that, whether is respected, you skip to the return. Instead, you continue with the execution.
I synthesized your code to let you understand the logic easier, but it’s not a problem if I have to write it fully
Hehey, Got it working finally, so the solution was to break from loop earlier.
I had to put the condition in the beginning of the loop otherwise it was doing the same process and condition was wrong.
total = 0
index = 0
skip = False
position = 0
def max_value(list, index):
for _ in range(len(list)):
dictionary = dict()
maximum = max(list[index], list[index + 1])
dictionary['max'] = maximum
if maximum == list[index]:
dictionary['next_index'] = index
else:
dictionary['next_index'] = index + 1
return dictionary
def sliding_triangle(triangle):
global total
global index
global skip
global position
if not skip:
skip = True
total += triangle[0][0]
total += max_value(triangle[1], index).get("max")
index = max_value(triangle[1], index).get("next_index")
position = 2
sliding_triangle(triangle)
for row in range(position, len(triangle)):
if position == int(len(triangle)): break <<<--------------- I HAD TO CALL BREAK EARLIER, OTHERWISE FOR LOOP WAS KEEP WORKING INSTEAD OF STOPPING
values = max_value(triangle[row], index)
total += values.get("max")
index = values.get("next_index")
position += 1
sliding_triangle(triangle)
return total
print(sliding_triangle([
[75],
[95, 64],
[17, 47, 82],
[18, 35, 87, 10],
[20, 4, 82, 47, 65],
[19, 1, 23, 75, 3, 34],
[88, 2, 77, 73, 7, 63, 67],
[99, 65, 4, 28, 6, 16, 70, 92],
[41, 41, 26, 56, 83, 40, 80, 70, 33],
[41, 48, 72, 33, 47, 32, 37, 16, 94, 29],
[53, 71, 44, 65, 25, 43, 91, 52, 97, 51, 14],
[70, 11, 33, 28, 77, 73, 17, 78, 39, 68, 17, 57],
[91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48],
[63, 66, 4, 68, 89, 53, 67, 30, 73, 16, 69, 87, 40, 31],
[ 4, 62, 98, 27, 23, 9, 70, 98, 73, 93, 38, 53, 60, 4, 23],
]))
Recursive brute force solution
def sliding_triangle(triangle, row = 0, index = 0):
if row >= len(triangle) or index >= len(triangle[row]):
return 0 # row or index out of bounds
# Add parent value to max of child triangles
return triangle[row][index] + max(sliding_triangle(triangle, row+1, index), sliding_triangle(triangle, row+1, index+1))
Tests
print(sliding_triangle([[3], [7, 4], [2, 4, 6], [8, 5, 9, 3]]))
# Output: 23
print(sliding_triangle([
[75],
[95, 64],
[17, 47, 82],
[18, 35, 87, 10],
[20, 4, 82, 47, 65],
[19, 1, 23, 75, 3, 34],
[88, 2, 77, 73, 7, 63, 67],
[99, 65, 4, 28, 6, 16, 70, 92],
[41, 41, 26, 56, 83, 40, 80, 70, 33],
[41, 48, 72, 33, 47, 32, 37, 16, 94, 29],
[53, 71, 44, 65, 25, 43, 91, 52, 97, 51, 14],
[70, 11, 33, 28, 77, 73, 17, 78, 39, 68, 17, 57],
[91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48],
[63, 66, 4, 68, 89, 53, 67, 30, 73, 16, 69, 87, 40, 31],
[ 4, 62, 98, 27, 23, 9, 70, 98, 73, 93, 38, 53, 60, 4, 23],
]))
# Output: 1074
However, brute force approach times out on larges dataset
Optimized Solution
Apply memoization to brute force solution.
- Uses cache to avoid repeatedly solving for subpaths of a parent triangle node
Code
def sliding_triangle(triangle):
' Wrapper setup function '
def sliding_triangle_(row, index):
' Memoized function which does the calcs'
if row >= len(triangle) or index >= len(triangle[row]):
return 0
if not (row, index) in cache:
# Update cache
cache[(row, index)] = (triangle[row][index] +
max(sliding_triangle_(row+1, index),
sliding_triangle_(row+1, index+1)))
return cache[(row, index)]
cache = {} # init cache
return sliding_triangle_(0, 0) # calcuate starting from top most node
Tests
- Same results as brute force solution for simple test cases
- Works on large dataset i.e. https://projecteuler.net/project/resources/p067_triangle.txt
Find and Show Optimal Path*
- Modify Brute Force to Return Path
- Show highlighted path in triangle
Code
####### Main function
def sliding_triangle_path(triangle, row = 0, index = 0, path = None):
'''
Finds highest scoring path (using brute force)
'''
if path is None:
path = [(0, 0)] # Init path with top most triangle node
if row >= len(triangle) or index >= len(triangle[row]):
path.pop() # drop last item since place out of bounds
return path
# Select best path of child nodes
path_ = max(sliding_triangle_path(triangle, row+1, index, path + [(row+1, index)]),
sliding_triangle_path(triangle, row+1, index+1, path + [(row+1, index+1)]),
key = lambda p: score(triangle, p))
return path_
####### Utils
def getter(x, args):
'''
Gets element of multidimensional array using tuple as index
Source (Modified): https://stackoverflow.com/questions/40258083/recursive-itemgetter-for-python
'''
try:
for k in args:
x = x[k]
return x
except IndexError:
return 0
def score(tri, path):
' Score for a path through triangle tri '
return sum(getter(tri, t) for t in path)
def colored(r, g, b, text):
'''
Use rgb code to color text'
Source: https://www.codegrepper.com/code-examples/python/how+to+print+highlighted+text+in+python
'''
return "
In this code I need to exit loop on certain condition. if position + 1 == len(triangle)
Maybe I am not good at Python and don’t understand clearly its behaviour.
It is not listening to my command and keep calling same function instead of leaving the loop.
The only other thing I tried is to call break in the loop itself when same condition is met but it is not working as well.
def max_value(list, index):
for _ in range(len(list)):
dictionary = dict()
maximum = max(list[index], list[index + 1])
dictionary['max'] = maximum
if maximum == list[index]:
dictionary['next_index'] = index
else:
dictionary['next_index'] = index + 1
return dictionary
total = 0
index = 0
skip = False
position = 0
def sliding_triangle(triangle):
global total
global index
global skip
global position
if not skip:
skip = True
total += triangle[0][0]
total += max_value(triangle[1], index).get("max")
index = max_value(triangle[1], index).get("next_index")
position = 2
sliding_triangle(triangle)
if position + 1 == len(triangle): return <-----------------HERE I AM EXPECTING IT TO EXIT
for row in range(position, len(triangle)):
values = max_value(triangle[row], index)
total += values.get("max")
index = values.get("next_index")
print(position, int(len(triangle)), index, values.get("max"), total)
position += 1
sliding_triangle(triangle)
return total
print(sliding_triangle([
[75],
[95, 64],
[17, 47, 82],
[18, 35, 87, 10],
[20, 4, 82, 47, 65],
[19, 1, 23, 75, 3, 34],
[88, 2, 77, 73, 7, 63, 67],
[99, 65, 4, 28, 6, 16, 70, 92],
[41, 41, 26, 56, 83, 40, 80, 70, 33],
[41, 48, 72, 33, 47, 32, 37, 16, 94, 29],
[53, 71, 44, 65, 25, 43, 91, 52, 97, 51, 14],
[70, 11, 33, 28, 77, 73, 17, 78, 39, 68, 17, 57],
[91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48],
[63, 66, 4, 68, 89, 53, 67, 30, 73, 16, 69, 87, 40, 31],
[ 4, 62, 98, 27, 23, 9, 70, 98, 73, 93, 38, 53, 60, 4, 23],
]))
You can try using an else
and a pass
, like so:
def max_value():
# code
def sliding_triangle():
if not skip:
# code
if position + 1 == len(triangle):
pass
else:
for row in range(position, len(triangle)):
# code
return total
print sliding_triangle()
As far as I know, you can’t interrupt a def by throwing a return in two or more different points of the script just like in Java. Instead, you can just place a condition that, whether is respected, you skip to the return. Instead, you continue with the execution.
I synthesized your code to let you understand the logic easier, but it’s not a problem if I have to write it fully
Hehey, Got it working finally, so the solution was to break from loop earlier.
I had to put the condition in the beginning of the loop otherwise it was doing the same process and condition was wrong.
total = 0
index = 0
skip = False
position = 0
def max_value(list, index):
for _ in range(len(list)):
dictionary = dict()
maximum = max(list[index], list[index + 1])
dictionary['max'] = maximum
if maximum == list[index]:
dictionary['next_index'] = index
else:
dictionary['next_index'] = index + 1
return dictionary
def sliding_triangle(triangle):
global total
global index
global skip
global position
if not skip:
skip = True
total += triangle[0][0]
total += max_value(triangle[1], index).get("max")
index = max_value(triangle[1], index).get("next_index")
position = 2
sliding_triangle(triangle)
for row in range(position, len(triangle)):
if position == int(len(triangle)): break <<<--------------- I HAD TO CALL BREAK EARLIER, OTHERWISE FOR LOOP WAS KEEP WORKING INSTEAD OF STOPPING
values = max_value(triangle[row], index)
total += values.get("max")
index = values.get("next_index")
position += 1
sliding_triangle(triangle)
return total
print(sliding_triangle([
[75],
[95, 64],
[17, 47, 82],
[18, 35, 87, 10],
[20, 4, 82, 47, 65],
[19, 1, 23, 75, 3, 34],
[88, 2, 77, 73, 7, 63, 67],
[99, 65, 4, 28, 6, 16, 70, 92],
[41, 41, 26, 56, 83, 40, 80, 70, 33],
[41, 48, 72, 33, 47, 32, 37, 16, 94, 29],
[53, 71, 44, 65, 25, 43, 91, 52, 97, 51, 14],
[70, 11, 33, 28, 77, 73, 17, 78, 39, 68, 17, 57],
[91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48],
[63, 66, 4, 68, 89, 53, 67, 30, 73, 16, 69, 87, 40, 31],
[ 4, 62, 98, 27, 23, 9, 70, 98, 73, 93, 38, 53, 60, 4, 23],
]))
Recursive brute force solution
def sliding_triangle(triangle, row = 0, index = 0):
if row >= len(triangle) or index >= len(triangle[row]):
return 0 # row or index out of bounds
# Add parent value to max of child triangles
return triangle[row][index] + max(sliding_triangle(triangle, row+1, index), sliding_triangle(triangle, row+1, index+1))
Tests
print(sliding_triangle([[3], [7, 4], [2, 4, 6], [8, 5, 9, 3]]))
# Output: 23
print(sliding_triangle([
[75],
[95, 64],
[17, 47, 82],
[18, 35, 87, 10],
[20, 4, 82, 47, 65],
[19, 1, 23, 75, 3, 34],
[88, 2, 77, 73, 7, 63, 67],
[99, 65, 4, 28, 6, 16, 70, 92],
[41, 41, 26, 56, 83, 40, 80, 70, 33],
[41, 48, 72, 33, 47, 32, 37, 16, 94, 29],
[53, 71, 44, 65, 25, 43, 91, 52, 97, 51, 14],
[70, 11, 33, 28, 77, 73, 17, 78, 39, 68, 17, 57],
[91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48],
[63, 66, 4, 68, 89, 53, 67, 30, 73, 16, 69, 87, 40, 31],
[ 4, 62, 98, 27, 23, 9, 70, 98, 73, 93, 38, 53, 60, 4, 23],
]))
# Output: 1074
However, brute force approach times out on larges dataset
Optimized Solution
Apply memoization to brute force solution.
- Uses cache to avoid repeatedly solving for subpaths of a parent triangle node
Code
def sliding_triangle(triangle):
' Wrapper setup function '
def sliding_triangle_(row, index):
' Memoized function which does the calcs'
if row >= len(triangle) or index >= len(triangle[row]):
return 0
if not (row, index) in cache:
# Update cache
cache[(row, index)] = (triangle[row][index] +
max(sliding_triangle_(row+1, index),
sliding_triangle_(row+1, index+1)))
return cache[(row, index)]
cache = {} # init cache
return sliding_triangle_(0, 0) # calcuate starting from top most node
Tests
- Same results as brute force solution for simple test cases
- Works on large dataset i.e. https://projecteuler.net/project/resources/p067_triangle.txt
Find and Show Optimal Path*
- Modify Brute Force to Return Path
- Show highlighted path in triangle
Code
####### Main function
def sliding_triangle_path(triangle, row = 0, index = 0, path = None):
'''
Finds highest scoring path (using brute force)
'''
if path is None:
path = [(0, 0)] # Init path with top most triangle node
if row >= len(triangle) or index >= len(triangle[row]):
path.pop() # drop last item since place out of bounds
return path
# Select best path of child nodes
path_ = max(sliding_triangle_path(triangle, row+1, index, path + [(row+1, index)]),
sliding_triangle_path(triangle, row+1, index+1, path + [(row+1, index+1)]),
key = lambda p: score(triangle, p))
return path_
####### Utils
def getter(x, args):
'''
Gets element of multidimensional array using tuple as index
Source (Modified): https://stackoverflow.com/questions/40258083/recursive-itemgetter-for-python
'''
try:
for k in args:
x = x[k]
return x
except IndexError:
return 0
def score(tri, path):
' Score for a path through triangle tri '
return sum(getter(tri, t) for t in path)
def colored(r, g, b, text):
'''
Use rgb code to color text'
Source: https://www.codegrepper.com/code-examples/python/how+to+print+highlighted+text+in+python
'''
return "