Evan Emolo
Evan Emolo

Reputation: 1670

Python: `choice()` selecting the same choice

I am attempting to write a simple math expression generator. The problem I am having is achieving an expression with random numbers selected from within a range, and inserting a random operator between each number.

Here's what I have so far:

from random import randint
from random import choice

lower = int(raw_input("Enter a lower integer constraint: "))
higher = int(raw_input("Enter a higher integer constraint: "))

def gen_randoms(lower, higher):
    integers = list()
    for x in xrange(4):
        rand_int = randint(lower, higher)
        integers.append(rand_int)
    return integers

def gen_equations(integers):
    nums = map(str, integers)
    print nums
    operators = ['*', '+', '-']
    equation = 'num op num op num op num'
    equation = equation.replace('op', choice(operators))
    equation = equation.replace('num', choice(nums))
    print equation

nums = gen_randoms(lower, higher)
gen_equations(nums)

The problem here is the output will repeat the operator choice and random integer selection, so it gives 5 + 5 + 5 + 5 or 1 - 1 - 1 - 1 instead of something like 1 + 2 - 6 * 2. How do I instruct choice to generate different selections?

Upvotes: 0

Views: 292

Answers (3)

Jon Clements
Jon Clements

Reputation: 142136

I'd go for using a replacements dict and using that to replace each "word":

import random

replacements = {
    'op': ['*', '+', '-'],
    'num': map(str, range(1, 6))
}

equation = 'num op num op num op num op num'
res = ' '.join(random.choice(replacements[word]) for word in equation.split())
# 1 + 3 * 5 * 2 + 2

You could then generalise this so that each word performs a different action, so to pick a random operator, but keep the numbers in sequence...:

replacements = {
    'op': lambda: random.choice(['*', '+', '-']),
    'num': lambda n=iter(map(str, range(1, 6))): next(n)
}

equation = 'num op num op num op num op num'
res = ' '.join(replacements[word]() for word in equation.split())
# 1 + 2 + 3 - 4 * 5

Note, this will throw an error if there are more num's present in the string, then there are in the replacements...

Upvotes: 4

Martijn Pieters
Martijn Pieters

Reputation: 1121744

str.replace() replaces all occurrences of the first operand with the second operand. It does not treat the second argument as an expression, however.

Replace one occurrence at a time; the str.replace() method takes a third argument that limits how many replacements are made:

while 'op' in equation:
    equation = equation.replace('op', choice(operators), 1)
while 'num' in equation:
    equation = equation.replace('num', choice(nums), 1)

Now the choice() is called for each iteration through the loop.

Demo:

>>> from random import choice
>>> operators = ['*', '+', '-']
>>> nums = map(str, range(1, 6))
>>> equation = 'num op num op num op num op num'
>>> while 'op' in equation:
...     equation = equation.replace('op', choice(operators), 1)
... 
>>> while 'num' in equation:
...     equation = equation.replace('num', choice(nums), 1)
... 
>>> equation
'5 - 1 * 2 * 4 - 1'

Upvotes: 5

Marcin
Marcin

Reputation: 49826

This line calls choice only once:

equation = equation.replace('num', choice(nums))

It replaces every instance of 'num' with the one value you pass as the second argument.

This is as expected.

The correct way to replace values in a string is to use format or the % operator. See: http://docs.python.org/2/library/string.html

Alternatively, you could iteratively build up the string.

Upvotes: 0

Related Questions