I have a working recursive function which I have to recursively call out in another function "depth" amount of times.
Working function:
def apply_dragon_rules(string):
"""
Write a recursive function that replaces characters in string.
Like so:
"a" -> "aRbFR"
"b" -> "LFaLb"
apply_dragon_rules("a") -> "aRbFR"
apply_dragon_rules("aa") -> "aRbFRaRbFR"
apply_dragon_rules("FRaFRb") -> "FRaRbFRFRLFaLb"
:param string: sentence with "a" and "b" characters that need to be replaced
:return: new sentence with "a" and "b" characters replaced
"""
if not string: # if the string is empty
return ""
elif string[0] == "b": # if the string start with b, replace it with a
return "LFaLb" + apply_dragon_rules(string[1:])
elif string[0] == "a": # if the string start with b, replace it with a
return "aRbFR" + apply_dragon_rules(string[1:])
else: # else, add this character and go to the next one
return string[0] + apply_dragon_rules(string[1:])
The function in which I have to recursively call out the working function "depth" times:
def curve(string, depth):
"""
Recursively generate the next depth of rules.
Calls apply_dragon_rules() function `depth` times.
curve("Fa", 2) -> "FaRbFRRLFaLbFR"
:param string: current instruction string
:param depth: how many times the rules are applied
:return: instructionset at iteration 'depth'
"""
if depth == 0:
return ""
elif depth == 1:
return apply_dragon_rules(string)
else:
return depth * apply_dragon_rules(apply_dragon_rules(string))
Expected output:
curve("Fa", 2) -> "FaRbFRRLFaLbFR"
My output(2x the expected output):
curve("Fa", 2) -> "FaRbFRRLFaLbFRFaRbFRRLFaLbFR"
I was able to produce a correct output by making function curve (although, that is not a recursive solution and only works if the "depth" is 2):
return apply_dragon_rules(apply_dragon_rules(string))
Any ideas how to implement depth into the curve function so it works as intended?
The second function doesn't need to be recursive. You can simply loop to achieve that effect in general.
def curve(string, depth):
if depth == 0:
return ""
else:
for i in range(depth):
string = apply_dragon_rules(string)
return string
print(curve("Fa", 2) == "FaRbFRRLFaLbFR") # Test result
A common approach is to pass the remaining depth as a parameter and make the base case of the recursion that this remaining depth is zero.
...which you already did halfway, but what's missing is to actually make the recursive call and pass the remaining depth—which is one less than the current depth:
def curve(string, depth):
if depth == 0:
return ""
else:
return curve(apply_dragon_rules(string), depth - 1)
Related
I am trying to pass a parameter "by value". I have tried making a deep copy of the parameter that is passed recursively in order to prevent any changes from circling back to the parent function calls.
Here is a snippet of code that tries to generate the array of all possible parentheses.
def generateParenthesis(n):
#Iterate for each move.
M = 2 * n
retArray = []
def recHelper(numMoves, perm, stack):
print("Function call: ", numMoves, perm, stack)
newPerm = copy.deepcopy(perm)
newStack = stack.copy()
#Base case, no moves
if (numMoves == 0):
retArray.append(newPerm)
return
#Case when left move is valid
if (numMoves != len(newStack)):
#Apply the left move. Pass it recursively
newPerm +='('
#Update the stack accordingly
newStack.append('(')
#Decrease numMoves
newNumMoves = numMoves - 1
#Call it recursively
recHelper(newNumMoves, newPerm, newStack)
#Case when right move is valid
if len(newStack) != 0:
#Apply the right move. Pass it recursively
newPerm +=')'
#Update the stack accordingly, delete the top, last elm
newStack.pop()
#Decrease numMoves
newNumMoves = numMoves - 1
#Call it recursively
recHelper(newNumMoves, newPerm, newStack)
#done
return
recHelper(M, "", [])
return retArray
Unfortunately, calling generateParenthesis(1) returns ['()','()(', '()()'] and not ['()'].
def generateParenthesis(n):
retArray = []
def append_solutions(partial, opened_p, closed_p):
if opened_p < n:
append_solutions(partial + '(', opened_p + 1, closed_p)
if closed_p < n and opened_p > closed_p:
append_solutions(partial + ')', opened_p, closed_p + 1)
if opened_p == closed_p == n and partial:
retArray.append(partial)
append_solutions('', 0, 0)
return retArray
print(generateParenthesis(1))
print(generateParenthesis(2))
print(generateParenthesis(3))
print(generateParenthesis(4))
print(generateParenthesis(5))
After 3 hours of simplifying ideas, I came with this working code.
Now it finds things like (()())() for generateParenthesis(4), for instance.
The code is very self explanatory. You keep a count of opened/closed parenthesis and only close parenthesis when there are correspondent opened.
I chose not to use a stack because since everything in Python is passed by "pointer copy", stacks (i.e. lists in your OP) would require a costly list(stack) in the function body, creating a local copy of the list.
Notice that I create new strings (partial + '(', for instance), and these new string objects are passed by "pointer copy" to recursion calls.
(Sorry I lack a better term now. But this is important)
Edit
Your solution has a problem with the variable newPerm. Its value is leaking into the second recursive function. Also, you need to understand that all your copying of values is not needed except for the stack.
See how your function can be simplified. I think it's working:
def generateParenthesisOP(n):
#Iterate for each move.
M = 2 * n
retArray = []
def recHelper(numMoves, perm, stack):
print("Function call: ", numMoves, perm, stack)
if numMoves == 0:
retArray.append(perm)
return
if numMoves != len(stack):
left_stack = list(stack)
left_stack.append('(')
recHelper(numMoves - 1, perm + '(', left_stack)
if len(stack) != 0:
right_stack = list(stack)
right_stack.pop()
recHelper(numMoves - 1, perm + ')', right_stack)
recHelper(M, "", [])
return retArray
Use + operator to add to a list rather than .append() behavior to best emulate pass by value behavior.
Python officially abides by pass-by-object-reference. In your case, when a list stack or perm passed and modified in the child function, the parent function's stack or perm will see the updated variable.
So far I have done this. I am stuck on recursion. I have no idea how to move forward, joining and reversing etc.
def callrecursion(s):
a=s.index('(')
z=len(s) - string[::-1].index(')') -1
newStr=s[a+1:z]
# Something is missing here i cant figure it out
print(newStr)
return newStr
def reverseParentheses(s):
if '(' in s:
return reverseParentheses(callrecursion(s))
print('wabba labba dub dub')
else:
return s
string='a(bcdefghijkl(mno)p)q'
reverseParentheses(string)
EXPECTED OUTPUT : "apmnolkjihgfedcbq"
def reverseParentheses(s):
if '(' in s:
posopen=s.find('(')
s=s[:posopen]+reverseParentheses(s[posopen+1:])
posclose=s.find(')',posopen+1)
s=s[:posopen]+s[posopen:posclose][::-1]+s[posclose+1:]
return s
string='a(bcdefghijkl(mno)p)q'
print(string)
print(reverseParentheses(string))
print('apmnolkjihgfedcbq') # your test
string='a(bc)(ef)g'
print(string)
print(reverseParentheses(string))
The idea is to go 'inward' as long as possible (where 'inward' does not even mean 'nesting', it goes as long as there are any opening parentheses), so the innermost pairs are flipped first, and then the rest as the recursion returns. This way 'parallel' parentheses seem to work too, while simple pairing of "first opening parentheses" with "last closing ones" do not handle them well. Or at least that is what I think.
Btw: recursion is just a convoluted replacement for rfind here:
def reverseParentheses(s):
while '(' in s:
posopen=s.rfind('(')
posclose=s.find(')',posopen+1)
s=s[:posopen]+s[posopen+1:posclose][::-1]+s[posclose+1:]
return s;
(... TBH: now I tried, and the recursive magic dies on empty parentheses () placed in the string, while this one works)
I've come up with tho following logic (assuming the parentheses are properly nested).
The base case is the absence of parentheses in s, so it is returned unchanged.
Otherwise we locate indices of leftmost and rightmost opening and closing parentheses
(taking care of possible string reversal, so ')' might appear opening and '(' -- as closing).
Having obtained beg and end the remaining job is quite simple: one has to pass the reversed substring contained between beg and end to the subsequent recursive call.
def reverseParentheses(s):
if s.find('(') == -1:
return s
if s.find('(') < s.find(')'):
beg, end = s.find('('), s.rfind(')')
else:
beg, end = s.find(')'), s.rfind('(')
return s[:beg] + reverseParentheses(s[beg + 1:end][::-1]) + s[end + 1:]
Assuming that number of opening and closing brackets always match, this might be the one of the simplest method to reverse words in parenthesis:
def reverse_parentheses(st: str) -> str:
while True:
split1 = st.split('(')
split2 = split1[-1].split(')')[0]
st = st.replace(f'({split2})', f'{split2[::-1]}')
if '(' not in st and ')' not in st:
return st
# s = "(abcd)"
# s = "(ed(et)el)"
# s = "(ed(et(oc))el)"
# s = "(u(love)i)"
s= "((ng)ipm(ca))"
reversed = reverse_parentheses(s)
print(reversed)
You have a few issues in your code, and much of the logic missing. This adapts your code and produces the desired output:
def callrecursion(s):
a=s.index('(')
# 's' not 'string'
z=len(s) - s[::-1].index(')') -1
newStr=s[a+1:z][::-1]
# Need to consider swapped parentheses
newStr=newStr.replace('(', "$") # Placeholder for other swap
newStr=newStr.replace(')', "(")
newStr=newStr.replace('$', ")")
#Need to recombine initial and trailing portions of original string
newStr = s[:a] + newStr + s[z+1:]
return newStr
def reverseParentheses(s):
if '(' in s:
return reverseParentheses(callrecursion(s))
print('wabba labba dub dub')
else:
return s
string='a(bcdefghijkl(mno)p)q'
print(reverseParentheses(string))
>>>apmnolkjihgfedcbq
While the existing O(n^2) solutions were sufficient here, this problem is solvable in O(n) time, and the solution is pretty fun.
The idea is to build a k-ary tree to represent our string, and traverse it with DFS. Each 'level' of the tree represents one layer of nested parentheses. There is one node for each set of parentheses, and one node for each letter, so there are only O(n) nodes in the tree.
For example, the tree-nodes at the top level are either:
A letter that is not contained in parentheses
A tree-node representing a pair of parentheses at the outermost layer of our string, which may have child tree-nodes
To get the effect of reversals, we can traverse the tree in a depth-first way recursively. Besides knowing our current node, we just need to know if we're in 'reverse mode': a boolean to tell us whether to visit our node's children from left to right, or right to left.
Every time we go down a level in our tree, whether we're in 'reverse mode' or not is flipped.
Python code:
class TreeNode:
def __init__(self, parent=None):
self.parent = parent
self.children = []
def reverseParentheses(s: str) -> str:
root_node = TreeNode()
curr_node = root_node
# Build the tree
for let in s:
# Go down a level-- new child
if let == '(':
new_child = TreeNode(parent=curr_node)
curr_node.children.append(new_child)
curr_node = new_child
# Go back to our parent
elif let == ')':
curr_node = curr_node.parent
else:
curr_node.children.append(let)
answer = []
def dfs(node, is_reversed: bool):
nonlocal answer
num_children = len(node.children)
if is_reversed:
range_start, range_end, range_step = num_children-1, -1, -1
else:
range_start, range_end, range_step = 0, num_children, 1
for i in range(range_start, range_end, range_step):
if isinstance(node.children[i], str):
answer.append(node.children[i])
else:
dfs(node.children[i], not is_reversed)
dfs(root_node, False)
return ''.join(answer)
Here is the correct version for your callrecursion function:
def callrecursion(text):
print(text)
a = text.find('(') + 1
z = text.rfind(')') + 1
newStr = text[:a - 1] + text[a:z-1][::-1].replace('(', ']').replace(')', '[').replace(']', ')').replace('[', '(') + text[z:]
return newStr
You probably have to take into account if the parethesis is the first/last character.
I'm working on a project to implement infix-to-postfix transformations in python.
The implementation of the code works as long as the expression is fully bracketed. It cannot handle expressions where humans would implicitly assume an order to the calculation.
For instance, I can use a fully-bracketed expression such as:
((3+15)*2)+(6-3)
And get the right result.
However, humans might normally write:
(3+15)*2+(6-3)
Where the first outer bracket is assumed.
Are there any algorithms that could correctly add brackets. If not, is there a best-practice solution for how to handle this sort of problem?
Update:
Here is the implementation of the parse tree function:
class BinaryTree:
def __init__(self, root):
self.key = root
self.left_child = None
self.right_child = None
def insert_left(self, new_node):
if self.left_child == None:
self.left_child = BinaryTree(new_node)
else:
t = BinaryTree(new_node)
t.left_child = self.left_child
self.left_child = t
def insert_right(self, new_node):
if self.right_child == None:
self.right_child = BinaryTree(new_node)
else:
t = BinaryTree(new_node)
t.right_child = self.right_child
self.right_child = t
def get_right_child(self):
return self.right_child
def get_left_child(self):
return self.left_child
def set_root_val(self, obj):
self.key = obj
def get_root_val(self):
return self.key
def build_parse_tree(fp_exp):
fp_list = re.findall('[+-/*//()]|\d+', fp_exp)
p_stack = Stack()
e_tree = BinaryTree('')
p_stack.push(e_tree)
current_tree = e_tree
for i in fp_list:
if i == '(':
current_tree.insert_left('')
p_stack.push(current_tree)
current_tree = current_tree.get_left_child()
elif i not in ['+', '-', '*', '/', ')']:
current_tree.set_root_val(int(i))
parent = p_stack.pop()
current_tree = parent
elif i in ['+', '-', '*', '/']:
current_tree.set_root_val(i)
current_tree.insert_right('')
p_stack.push(current_tree)
current_tree = current_tree.get_right_child()
elif i == ')':
current_tree = p_stack.pop()
else:
raise ValueError
return e_tree
def postorder(tree):
if tree != None:
postorder(tree.get_left_child())
postorder(tree.get_right_child())
print (tree.get_root_val())
The output from the second expression postorder:
3
6
15
2
3
-
+
The one with the first (correct) is:
3
15
+
6
2
3
-
+
Disclaimer: Sorry for that huge response; I was curious and just wrote down what I did during testing around a bit for your question. Find the entire code here: https://gist.github.com/jbndlr/3657fa890539d29c9e4b0311dc60835d
By the way, this is just test code and not meant to be used in production, as it may still be flawed.
Response
Your sequence parsing and tree setup with pushes of empty strings seems a bit odd, but I cannot accurately point to your error. Your parsing somehow swallows the * operator, probably because its left element is a closing bracket.
While I was playing around with this a bit, I tried to reproduce and came up with a solution that correctly parses simple equations and can generate the required parentheses. Even though no longer required, if the tree is already parsed correctly, you can use this to generate fully bracketed equations, or extend it by your own needs.
Preparation: The Imports
from __future__ import print_function
import re
Step 1: Tokenizing the Input
This function takes a string as an expression and generates a list of tuples representing your tokens. It also already classifies them as kind of simple (string-represented) types for later processing.
def tokenize(expression):
'''Generate tokens from a string following fixed rules.
'''
scanner = re.Scanner([
(r'[0-9]\.[0-9]+', lambda _, t: ('FLOAT', t)),
(r'[0-9]+', lambda _, t: ('INTEGER', t)),
(r'[a-z_]+', lambda _, t: ('IDENTIFIER', t)),
(r'\(', lambda _, t: ('P_OPEN', t)),
(r'\)', lambda _, t: ('P_CLOSE', t)),
(r'[+\-*/]', lambda _, t: ('OPERATOR', t)),
(r'\s+', None),
])
tokens, _ = scanner.scan(expression)
return tokens
This approach is by far not complete, but it is sufficient for demonstrating building binary parse trees. Note that the order of rules is important; it makes no difference here, as I do not catch single dots, but putting INTEGER before FLOAT could mess things up later.
Step 2: Parse the Hierarchy
The next function takes a list of tokens as generated in Step 1 and resolves all parts that are put into brackets as individual sub-lists. The result is a nested list where each previously bracketed part is shifted to a deeper level.
def parse(tokens, in_parens=False):
'''Parse a list of tokens that may contain brackets into a token hierarchy
where all brackets are removed and replaced by list nesting.
'''
cur = []
i = 0
while i < len(tokens):
t = tokens[i]
if t[0] == 'P_OPEN':
# If we hit an opening bracket, we memorize its position and search
# for the corresponding closing one by counting the stacked
# brackets.
pos_open = i
pos_close = None
par_stack = 0
for j, p in enumerate(tokens[i:]):
if p[0] == 'P_OPEN':
# Deeper nesting, increase.
par_stack += 1
elif p[0] == 'P_CLOSE':
# Level closed, decrease.
par_stack -= 1
if par_stack == 0:
# If we hit level 0, we found the corresponding closing
# bracket for the opening one.
pos_close = i + j
break
if pos_close is None:
# If we did not find a corresponding closing bracket, there
# must be some syntax error.
raise Exception('Syntax error; missing closing bracket.')
# For the bracketed subset we just found, we invoke a recursive
# parsing for its contents and append the result to our hierarchy.
elem = parse(tokens[i + 1:j], in_parens=True)
cur.append(elem)
i = j
elif t[0] == 'P_CLOSE':
if not in_parens:
# If we hit a closing bracket but are not searching for one, we
# found too many closing brackets, which is a syntax error.
raise Exception('Syntax error; too many closing brackets.')
return cur
else:
cur.append(t)
i += 1
return cur
This makes sure that we do not miss the explicit grouping given by parentheses in the expression. At the same time, as we count parenthesis levels, we can spot syntax errors that result from wrong bracket counts.
Step 3: Build a Tree
In order to proceed, we need to build an actual binary tree from our hierarchy. The hierarchy we got from Step 2 still has all un-bracketed chained operators on the same level, so we do not know yet about the order in which the operators need to be executed. This is what is solved now.
When creating a new Node from a hierarchy (i.e. a nested list of tokens), we search for a pivot element that we can use as the operator of the currently built Node. We choose the weakest binding operator, because we build the tree top-down, but it will be evaluated bottom-up. Hence, the operation that shall be performed last is the one we want to have in the upmost Node of our tree.
class Node(object):
def __init__(self, hierarchy, parent=None):
if len(hierarchy) == 1 and type(hierarchy[0]) is list:
hierarchy = hierarchy[0] # Bracketed descent
# Find position of operator that has the weakest binding priority and
# use it as pivot element to split the sequence at. The weakest binding
# is executed last, so it's the topmost node in the tree (which is
# evaluated bottom-up).
pivot = self._weakest_binding_position(hierarchy)
if pivot is not None:
self.left = Node(hierarchy[:pivot], parent=self)
self.op = hierarchy[pivot][1]
self.right = Node(hierarchy[pivot + 1:], parent=self)
else:
# There is no pivot element if there is no operator in our
# hierarchy. If so, we hit an atomic item and this node will be
# a leaf node.
self.value = hierarchy[0]
def _binding_order(self, operator):
'''Resolve operator to its binding order.'''
if operator in '+-':
return 1
elif operator in '*/':
return 2
raise Exception('Parsing error; operator binding cannot be assessed.')
def _weakest_binding_position(self, tokens):
'''Return position of operator with weakest binding (maintains LTR).'''
ops = sorted([
(i, self._binding_order(t[1]))
for i, t in enumerate(tokens)
if t[0] == 'OPERATOR'
], key=lambda e: e[1], reverse=True)
if len(ops) == 0:
if len(tokens) != 1:
raise Exception('Parsing error; found sequence w/o operator.')
return None
return ops[-1][0]
def isleaf(self):
if hasattr(self, 'value'):
return True
return False
def __str__(self):
if self.isleaf():
return str(self.value[1])
else:
return '({:s} {:s} {:s})'.format(self.left, self.op, self.right)
If you want to see how the tree is set up, just print(self) at the end of Node.__init__(). This will give you a bottom-up print of all nodes.
I added some parentheses in the Node.__str__() method to actually make a fully-bracketed expression from the input. You can verify with some samples like so:
if __name__ == '__main__':
expressions = [
'(3+15)*2+6-3',
'(a+15)*2+6/3'
]
for expr in expressions:
root = Node(parse(tokenize(expr)))
print(root)
... yields
>>> ((((3 + 15) * 2) + 6) - 3)
>>> (((a + 15) * 2) + (6 / 3))
So, if you want to print (or return) this in postfix notation now, you can just switch the operator and operands by changing this row in the Node.__str__() method:
<<<<<<<<
return '({:s} {:s} {:s})'.format(self.left, self.op, self.right)
======
return '({:s} {:s} {:s})'.format(self.left, self.right, self.op)
>>>>>>>>
If you want your postfix notation to be returned for further processing instead of just obtaining it as a string, just write another method like so (warning: pseudo-code):
def postfix(self):
if self.isleaf():
return self.value
else:
return (self.left.postfix(), self.right.postfix(), self.op)
and then invoke it from your root node:
pf = root.postfix()
Step 4: Evaluation
Finally, you can put a method into the Node class to evaluate the expression. This method checks whether or not we have a leaf node, and, if so, returns its value using the correct type. Otherwise, it evaluates its left and right child and applies the desired operator and then passes the result upwards the tree.
def eval(self, variables={}):
if self.isleaf():
ttype, value = self.value
if ttype == 'FLOAT':
return float(value)
elif ttype == 'INTEGER':
return int(value)
elif ttype == 'IDENTIFIER':
if value in variables.keys():
return variables[value]
else:
raise Exception('Unbound variable: {:s}'.format(value))
else:
raise Exception('Unknown type: {:s}'.format(ttype))
else:
left = self.left.eval(variables=variables)
right = self.right.eval(variables=variables)
if self.op == '+':
return left + right
elif self.op == '-':
return left - right
elif self.op == '*':
return left * right
elif self.op == '/':
return left / right
else:
raise Exception('Unknown operator: {:s}'.format(self.op))
Some special thing here is, that you can also use variables (like a in my example in Step 3), but you have to map them to actual (un-typed) values on evaluation:
if __name__ == '__main__':
expression = '(a+15)*2+6/3'
tokens = tokenize(expression)
hierarchy = parse(tokens)
root = Node(hierarchy)
print(root)
print(root.eval({'a': 7}))
... yields:
>>> (((a + 15) * 2) + (6 / 3))
>>> 46
Final Thoughts
As already stated, this is far from perfect. I even noticed, that it somehow fails to parse an expression, where a single operator connects two bracketed parts like (1-2)/(0+5) -- but I leave this to whoever wants to have a look at it ;)
Hope it helps somehow; and sorry for this huge response. I was just curious and had a little bit of spare time.
Im not sure how to get my recursion to work properly or keep from infinitely repeating.
This is what i have so far:
def listSquareR(lst):
if len(lst)== 1:
return lst[0]**2
else:
return lst[0]**2+[listSquareR(lst[1:])]
last return line is obviously wrong
Another possibility:
def listSquare(L):
if L: return [L[0]**2] + listSquare(L[1:])
else: return []
An even shorter version (as Cristian Ciupitu mentions below) is:
def listSquare(L):
return [L[0]**2] + listSquare(L[1:]) if L else []
You have it almost right, but the key is to mind your types. In your code:
def listSquareR(lst):
if len(lst)== 1:
return lst[0]**2 # Returning a number
else:
return lst[0]**2+[listSquareR(lst[1:])] # Returning a number plus a list of a list
We just need two small fixes:
def listSquareR(lst):
if len(lst)== 1:
return [lst[0]**2] # Returning a list
else:
return [lst[0]**2] + listSquareR(lst[1:]) # Returning a list plus a list
def SquareArea(width):
if width == 0:
return 0
else:
return SquareArea(width-1) + (2*width-1)
This is a recursive function I've recently used to find the area of a square.
And since the area of a square is Side*Side, one can use it to find the square of any function.
Now all that is required of you is to make a loop, eg:
for i in range (list):
and implement this function on i
Or maybe use while loop.
newList=[]
length = len(list)
while i != length:
newList.append(SquareArea(i))
And then return the newList
I understand that recursion is when a function calls itself, however I can't figure out how exactly to get my function to call it self to get the desired results. I need to simply count the vowels in the string given to the function.
def recVowelCount(s):
'return the number of vowels in s using a recursive computation'
vowelcount = 0
vowels = "aEiou".lower()
if s[0] in vowels:
vowelcount += 1
else:
???
I came up with this in the end, thanks to some insight from here.
def recVowelCount(s):
'return the number of vowels in s using a recursive computation'
vowels = "aeiouAEIOU"
if s == "":
return 0
elif s[0] in vowels:
return 1 + recVowelCount(s[1:])
else:
return 0 + recVowelCount(s[1:])
Try this, it's a simple solution:
def recVowelCount(s):
if not s:
return 0
return (1 if s[0] in 'aeiouAEIOU' else 0) + recVowelCount(s[1:])
It takes into account the case when the vowels are in either uppercase or lowercase. It might not be the most efficient way to traverse recursively a string (because each recursive call creates a new sliced string) but it's easy to understand:
Base case: if the string is empty, then it has zero vowels.
Recursive step: if the first character is a vowel add 1 to the solution, otherwise add 0. Either way, advance the recursion by removing the first character and continue traversing the rest of the string.
The second step will eventually reduce the string to zero length, therefore ending the recursion. Alternatively, the same procedure can be implemented using tail recursion - not that it makes any difference regarding performance, given that CPython doesn't implement tail recursion elimination.
def recVowelCount(s):
def loop(s, acc):
if not s:
return acc
return loop(s[1:], (1 if s[0] in 'aeiouAEIOU' else 0) + acc)
loop(s, 0)
Just for fun, if we remove the restriction that the solution has to be recursive, this is how I'd solve it:
def iterVowelCount(s):
vowels = frozenset('aeiouAEIOU')
return sum(1 for c in s if c in vowels)
Anyway this works:
recVowelCount('murcielago')
> 5
iterVowelCount('murcielago')
> 5
Your function probably needs to look generally like this:
if the string is empty, return 0.
if the string isn't empty and the first character is a vowel, return 1 + the result of a recursive call on the rest of the string
if the string isn't empty and the first character is not a vowel, return the result of a recursive call on the rest of the string.
Use slice to remove 1st character and test the others. You don't need an else block because you need to call the function for every case. If you put it in else block, then it will not be called, when your last character is vowel: -
### Improved Code
def recVowelCount(s):
'return the number of vowels in s using a recursive computation'
vowel_count = 0
# You should also declare your `vowels` string as class variable
vowels = "aEiou".lower()
if not s:
return 0
if s[0] in vowels:
return 1 + recVowelCount(s[1:])
return recVowelCount(s[1:])
# Invoke the function
print recVowelCount("rohit") # Prints 2
This will call your recursive function with new string with 1st character sliced.
this is the straightforward approach:
VOWELS = 'aeiouAEIOU'
def count_vowels(s):
if not s:
return 0
elif s[0] in VOWELS:
return 1 + count_vowels(s[1:])
else:
return 0 + count_vowels(s[1:])
here is the same with less code:
def count_vowels_short(s):
if not s:
return 0
return int(s[0] in VOWELS) + count_vowels_short(s[1:])
here is another one:
def count_vowels_tailrecursion(s, count=0):
return count if not s else count_vowels_tailrecursion(s[1:], count + int(s[0] in VOWELS))
Unfortunately, this will fail for long strings.
>>> medium_sized_string = str(range(1000))
>>> count_vowels(medium_sized_string)
...
RuntimeError: maximum recursion depth exceeded while calling a Python object
if this is something of interest, look at this blog article.
Here's a functional programming approach for you to study:
map_ = lambda func, lst: [func(lst[0])] + map_(func, lst[1:]) if lst else []
reduce_ = lambda func, lst, init: reduce_(func, lst[1:], func(init, lst[0])) if lst else init
add = lambda x, y: int(x) + int(y)
is_vowel = lambda a: a in 'aeiou'
s = 'How razorback-jumping frogs can level six piqued gymnasts!'
num_vowels = reduce_(add, map_(is_vowel, s), 0)
The idea is to divide the problem into two steps, where the first ("map") converts the data into another form (a letter -> 0/1) and the second ("reduce") collects converted items into one single value (the sum of 1's).
References:
http://en.wikipedia.org/wiki/Map_(higher-order_function)
http://en.wikipedia.org/wiki/Reduce_(higher-order_function)
http://en.wikipedia.org/wiki/MapReduce
Another, more advanced solution is to convert the problem into tail recursive and use a trampoline to eliminate the recursive call:
def count_vowels(s):
f = lambda s, n: lambda: f(s[1:], n + (s[0] in 'aeiou')) if s else n
t = f(s, 0)
while callable(t): t = t()
return t
Note that unlike naive solutions this one can work with very long strings without causing "recursion depth exceeded" errors.