turn lisp command to nested lists in python - python-3.x

I am building a lisp parser in python 3.7.
Imagine I have this list program as a string
"(begin (define r 10) (* pi (* r r)))"
which I tokenize using:
def tokenize(string):
return string.replace('(', ' ( ').replace(')', ' ) ').split()
returning
['(', 'begin', '(', 'define', 'r', '10', ')', '(', '*', 'pi', '(', '*', 'r', 'r', ')', ')', ')']
Now I am trying to build a function that reads from this list of tokens and returns this.
['begin', ['define', 'r', '10'], ['*', 'pi', ['*', 'r', 'r']]]
Any idea is welcome.

Here is (at last!) the programme i created using recursion.
class LisParser:
"""class expecting a lisp program to recursively walk through."""
def __init__(self):
self.program = input(("Please provide lisp programme "
"you want to pythonize"
"(no need to pass the program as a string) : "))
self.sub_program_sep = "("
self.program_stack = self._tokenize_program()
self.atom_is_int = self._is_int
self.atom_is_flt = self._is_flt
self.recursive_unpack = self.recursive_unpack
def _tokenize_program(self):
"""func that splits a lisp program on white spaces ensuring parentheses tokenisation."""
# if accidental multiple spaces, join method collapses them before padding parentheses before and after.
tokens = " ".join(self.program.split()).replace('(', ' ( ').replace(')', ' ) ')
# user might have inputted lisp program as python string i.e. "program". If so, get rid of double quotes.
return (self.program.startswith('"')
and tokens.split()[1:-1]
or tokens.split())
#staticmethod
def _walk_stack(stack):
"""func returning the popped element at index 0 of the stack."""
return stack.pop(0)
#staticmethod
def _is_flt(atom):
"""func trying to turn an atom to an float, else throws error."""
try:
float(atom)
return True
except ValueError:
return False
#staticmethod
def _is_int(atom):
"""func trying to turn an atom to an int, else throws error."""
try:
int(atom)
return True
except ValueError:
return False
def _to_py_type(self, atom):
"""func that trying to an atom to an int, then a float and finally a string."""
return ((self.atom_is_int(atom) and int(atom)) or
(self.atom_is_flt(atom) and float(atom)) or
str(atom))
def recursive_unpack(self):
# _walk_stack pops the first element off the stack.
stack_head = self._walk_stack(stack=self.program_stack)
# if token is an atom, convert to python type.
if stack_head != self.sub_program_sep:
return self._to_py_type(atom=stack_head)
# "(" starts a sub_program, needs to be in its own unit (list).
# The nested lists will represent the ast of the lisp program.
elif stack_head == self.sub_program_sep:
ast = list()
# recursion base case is the end of the sub_program with ")".
while self.program_stack[0] != ")":
ast.append(self.recursive_unpack())
else:
# remove the closing parent, so that walk_atom() will return the atom, not the closing paren.
self.program_stack.remove(")")
return ast
if __name__ == '__main__':
parser = LisParser()
# when prompted, type "(first (list 1 (+ 2 3) 9))"
var = parser.recursive_unpack()
print(var)
# ['first', ['list', 1, ['+', 2, 3], 9]]

Related

Alien Dictionary Python

Alien Dictionary
Link to the online judge -> LINK
Given a sorted dictionary of an alien language having N words and k starting alphabets of standard dictionary. Find the order of characters in the alien language.
Note: Many orders may be possible for a particular test case, thus you may return any valid order and output will be 1 if the order of string returned by the function is correct else 0 denoting incorrect string returned.
Example 1:
Input:
N = 5, K = 4
dict = {"baa","abcd","abca","cab","cad"}
Output:
1
Explanation:
Here order of characters is
'b', 'd', 'a', 'c' Note that words are sorted
and in the given language "baa" comes before
"abcd", therefore 'b' is before 'a' in output.
Similarly we can find other orders.
My working code:
from collections import defaultdict
class Solution:
def __init__(self):
self.vertList = defaultdict(list)
def addEdge(self,u,v):
self.vertList[u].append(v)
def topologicalSortDFS(self,givenV,visited,stack):
visited.add(givenV)
for nbr in self.vertList[givenV]:
if nbr not in visited:
self.topologicalSortDFS(nbr,visited,stack)
stack.append(givenV)
def findOrder(self,dict, N, K):
list1 = dict
for i in range(len(list1)-1):
word1 = list1[i]
word2 = list1[i+1]
rangej = min(len(word1),len(word2))
for j in range(rangej):
if word1[j] != word2[j]:
u = word1[j]
v = word2[j]
self.addEdge(u,v)
break
stack = []
visited = set()
vlist = [v for v in self.vertList]
for v in vlist:
if v not in visited:
self.topologicalSortDFS(v,visited,stack)
result = " ".join(stack[::-1])
return result
#{
# Driver Code Starts
#Initial Template for Python 3
class sort_by_order:
def __init__(self,s):
self.priority = {}
for i in range(len(s)):
self.priority[s[i]] = i
def transform(self,word):
new_word = ''
for c in word:
new_word += chr( ord('a') + self.priority[c] )
return new_word
def sort_this_list(self,lst):
lst.sort(key = self.transform)
if __name__ == '__main__':
t=int(input())
for _ in range(t):
line=input().strip().split()
n=int(line[0])
k=int(line[1])
alien_dict = [x for x in input().strip().split()]
duplicate_dict = alien_dict.copy()
ob=Solution()
order = ob.findOrder(alien_dict,n,k)
x = sort_by_order(order)
x.sort_this_list(duplicate_dict)
if duplicate_dict == alien_dict:
print(1)
else:
print(0)
My problem:
The code runs fine for the test cases that are given in the example but fails for ["baa", "abcd", "abca", "cab", "cad"]
It throws the following error for this input:
Runtime Error:
Runtime ErrorTraceback (most recent call last):
File "/home/e2beefe97937f518a410813879a35789.py", line 73, in <module>
x.sort_this_list(duplicate_dict)
File "/home/e2beefe97937f518a410813879a35789.py", line 58, in sort_this_list
lst.sort(key = self.transform)
File "/home/e2beefe97937f518a410813879a35789.py", line 54, in transform
new_word += chr( ord('a') + self.priority[c] )
KeyError: 'f'
Running in some other IDE:
If I explicitly give this input using some other IDE then the output I'm getting is b d a c
Interesting problem. Your idea is correct, it is a partially ordered set you can build a directed acyclcic graph and find an ordered list of vertices using topological sort.
The reason for your program to fail is because not all the letters that possibly some letters will not be added to your vertList.
Spoiler: adding the following line somewhere in your code solves the issue
vlist = [chr(ord('a') + v) for v in range(K)]
A simple failing example
Consider the input
2 4
baa abd
This will determine the following vertList
{"b": ["a"]}
The only constraint is that b must come before a in this alphabet. Your code returns the alphabet b a, since the letter d is not present you the driver code will produce an error when trying to check your solution. In my opinion it should simply output 0 in this situation.

Search not working for river crossing problem in python

The problem of the river crossing description:
We have a farmer, a wolf, a goat and a cabbage and they need to cross the river with the following restrictions:
The wolf can’t be on the shame side with the goat
The goat can’t stay at the same side with the cabbage
-Initial state: (‘L’,‘L’,‘L’,‘L’)
-Finale state: (‘R’,‘R’,‘R’,‘R’)
The state list describes where everyone is, (farmer, wolf, goat, cabbage) so state(‘L’,‘R’,‘L’,‘R’) means that the wolf and the cabbage are on the right side and the farmer and the goat are on the left side of the river. I don’t want to make it more complex by implementing list of lists.
The problem with my code is that it starts with the initial state and after the first step (farmer and goat on the right) it stops. I can’t find any way to make the recursion to work in order to have an acceptable result. So I could use some help in order to make the recursion functional and work as intended.
Thank you in advance.
""" ----------------------------------------------------------------------------
******** Search Code for DFS and other search methods
******** (expanding front and extending queue)
******** author:
"""
import copy
#left, right of the river
spaces = {
'R': ['L'],
'L': ['R']
}
def enter(state):
if state[0]==state[1] and state[2]==state[3]:
new_state=state=['R', state[1]] + ['R', state[3]]
return new_state
#function that swaps the side of the river that the farmer is
def swap(state_l, i, j):
state_l[i] = state_l[j]
return state_l
'''
operators for the movement of the farmer
'''
def neighbour1(state):
elem=['L']
i=state.index(elem) if elem in state else -1 #checking if the elem is in the state
if i >=0:
swap(state, i, spaces[i][0])
return state
def neighbour2(state):
elem=['L']
i=state.index(elem) if elem in state else -1
if i >=0:
swap(state, i, spaces[i][1])
return state
""" ----------------------------------------------------------------------------
**** FRONT managment
****
"""
def find_children(state):
children=[]
enter_state=copy.deepcopy(state)
enter_child=enter(enter_state)
tr1_state=copy.deepcopy(state)
tr1_child=neighbour1(tr1_state)
tr2_state=copy.deepcopy(state)
tr2_child=neighbour2(tr2_state)
if tr1_child is not None:
children.append(tr1_child)
if tr2_child is not None:
children.append(tr2_child)
if enter_child is not None:
children.append(enter_child)
return children
""" ----------------------------------------------------------------------------
** initialization of front
**
"""
def make_front(state):
return [state]
""" ----------------------------------------------------------------------------
**** expanding front
****
"""
def expand_front(front, method):
if method=='DFS':
if front:
print("Front:")
print(front)
node=front.pop(0)
for child in find_children(node):
front.insert(0,child)
elif method=='BFS':
if front:
print("Front:")
print(front)
node=front.pop(0)
for child in find_children(node):
front.append(child)
return front
'''
elif method=='BestFS':
#'''
#else: "other methods to be added"
""" ----------------------------------------------------------------------------
**** QUEUE
****
"""
""" ----------------------------------------------------------------------------
** initialization of queue
**
"""
def make_queue(state):
return [[state]]
""" ----------------------------------------------------------------------------
**** expanding queue
**** επέκταση ουράς
"""
def extend_queue(queue, method):
if method=='DFS':
print("Queue:")
print(queue)
node=queue.pop(0)
queue_copy=copy.deepcopy(queue)
children=find_children(node[-1])
for child in children:
path=copy.deepcopy(node)
path.append(child)
queue_copy.insert(0,path)
elif method=='BFS':
if queue:
print("Queue:")
print(queue)
node=queue.pop(0)
queue_copy=copy.deepcopy(queue)
children=find_children(node[-1])
for child in children:
path=copy.deepcopy(node)
path.append(child)
queue_copy.append(path)
'''
elif method=='BestFS':
#'''
#else: "other methods to be added"
return queue_copy
""" ----------------------------------------------------------------------------
**** Basic recursive function to create search tree (recursive tree expansion)
****
"""
def find_solution(front, queue, closed, method):
if not front:
print('_NO_SOLUTION_FOUND_')
elif front[0] in closed:
new_front=copy.deepcopy(front)
new_front.pop(0)
new_queue=copy.deepcopy(queue)
new_queue.pop(0)
find_solution(new_front, new_queue, closed, method)
elif is_goal_state(front[0]):
print('_GOAL_FOUND_')
print(queue[0])
else:
closed.append(front[0])
front_copy=copy.deepcopy(front)
front_children=expand_front(front_copy, method)
queue_copy=copy.deepcopy(queue)
queue_children=extend_queue(queue_copy, method)
closed_copy=copy.deepcopy(closed)
find_solution(front_children, queue_children, closed_copy, method)
"""" ----------------------------------------------------------------------------
** Executing the code
**
"""
def is_goal_state(state):
if state == ['R','R','R','R']:
return True
def main():
initial_state = ['L','L','L','L']
method='DFS'
""" ----------------------------------------------------------------------------
**** starting search
****
"""
print('____BEGIN__SEARCHING____')
find_solution(make_front(initial_state), make_queue(initial_state), [], method)
if __name__ == "__main__":
main()
The Farmer Problem
I'm going to approach your problem from a different angle. Instead of debugging your problem I've solved it in a step-by-step method that has tests as we go.
The setup and rules
First lets define the situation and the rules
FARMER = 0
WOLF = 1
GOAT = 2
CABBAGE = 3
START_STATE = ['L','L','L','L']
def wolfRule(state):
return state[FARMER] == state[WOLF] or state[WOLF] != state[GOAT]
assert( wolfRule(['L','L','L','L']) == True)
assert( wolfRule(['R','L','L','L']) == False)
assert( wolfRule(['R','L','R','L']) == True)
def goatRule(state):
return state[FARMER] == state[GOAT] or state[GOAT] != state[CABBAGE]
assert( goatRule(['L','L','L','L']) == True)
assert( goatRule(['R','L','L','L']) == False)
assert( goatRule(['R','L','L','R']) == True)
def validRule(state):
return wolfRule(state) and goatRule(state)
def winRule(state):
return state == ['R','R','R','R']
assert( winRule(['L','L','L','L']) == False)
assert( winRule(['R','L','L','L']) == False)
assert( winRule(['R','R','R','R']) == True)
We have defined each rule, added some assert statements so we know they work, and when we run the above code we can be sure it all is good-to-go.
Generating moves
Part of the recursive search is to generate the next valid move. We will do this in two parts. The first just generates all possible moves, and the second part will filter out only the valid moves
def generateMoves(state):
# The farmer moves to the other side and can bring 0 or 1 other things so long as it is on the same starting side
for other in [FARMER, WOLF, GOAT, CABBAGE]:
if state[FARMER] == state[other]:
move = copy(state)
move[FARMER] = 'L' if state[FARMER] == 'R' else 'R'
move[other] = 'L' if state[other] == 'R' else 'R'
yield move
assert( list(generateMoves(START_STATE)) == [['R','L','L','L'],['R','R','L','L'],['R','L','R','L'],['R','L','L','R']] )
Again, we add a test to make sure it is doing what we expect when we generate some moves
def validMoves(state_list):
return [ state for state in state_list if validRule(state)]
assert( list(validMoves(generateMoves(START_STATE))) == [['R','L','R','L']] )
Indeed the only first valid move is for the farmer to move the goat!
Depth First Search
Now we do a depth first search, using a previous_states list to keep track of where we have been. This function only returns a winning answer.
def depthFirstSearch(state, previous_states):
previous_states.append(state)
if winRule(state):
return previous_states
for move in validMoves(generateMoves(state)):
if move not in previous_states:
result = depthFirstSearch(move, previous_states)
if result is not None:
return result
previous_states.pop()
return None
again, at least one test to make sure it is giving expected results
assert( depthFirstSearch(['L','R','L','R'],[]) == [['L','R','L','R'],['R','R','R','R']] )
Final output
We run it
depthFirstSearch(START_STATE,[])
and get the solution!
[['L', 'L', 'L', 'L'],
['R', 'L', 'R', 'L'],
['L', 'L', 'R', 'L'],
['R', 'R', 'R', 'L'],
['L', 'R', 'L', 'L'],
['R', 'R', 'L', 'R'],
['L', 'R', 'L', 'R'],
['R', 'R', 'R', 'R']]

A simple Python program to study classes

For the sake of studying the concept of classes in Python, I have written a program which is meant to calculate the average of a tuple of numbers. However, the program returns an error message which is quoted.
#!/usr/bin/python3
"""
Python program to calculate the average value of
a set of integers or float numbers.
Input format: a tuple, e.g. (1,2,3)
When run, the program generates an error message in line 27
"""
class Mean_value():
def __init__(self, operand):
self.operand = operand
def calculate_average(self, operand):
self.operand = operand
all_in_all = sum(operand)
nmbr = len(operand)
average = all_in_all/nmbr
self.average = average
return self.average
operand = input("Key in numbers as a tuple: ")
print(operand) #temp, the operand is taken in by the program
x = Mean_value.calculate_average(operand) #line 27
print(x)
The error message:
Traceback (most recent call last):
File "D:\Python\Exercise76a.py", line 27, in <module>
x = Mean_value.calculate_average(operand)
TypeError: calculate_average() missing 1 required positional argument: 'operand'
I would highly appreciate any hints from members more experienced than myself.
Any method in your class with self as the first parameter is an instance method, meaning it's supposed to be called on an instance of the class and not on the class itself.
In other words, the self parameter isn't just for show. When you do this:
x = Mean_value(operand)
x.calculate_average(operand)
the python interpreter actually takes x and passes it through to the function as the first parameter (i.e. self). Hence, when you try to call calculate_average() on the class Mean_value instead of on an object of that type, it only passes one of the two required parameters (there's no instance to pass automatically as self, so it just passes the one argument you've given it, leaving the second argument unspecified).
If you want to have a method be static (called on the class instead of on an instance of the class), you should use the #staticmethod decorator on the method in question, and omit the self parameter.
Another way to fix this error is to make your calculate_average method static. Like this:
#staticmethod
def calculate_average(operand):
# but be careful here as you can't access properties with self here.
all_in_all = sum(operand)
nmbr = len(operand)
average = all_in_all/nmbr
return average
The program contains comments
#!/usr/bin/python3
"""
The program computes the average value of a sequence of positive integers,
keyed in as a tuple.
After entering the tuple, the input function returns a string,
e.g.(1,2,3) (tuple) --> (1,2,3) (string).
On screen the two objects look the same.
The major code block deals with reversing the type of the input,
i.e. string --> tuple,
e.g. (1,2,3) (string) --> (1,2,3) (tuple).
The maths is dealt with by the class Average.
"""
class Average:
def __init__(self, tup):
self.tup = tup
def calculate(self):
return sum(self.tup)/len(self.tup)
"""Major code block begins ----------"""
#create containers
L_orig_lst = []
S_str = ""
print("The program computes the average value of")
print("a sequence of positive integers, input as a tuple.\n")
#in orig_str store the string-type of the tuple keyed in
orig_str = input("Key in the numbers as a tuple:\n")
lnth = len(orig_str)
#copy each character from orig_str into the list L_orig_lst (the original list)
for i in range(lnth):
if orig_str[i] in ['(', '.', ',' , ')', '1', '2', '3','4', '5', '6', '7', '8', '9', '0']:
#if one of the characters left parenthesis, period, comma, right parenthesis or a digit
#is found in orig_str, then S_str is extended with that character
S_str = S_str + orig_str[i]
L_orig_lst.append(S_str)
#set S_str to be empty
S_str = ""
elif orig_str[i] == " ":
pass
else:
print("Error in input")
break
#at this stage the following transformation has taken place,
#tuple (string) --> tuple (list)
#e.g. (1,2,3) (string) --> ['(' , '1' , ',' , '2' , ',' , '3' , ')'], L_orig_lst
#create new container
#and set S_str to be empty
L_rev_lst = []
S_str = ""
lnth = len(L_orig_lst)
#from the original list, L_orig_lst, copy those elements which are digits (type string)
#and append them to the revised list, L_rev_lst.
for i in range(lnth):
if L_orig_lst[i] in ['1', '2', '3','4', '5', '6', '7', '8', '9', '0']:
#if the element in the original list is a digit (type string),
#then extend S_str with this element
S_str = S_str + L_orig_lst[i]
elif L_orig_lst[i] == ',':
#if the element in the original list is a comma, then finalize the reading of the number,
#convert the current number (type string) into an integer
S_int = int(S_str)
#and append the integer to the revised list
L_rev_lst.append(S_int)
#set S_str to be empty
S_str = ""
else:
pass
#also convert the last number (type string) to an integer,
S_int = int(S_str)
#and also append the last integer to the revised list
L_rev_lst.append(S_int)
#now convert the revised list into a tuple
T_tpl = tuple(L_rev_lst)
"""Major code block ends --------"""
#calculate the average for the tuple T_tpl
#call the class
x = Average(T_tpl)
#instantiate the class
y = x.calculate()
print("Average value: ", y)

Automated Testing Reverse String in Python 3

I am trying to create a function that can test two different functions.
I am naming my test function test_reverse, to automate testing of strReverseI and strReverseR (see the code for these below)
strReverseI and strReverseR will be arguments to test_reverse.
test_reverse will have one parameter, f, a function (either strReverseR or strReverseI)
test_reverse will call f repeatedly, for a series of test cases, and compare the result returned by either strReverseR or strReverseI to the expected result.
test_reverse should report for each test case whether the actual result matches the expected result, e.g.,
Checking strReverseR('testing123')...its value '321gnitset' is correct!
Checking strReverseI('a')... Error: has wrong value 'a', expected 'b'.
Function test_reverse should return None.
Test cases and expected results should be stored in a tuple of tuples defined in test_reverse. test_reverse should include at least the following tests:
(
('', ''),
('a', 'a'),
('aaaa', 'aaaa'),
('abc', 'cba'),
('hello', 'olleh'), ('racecar', 'racecar'),
('testing123', '321gnitset'), ('#CIS 210', '012 SIC#'), ('a', 'b')
)
Here is the code I have so far… strReverseR and strReverseI work perfectly, I am not just needing to figure out test_reverse:
import doctest
def strReverseI(s):
'''
(str) -> (str)
Returns the reverse of the string input. This function finds
the reverse string via an interative implementation.
NOTE TO MYSELF: I could also have chosen to do a while loop being
(s[n:] != ''):
This is using a while loop and as long as the slice of the string
is not an empty string,
it will continue to run the loop. As soon as the slice becomes an
empty string,
it will stop the loop and you will have the reverse string.
Examples:
>>> strReverseI('hello, world')
'dlrow ,olleh'
>>> strReverseR('')
''
>>> strReverseR('a')
'a'
'''
result = ""
n = 0
for i in range(1, len(s) + 1):
result += s[len(s) - i]
return result
def strReverseR(s):
'''
(str) -> (str)
Returns the reverse of the string input. This function finds
the reverse string via recuersion. The base case would be an empty
string so the function would still return out an empty string and
not have any errors.
After we can confirm the string is not empty,
we return the last element of the string, followed by
the string without that last element/character. Since this
continues to call upon itself, it will continue to cut the string
of the "last" element to grow the reverse string.
Examples:
>>> strReverseR('hello, world')
'dlrow ,olleh'
>>> strReverseR('')
''
>>> strReverseR('a')
'a'
'''
if s == '':
return s
result = s[-1] + strReverseR(s[0:-1])
return result
def test_reverse(f):
'''
function ->
'''
if f('hi') == 'ih':
print("success")
else:
print("failed")
return None
Note Yes, this is something I am working on for a school hw assignment. I am not expecting anyone to write it for me. I am just really confused on this last part and would love some insight on how to move forward. Any examples, and explanations would be so helpful. Thanks.
Update I have been working on this more and this is along the lines of what I think I need to do, if you could help a bit with this that would be great!
def test_reverse(f):
'''
function ->
'''
list = (
('', ''),
('a', 'a'),
('aaaa', 'aaaa'),
('abc', 'cba'),
('hello', 'olleh'), ('racecar', 'racecar'),
('testing123', '321gnitset'), ('#CIS 210', '012 SIC#'), ('a', 'b')
)
for x in list:
#f(i[0]) = [1])
if f(i[0] == [1]):
print("Checking", f,"... its value", "is correct!")
else:
print("Checking", f, "... Error: has wrong value")
return None
def main():
'''calls string reverse test func 2 times'''
test_reverse(p5.strReverseR)
print()
test_reverse(p5.strReverseI)
return None
You should consider using a tool like unittest for this - it has assert style tests which make calling methods and seeing their output easy.
For your use case, you probably want to create tests.py, something like:
import unittest
from myreversemodule import strReverseL, strReverseR
class ReverseTestCase(unittest.TestCase):
def test_strreverser(self):
self.assertEqual(strReverseR('hi'), 'ih')
if __name__ == "__main__":
unittest.main()
Now run this script - and it will give you nice test output, show errors, return True or False if it succeeds/fails. You can also run specific tests by name if you only want to test a subset, and use setUp/tearDown methods to do some config before/after running each test/each suite of tests.
Without inherited of unittest you can use builtins AssertionError:
def test_case(first_string, reversed_string):
if strReverseR(first_string) != reversed_string:
raise AssertionError("Reverse of first string: " + strReverseR(first_string) + " are not equals to second string: " + reversed_string)
Hope, it helps.
import p52_stringreverse as p5 # this is importing my strReverseR and strReverseI from a different py file, but the code for those are posted in my question anyway.
def test_reverse(f):
'''
function -> None
'''
testingcases = (('', ''), ('a', 'a'), ('aaaa', 'aaaa'),
('abc', 'cba'), ('hello', 'olleh'), ('racecar', 'racecar'),
('testing123', '321gnitset'), ('#CIS 210', '012 SIC#'), ('a', 'b'))
for case in testingcases:
if f(case[0]) == case[1]:
print("Checking", f.__name__ + "(" + "'" + case[0] + "'" + ")",
"... its value", "'" + case[1] + "'", "is correct!")
else:
print("Checking", f.__name__ + "(" + "'" + case[0] + "'" + ")",
"... Error: has wrong value", "'" + case[0] + "'" + ",", "expected",
"'" + case[1] + "'")
return None
def main():
'''calls string reverse test func 2 times'''
test_reverse(p5.strReverseR)
print()
test_reverse(p5.strReverseI)
return None
main()

Markov analysis - Return and recursion role

I am working on the solution of the Markov analysis in Think Python, but I do not understand the role of "Return" in the block code below.
As far as I known when the code reach return the function is cancel immediately, but isn't it unnecessary in this case, because there is a recursion here random_text(n-i) before the code reach the return statement, so the function will cancel only when the recursion is finish which mean when the for loop is over?? The question seem stupid but I am newbie in python and the recursion stuff is really confusing with me. I try to remove 'return' and it still run well.
def random_text(n=100):
start = random.choice(list(suffix_map.keys()))
for i in range(n):
suffixes = suffix_map.get(start, None)
if suffixes == None:
# if the start isn't in map, we got to the end of the
# original text, so we have to start again.
random_text(n-i)
return
word = random.choice(suffixes)
print(word, end=' ')
start = shift(start, word)
The full code is as below so you can understand what each function do.
from __future__ import print_function, division
import os
os.chdir(r"C:\Users\Hoang-Ngoc.Anh\Documents\WinPython-64bit 3.4.4.2\notebooks\docs")
import sys
import string
import random
# global variables
suffix_map = {} # map from prefixes to a list of suffixes
prefix = () # current tuple of words
def process_file(filename, order=2):
"""Reads a file and performs Markov analysis.
filename: string
order: integer number of words in the prefix
returns: map from prefix to list of possible suffixes.
"""
fp = open(filename)
skip_gutenberg_header(fp)
for line in fp:
for word in line.rstrip().split():
process_word(word, order)
def skip_gutenberg_header(fp):
"""Reads from fp until it finds the line that ends the header.
fp: open file object
"""
for line in fp:
if line.startswith('*END*THE SMALL PRINT!'):
break
def process_word(word, order=2):
"""Processes each word.
word: string
order: integer
During the first few iterations, all we do is store up the words;
after that we start adding entries to the dictionary.
"""
global prefix
if len(prefix) < order:
prefix += (word,)
return
try:
suffix_map[prefix].append(word)
except KeyError:
# if there is no entry for this prefix, make one
suffix_map[prefix] = [word]
prefix = shift(prefix, word)
def random_text(n=100):
"""Generates random wordsfrom the analyzed text.
Starts with a random prefix from the dictionary.
n: number of words to generate
"""
# choose a random prefix (not weighted by frequency)
start = random.choice(list(suffix_map.keys()))
for i in range(n):
suffixes = suffix_map.get(start, None)
if suffixes == None:
# if the start isn't in map, we got to the end of the
# original text, so we have to start again.
random_text(n-i)
return
# choose a random suffix
word = random.choice(suffixes)
print(word, end=' ')
start = shift(start, word)
def shift(t, word):
"""Forms a new tuple by removing the head and adding word to the tail.
t: tuple of strings
word: string
Returns: tuple of strings
"""
return t[1:] + (word,)
def main(script, filename='emma.txt', n=100, order=2):
try:
n = int(n)
order = int(order)
except ValueError:
print('Usage: %d filename [# of words] [prefix length]' % script)
else:
process_file(filename, order)
random_text(n)
print()
if __name__ == '__main__':
main(*sys.argv)

Resources