Calling functions inside other functions in Python - python

I'm trying to write a simple program that creates a square grid like this:
+ - - - - + - - - - +
| | |
| | |
| | |
| | |
+ - - - - + - - - - +
| | |
| | |
| | |
| | |
+ - - - - + - - - - +
I want it to take an input x, which defines how many cells are in each row/column. The script is as follows:
def repeat(x, f, *args):
for i in range(x): f(*args)
def topx(x):
print x*top + '+'
def midx(x):
print x*mid + '|'
def block(f,g,*args):
f(*args)
g(*args)
top = str('+ - - - - ')
mid = str('| ')
x = 2
repeat(x,block,topx,repeat,x,4,midx,x)
topx()
I get the following error when I try to run the script:
TypeError: topx() takes exactly 1 argument (4 given)
It's something to do with the arguments in the block function, but can't figure out how to get around it.
Thanks in advance.
Edit:
Thanks for the pointers, I rewrote it slightly as follows and it works nicely. I also changed it to allow you to choose the number of columns and rows independently.
def repeat(x, f, *args):
for i in range(x): f(*args)
def topx(x):
print x*top + '+'
def midx(x):
print x*mid + '|'
def row(x):
topx(x)
repeat(4,midx,x)
top = str('+ - - - - ')
mid = str('| ')
x = 3
y = 4
repeat(y,row,x)
topx(x)

def topx(x):
print x*top + '+'
This takes one argument: x. But when you call it:
def block(f,g,*args):
f(*args) # This is what calls topx
g(*args)
You're passing it *args, which contains [x, 4, midx, x]. And that's four arguments right there.
You should probably reconsider your structure to fix this. Perhaps a class?

def grid(x, space=4):
for i in range(x):
print ("+" + "-"*space)*x + "+"
for j in range(space):
print ("|" + " "*space)*x + "|"
print ("+" + "-"*space)*x + "+"

I think this is not best way. but your script can be easily updated to be more readable and working.
cell_number = 2
top = str('+ - - - - ')
mid = str('| ')
def repeat(cell_number, f, *args):
"""
repeat call of function `f` `call_number` times
"""
for i in range(cell_number):
f(*args)
def topx(cell_number):
"""
draw horizontal edge
"""
print cell_number * top + '+'
def midx(cell_number):
"""
draw middle part with vertical edges
"""
print cell_number * mid + '|'
def block(cell_num):
"""
draw one row of `cell_num` cells
"""
topx(cell_num)
repeat((len(top) - 1)/2, midx, cell_num)
repeat(cell_number, block, cell_number)
topx(cell_number)

def box(width, height, cell=1):
print '+' + ('- ' * width + '+') * cell
for x in range(cell):
for x in range(height):
print '|' + (' ' * width + '|') * cell
print '+' + ('- ' * width + '+') * cell

Related

Outputting tictactoe board

I'm trying to print my tictactoe board so it looks like
| |
-+-+-
| |
-+-+-
| |
This is my code:
def __str__(self):
result = ""
for i in range(self.nrows):
for j in range(self.ncols):
cell = str(self.gameboard[i][j])
result += cell + "|"
result += "\n"
result += "-+"
result += '\n'
return result
The result looks like
| | |
-+
| | |
-+
| | |
-+
How should I change the code so it looks like the first board? self.nrows is the number of rows, self.ncols is the number of columns, and self.gameboard is the 2d array.
That's not the most pythonic way, and you probably should diverge the code into some functions, but it's working:
def __str__(self):
result = ""
for i in range(self.nrows):
for j in range(self.ncols-1):
cell = str(self.gameboard[i][j])
result += cell + "|"
result += "\n"
if i == self.nrows - 1:
break # added the last line, which is just |
for j in range(self.ncols-1):
result += "-+"
result += '-'
result += '\n'
return result
Output for self.nrows = self.ncols = 3 and self.gameboard[i][j] = ' ' for each i,j:
| |
-+-+-
| |
-+-+-
| |
You need to change the row result += "-+" to result += "-+" * self.ncols + "-". Reason is that you print "-+" symbol only once, but need to do that as many time as your number of columns. Also, in the end you need to add one more "-" symbol to make it look symmetric. In the end just remove the last row that have 2*ncols+2 symbols.
The complete code would be:
def __str__(self):
result = ""
for i in range(self.nrows):
for j in range(self.ncols):
cell = str(self.gameboard[i][j])
result += cell + "|"
result += "\n"
result += "-+" * self.ncols + "-"
result += '\n'
# Remove the last row
result = result[:-2*ncols-2]
result += "\n"
return result

How do fix this function to make it not show "None" at the bottom?

So right now this function returns the results I want but there is an annoying "None" at the very bottom. I understand this is because there is nothing to return that's why "None" is printed, but how can I change up my code to show the results without the "None"?
Thanks. Also, the "print(steps(6))" line is mandatory as it is part of an exercise.
def steps(i):
t = 0
for i in range(1, i + 1):
print(("\t" * t) + (str(i) * 3) + "\n")
t += 1
print(steps(6))
And the result is
111
222
333
444
555
666
None #I want to get rid of this.
You Should remove print
def steps(i):
t = 0
for i in range(1, i + 1):
print(("\t" * t) + (str(i) * 3) + "\n")
t += 1
steps(6)
Functions return None by default; by calling print(function), you are printing the returned value, in your case, it is None.
Because the printing happens in your function, you could call steps without printing:
Edit:
if the print is mandatory... Maybe you could return the empty string then:
def steps(i):
t = 0
for i in range(1, i + 1):
print(("\t" * t) + (str(i) * 3) + "\n")
t += 1
return ''
print(steps(6))
output:
111
222
333
444
555
666
''
Update your code like this:
def steps(i):
t = 0
for i in range(1, i + 1):
print(("\t" * t) + (str(i) * 3) + "\n")
t += 1
return "" #updated
print(steps(6)) #Now your print function will get the value "" (empty string), and will print it
When your function does not explicitly return any value then it returns the default return value None. Your print function is getting this None and printing it.
Output:
111
222
333
444
555
666
Consider this code
print str(10)
Simple right! the result is "10". So what is it going inside this snippet?
str(10) takes an object as input and return a string as output. So print operator takes a string as input and prints it in the STD output file.
So what about None return type function. (i.e Void return function), and that's what exactly happened in your code.
The correct code is
def steps(i):
t = 0
for i in range(1, i + 1):
print(("\t" * t) + (str(i) * 3) + "\n")
t += 1
return ''
print(steps(6))
or
def steps(i):
t = 0
for i in range(1, i + 1):
print(("\t" * t) + (str(i) * 3) + "\n")
t += 1
steps(6)
def steps(i):
t = 0
for i in range(1, i + 1):
print(("\t" * t) + (str(i) * 3) + "\n")
t += 1
return ""
steps(6)
Please remove print from steps(6) then you will get rid off None.

Writing a function that prints a pyramid - two parameters

I'm working on this problem, that reads as follows:
Problem: Write a function that draws a pyramid on your screen. The function needs two arguments. The first is the height of the pyramid. The second argument is optional: if not supplied, the symbol "#" should be used to draw the pyramid. Otherwise, if the users enters " * " for example, the pyramid should consist of asterisks.
Attempt: I wrote this program:
def main():
h = int(input("Please enter the height of the pyramid: "))
symbol = str(input("Enter the desired symbol or press enter for standard (#): "))
def pyramid(h,symbol):
if symbol == "" or symbol == "#":
for i in range(h):
pyr = print(" " * (h-i - 1) + "#" * (2 * i + 1))
return pyr
else:
for i in range(h):
pyr = print(" " * (h-i - 1) + symbol * (2 * i + 1))
return pyr
print()
main()
But this is not working properly when I try to call this. Can someone point out my mistakes? Also, I'm not sure how to deal with the 'optional' property of the function. Should I stick to my approach or is there a better way to define that ?
your logic is fine, you can try
def pyramid(h,symbol):
if symbol == "" or symbol == "#":
for i in range(h):
print(" " * (h-i - 1) + "#" * (2 * i + 1))
else:
for i in range(h):
print(" " * (h-i - 1) + symbol * (2 * i + 1))
print()
pyramid(5, "#")
#
###
#####
#######
#########
your problems:
pyr = print(" " * (h-i - 1) + "#" * (2 * i + 1)) .... print function return None, then pyr store None
return pyr statement return pyr variable content and finish pyramid function
pyramid function isn't calling never
Improving code
you can remove unnecessary if,
def pyramid(h,symbol="#"):
for i in range(h):
print(" " * (h-i - 1) + symbol * (2 * i + 1))
print()
pyramid(5)
you can return a str
def pyramid(h,symbol="#"):
out = ""
for i in range(h):
out += (" " * (h-i - 1)) + (symbol * (2 * i + 1)) + "\n"
return out
print(pyramid(5))
or, online solution
def pyramid(h,symbol="#"):
return "\n".join((" " * (h-i - 1)) + (symbol * (2 * i + 1)) for i in range(h))
print(pyramid(5))

Card Matching Game on Python

I am current building a simple card matching game in python, with a 5x4 (row*column) grid, in which two players try to match a deck of twenty cards (2,10 of only suit Hearts) * 2.
The problem I am running into is in iterating through the deck, printing the cards out in a grid fashion so it would look like this:
----- ----- ----- -----
- - - - - - - -
4-H 6-H 7-H 8-H
- - - - - - - -
----- ----- ----- -----
The code I currently have is below:
#needed import for shuffle function
from random import shuffle
#class for my deck
class Deck:
#constructor starts off with no cards
def __init__( self ):
self._deck = []
#populate the deck with every combination of suits and values
def Populate( self ):
#Heart, Diamond, Spades, Clubs
for suit in 'HDSC':
#Jack = 11, Queen = 12, King = 13, Ace = 14
for value in range(2, 15):
if value == 11:
value = 'J'
elif value == 12:
value = 'Q'
elif value == 13:
value = 'K'
elif value == 14:
value = 'A'
#add to deck list
self._deck.append(str(value) + '-' + suit)
#populate the deck with only hears hearts and all cards except face cards and aces (2, 3, 4, 5, 6, 7, 8, 9, 10) twice
def gamePop( self ):
suit = 'H'
for x in range(2):
for value in range(2, 11):
self._deck.append(str(value) + '-' + suit)
#shuffle the deck with the random import
def Shuffle( self ):
shuffle( self._deck )
#length of the deck
def len( self ):
return len( self._deck )
def stringIt( self ):
#Returns the string representation of a deck
result = ''
for c in self._deck:
result = result + str(c) + '\n'
return result
#class for a single card
class Card:
#constructor for what type of card it is
def __init__( self, value, suit ):
self._value = value
self._suit = suit
self._card = self._value + self._suit
#print the type of card
def Description( self ):
return ( self._card )
#overloaded ==
def __eq__( self, another ):
if ( self._card == another.Description() ):
return True
else:
return False
#main function which plays the game
def main():
#sets player counters to zero,
pOneCount = 0
pTwoCount = 0
#creates the deck to be put on the board
gameDeck = Deck()
gameDeck.gamePop()
gameDeck.Shuffle()
print(gameDeck._deck)
currentCard = 0
for row in range(5):
for card in range(0,4+i):
mystring =
print ('------- ' * 4)
print ('| | ' * 4)
for x in range(4):
print ('| ' +gameDeck._deck[currentCard]+'|'),
currentCard += 1
print ('| | ' * 4)
print ('------- ' * 4)
Edit: I cleared up the code which I've tried.
The current output is this:
------- ------- ------- -------
| | | | | | | |
| 7-H|
| 5-H|
| 7-H|
| 9-H|
| | | | | | | |
------- ------- ------- -------
the problem is in the def main():
def main():
print ('------- ' * 4)
print ('| | ' * 4)
for x in range(4):
print ('| ' +gameDeck._deck[currentCard]+'|'),
currentCard += 1
print ('| | ' * 4)
print ('------- ' * 4)
the * 4 just mean that this:
print ('------- ' * 4)
will become this:
print ('------- ' + '------- ' + '------- ' + '------- ' )
it can also be type as:
print ('------- ------- ------- ------- ' )
so. your problem is here:
for x in range(4):
print ('| ' +gameDeck._deck[currentCard]+'|'),
currentCard += 1
this would print as:
| 7-H|
| 5-H|
| 7-H|
| 9-H|
you need to put it as something like this:
print ('| ' +gameDeck._deck[currentCard]+'|'+'| ' +gameDeck._deck[currentCard+1]+'|'+'| ' +gameDeck._deck[currentCard+2]+'|'+'| ' +gameDeck._deck[currentCard+3]+'|')
so it would print in one line like how you want it:
| 7-H| | 5-H| | 7-H| | 9-H|
here is the code that i clean up a little. if it work like it should, it should work:
def main():
#sets player counters to zero,
pOneCount = 0
pTwoCount = 0
#creates the deck to be put on the board
gameDeck = Deck()
gameDeck.gamePop()
gameDeck.Shuffle()
print(gameDeck._deck)
currentCard = 0
for row in range(5):
for card in range(0,4+i):
print (' ------- ' * 4)
print (' | | ' * 4)
print (' | ' +gameDeck._deck[currentCard]+' | '+' | ' +gameDeck._deck[currentCard+1]+' | '+' | ' +gameDeck._deck[currentCard+2]+' | '+' | ' +gameDeck._deck[currentCard+3]+' | ')
print (' | | ' * 4)
print (' ------- ' * 4)
oh, and like John Y say (copy and paste):
The main function has a dangling mystring =, which is a blatant syntax error
here what i use to test, because the whole code don't work for me, i just tested the print part:
print (' ------- ' * 4)
print (' | | ' * 4)
print (' | ' +"1-H"+' | '+' | ' +"2-H"+' | '+' | ' +"3-H"+' | '+' | ' +"4-H"+' | ')
print (' | | ' * 4)
print (' ------- ' * 4)
that got me:
------- ------- ------- -------
| | | | | | | |
| 1-H | | 2-H | | 3-H | | 4-H |
| | | | | | | |
------- ------- ------- -------
>>>

Is there a way to autogenerate valid arithmetic expressions?

I'm currently trying to create a Python script that will autogenerate space-delimited arithmetic expressions which are valid. However, I get sample output that looks like this: ( 32 - 42 / 95 + 24 ( ) ( 53 ) + ) 21
While the empty parentheses are perfectly OK by me, I can't use this autogenerated expression in calculations since there's no operator between the 24 and the 53, and the + before the 21 at the end has no second argument.
What I want to know is, is there a way to account for/fix these errors using a Pythonic solution? (And before anyone points it out, I'll be the first to acknowledge that the code I posted below is probably the worst code I've pushed and conforms to...well, very few of Python's core tenets.)
import random
parentheses = ['(',')']
ops = ['+','-','*','/'] + parentheses
lines = 0
while lines < 1000:
fname = open('test.txt','a')
expr = []
numExpr = lines
if (numExpr % 2 == 0):
numExpr += 1
isDiv = False # Boolean var, makes sure there's no Div by 0
# isNumber, isParentheses, isOp determine whether next element is a number, parentheses, or operator, respectively
isNumber = random.randint(0,1) == 0 # determines whether to start sequence with number or parentheses
isParentheses = not isNumber
isOp = False
# Counts parentheses to ensure parentheses are matching
numParentheses = 0
while (numExpr > 0 or numParentheses > 0):
if (numExpr < 0 and numParentheses > 0):
isDiv = False
expr.append(')')
numParentheses -= 1
elif (isOp and numParentheses > 0):
rand = random.randint(0,5)
expr.append(ops[rand])
isDiv = (rand == 3) # True if div op was just appended
# Checks to see if ')' was appended
if (rand == 5):
isNumber = False
isOp = True
numParentheses -= 1
# Checks to see if '(' was appended
elif (rand == 4):
isNumber = True
isOp = False
numParentheses += 1
# All other operations go here
else:
isNumber = True
isOp = False
# Didn't add parentheses possibility here in case expression in parentheses somehow reaches 0
elif (isNumber and isDiv):
expr.append(str(random.randint(1,100)))
isDiv = False
isNumber = False
isOp = True
# If a number's up, decides whether to append parentheses or a number
elif (isNumber):
rand = random.randint(0,1)
if (rand == 0):
expr.append(str(random.randint(0,100)))
isNumber = False
isOp = True
elif (rand == 1):
if (numParentheses == 0):
expr.append('(')
numParentheses += 1
else:
rand = random.randint(0,1)
expr.append(parentheses[rand])
if rand == 0:
numParentheses += 1
else:
numParentheses -= 1
isDiv = False
numExpr -= 1
fname.write(' '.join(expr) + '\n')
fname.close()
lines += 1
Yes, you can generate random arithmetic expressions in a Pythonic way. You need to change your approach, though. Don't try to generate a string and count parens. Instead generate a random expression tree, then output that.
By an expression tree, I mean an instance of a class called, say, Expression with subclasses Number, PlusExpression,MinusExpression, 'TimesExpression, DivideExpression, and ParenthesizedExpression. Each of these, except Number will have fields of type Expression. Give each a suitable __str__ method. Generate some random expression objects and just print the "root."
Can you take it from here or would you like me to code it up?
ADDENDUM: Some sample starter code. Doesn't generate random expressions (yet?) but this can be added....
# This is just the very beginning of a script that can be used to process
# arithmetic expressions. At the moment it just defines a few classes
# and prints a couple example expressions.
# Possible additions include methods to evaluate expressions and generate
# some random expressions.
class Expression:
pass
class Number(Expression):
def __init__(self, num):
self.num = num
def __str__(self):
return str(self.num)
class BinaryExpression(Expression):
def __init__(self, left, op, right):
self.left = left
self.op = op
self.right = right
def __str__(self):
return str(self.left) + " " + self.op + " " + str(self.right)
class ParenthesizedExpression(Expression):
def __init__(self, exp):
self.exp = exp
def __str__(self):
return "(" + str(self.exp) + ")"
e1 = Number(5)
print e1
e2 = BinaryExpression(Number(8), "+", ParenthesizedExpression(BinaryExpression(Number(7), "*", e1)))
print e2
** ADDENDUM 2 **
Getting back into Python is really fun. I couldn't resist implementing the random expression generator. It is built on the code above. SORRY ABOUT THE HARDCODING!!
from random import random, randint, choice
def randomExpression(prob):
p = random()
if p > prob:
return Number(randint(1, 100))
elif randint(0, 1) == 0:
return ParenthesizedExpression(randomExpression(prob / 1.2))
else:
left = randomExpression(prob / 1.2)
op = choice(["+", "-", "*", "/"])
right = randomExpression(prob / 1.2)
return BinaryExpression(left, op, right)
for i in range(10):
print(randomExpression(1))
Here is the output I got:
(23)
86 + 84 + 87 / (96 - 46) / 59
((((49)))) + ((46))
76 + 18 + 4 - (98) - 7 / 15
(((73)))
(55) - (54) * 55 + 92 - 13 - ((36))
(78) - (7 / 56 * 33)
(81) - 18 * (((8)) * 59 - 14)
(((89)))
(59)
Ain't tooooo pretty. I think it puts out too many parents. Maybe change the probability of choosing between parenthesized expressions and binary expressions might work well....
Actually, as long as Ray Toal's response is formally correct, for such a simple problem you don't have to subclass each operator*. I came up with the following code which works pretty well:
import random
import math
class Expression(object):
OPS = ['+', '-', '*', '/']
GROUP_PROB = 0.3
MIN_NUM, MAX_NUM = 0, 20
def __init__(self, maxNumbers, _maxdepth=None, _depth=0):
"""
maxNumbers has to be a power of 2
"""
if _maxdepth is None:
_maxdepth = math.log(maxNumbers, 2) - 1
if _depth < _maxdepth and random.randint(0, _maxdepth) > _depth:
self.left = Expression(maxNumbers, _maxdepth, _depth + 1)
else:
self.left = random.randint(Expression.MIN_NUM, Expression.MAX_NUM)
if _depth < _maxdepth and random.randint(0, _maxdepth) > _depth:
self.right = Expression(maxNumbers, _maxdepth, _depth + 1)
else:
self.right = random.randint(Expression.MIN_NUM, Expression.MAX_NUM)
self.grouped = random.random() < Expression.GROUP_PROB
self.operator = random.choice(Expression.OPS)
def __str__(self):
s = '{0!s} {1} {2!s}'.format(self.left, self.operator, self.right)
if self.grouped:
return '({0})'.format(s)
else:
return s
for i in range(10):
print Expression(4)
It can although be improved to take into considerations things like divisions by zero (not handled currently), customization of all parameters through attributes, allowing any value for the maxNumbers argument and so on.
* By "simple problem" I mean "generating valid expressions"; if you are adding any other functionality (for example, expression evaluation), then Ray's approach will pay of because you can define the behavior of each subclass in a much cleaner way.
Edit (output):
(5 * 12 / 16)
6 * 3 + 14 + 0
13 + 15 - 1
19 + (8 / 8)
(12 + 3 - 5)
(4 * 0 / 4)
1 - 18 / (3 * 15)
(3 * 16 + 3 * 1)
(6 + 16) / 16
(8 * 10)
I found this thread on a similar quest, namely to generate random expressions for unit testing of symbolic calculations. In my version, I included unary functions and allowed the symbols to be arbitrary strings, i.e. you can use numbers or variable names.
from random import random, choice
UNARIES = ["sqrt(%s)", "exp(%s)", "log(%s)", "sin(%s)", "cos(%s)", "tan(%s)",
"sinh(%s)", "cosh(%s)", "tanh(%s)", "asin(%s)", "acos(%s)",
"atan(%s)", "-%s"]
BINARIES = ["%s + %s", "%s - %s", "%s * %s", "%s / %s", "%s ** %s"]
PROP_PARANTHESIS = 0.3
PROP_BINARY = 0.7
def generate_expressions(scope, num_exp, num_ops):
scope = list(scope) # make a copy first, append as we go
for _ in xrange(num_ops):
if random() < PROP_BINARY: # decide unary or binary operator
ex = choice(BINARIES) % (choice(scope), choice(scope))
if random() < PROP_PARANTHESIS:
ex = "(%s)" % ex
scope.append(ex)
else:
scope.append(choice(UNARIES) % choice(scope))
return scope[-num_exp:] # return most recent expressions
As copied from pervious answers, I just throw in some paranthesis around binary operators with probability PROP_PARANTHESIS (that is a bit of cheating). Binary operators are more common than unary ones, so I left it also for configuration (PROP_BINARY). An example code is:
scope = [c for c in "abcde"]
for expression in generate_expressions(scope, 10, 50):
print expression
This will generate something like:
e / acos(tan(a)) / a * acos(tan(a)) ** (acos(tan(a)) / a + a) + (d ** b + a)
(a + (a ** sqrt(e)))
acos((b / acos(tan(a)) / a + d) / (a ** sqrt(e)) * (a ** sinh(b) / b))
sin(atan(acos(tan(a)) ** (acos(tan(a)) / a + a) + (d ** b + a)))
sin((b / acos(tan(a)) / a + d)) / (a ** sinh(b) / b)
exp(acos(tan(a)) / a + acos(e))
tan((b / acos(tan(a)) / a + d))
acos(tan(a)) / a * acos(tan(a)) ** (acos(tan(a)) / a + a) + (d ** b + a) + cos(sqrt(e))
(acos(tan(a)) / a + acos(e) * a + e)
((b / acos(tan(a)) / a + d) - cos(sqrt(e))) + sinh(b)
Putting PROP_BINARY = 1.0 and applying with
scope = range(100)
brings us back to output like
43 * (50 * 83)
34 / (29 / 24)
66 / 47 - 52
((88 ** 38) ** 40)
34 / (29 / 24) - 27
(16 + 36 ** 29)
55 ** 95
70 + 28
6 * 32
(52 * 2 ** 37)
Ok, I couldn't resist adding my own implementation using some of the ideas we discussed in Ray's answer. I approached a few things differently than Ray did.
I added some handling of the probability of the incidence of each operator. The operators are biased so that the lower priority operators (larger precedence values) are more common than the higher order ones.
I also implemented parentheses only when precedence requires. Since the integers have the highest priority (lowest precedence value) they never get wrapped in parentheses. There is no need for a parenthesized expression as a node in the expression tree.
The probability of using an operator is biased towards the initial levels (using a quadratic function) to get a nicer distribution of operators. Choosing a different exponent gives more potential control of the quality of the output, but I didn't play with the possibilities much.
I further implemented an evaluator for fun and also to filter out indeterminate expressions.
import sys
import random
# dictionary of operator precedence and incidence probability, with an
# evaluator added just for fun.
operators = {
'^': {'prec': 10, 'prob': .1, 'eval': lambda a, b: pow(a, b)},
'*': {'prec': 20, 'prob': .2, 'eval': lambda a, b: a*b},
'/': {'prec': 20, 'prob': .2, 'eval': lambda a, b: a/b},
'+': {'prec': 30, 'prob': .25, 'eval': lambda a, b: a+b},
'-': {'prec': 30, 'prob': .25, 'eval': lambda a, b: a-b}}
max_levels = 3
integer_range = (-100, 100)
random.seed()
# A node in an expression tree
class expression(object):
def __init__(self):
super(expression, self).__init__()
def precedence(self):
return -1
def eval(self):
return 0
#classmethod
def create_random(cls, level):
if level == 0:
is_op = True
elif level == max_levels:
is_op = False
else:
is_op = random.random() <= 1.0 - pow(level/max_levels, 2.0)
if is_op:
return binary_expression.create_random(level)
else:
return integer_expression.create_random(level)
class integer_expression(expression):
def __init__(self, value):
super(integer_expression, self).__init__()
self.value = value
def __str__(self):
return self.value.__str__()
def precedence(self):
return 0
def eval(self):
return self.value
#classmethod
def create_random(cls, level):
return integer_expression(random.randint(integer_range[0],
integer_range[1]))
class binary_expression(expression):
def __init__(self, symbol, left_expression, right_expression):
super(binary_expression, self).__init__()
self.symbol = symbol
self.left = left_expression
self.right = right_expression
def eval(self):
f = operators[self.symbol]['eval']
return f(self.left.eval(), self.right.eval())
#classmethod
def create_random(cls, level):
symbol = None
# Choose an operator based on its probability distribution
r = random.random()
cumulative = 0.0
for k, v in operators.items():
cumulative += v['prob']
if r <= cumulative:
symbol = k
break
assert symbol != None
left = expression.create_random(level + 1)
right = expression.create_random(level + 1)
return binary_expression(symbol, left, right)
def precedence(self):
return operators[self.symbol]['prec']
def __str__(self):
left_str = self.left.__str__()
right_str = self.right.__str__()
op_str = self.symbol
# Use precedence to determine if we need to put the sub expressions in
# parentheses
if self.left.precedence() > self.precedence():
left_str = '('+left_str+')'
if self.right.precedence() > self.precedence():
right_str = '('+right_str+')'
# Nice to have space around low precedence operators
if operators[self.symbol]['prec'] >= 30:
op_str = ' ' + op_str + ' '
return left_str + op_str + right_str
max_result = pow(10, 10)
for i in range(10):
expr = expression.create_random(0)
try:
value = float(expr.eval())
except:
value = 'indeterminate'
print expr, '=', value
I got these results:
(4 + 100)*41/46 - 31 - 18 - 2^-83 = -13.0
(43 - -77)/37^-94 + (-66*67)^(-24*49) = 3.09131533541e+149
-32 - -1 + 74 + 74 - 15 + 64 - -22/98 = 37.0
(-91*-4*45*-55)^(-9^2/(82 - -53)) = 1.0
-72*-85*(75 - 65) + -100*19/48*22 = 61198.0
-57 - -76 - -54*76 + -38 - -23 + -17 - 3 = 4088.0
(84*-19)^(13 - 87) - -10*-84*(-28 + -49) = 64680.0
-69 - -8 - -81^-51 + (53 + 80)^(99 - 48) = 2.07220963807e+108
(-42*-45)^(12/87) - -98 + -23 + -67 - -37 = 152.0
-31/-2*-58^-60 - 33 - -49 - 46/12 = -79.0
There are a couple of things the program does, that although are valid, a human wouldn't do. For example:
It can create long strings of sequential divides (e.g. 1/2/3/4/5).
+/- of a negative number is common (e.g. 1 - -2)
These can be corrected with a clean-up pass.
Also, there is no guarantee that the answer is determinate. Divides by 0 and 0^0 are possible, although with the exception handling these can be filtered out.
import random
def expr(depth):
if depth==1 or random.random()<1.0/(2**depth-1):
return str(int(random.random() * 100))
return '(' + expr(depth-1) + random.choice(['+','-','*','/']) + expr(depth-1) + ')'
for i in range(10):
print expr(4)
Generate an array at random in RPN with mixtures of operators and numbers (always valid). Then start from middle of the array and generate the corresponding evaluation tree.

Categories

Resources