A simple way with re.subn, which can also accept a function instead of a replacement string:
import re
from random import randint
def select(m):
choices = m.group(1).split('|')
return choices[randint(0, len(choices)-1)]
def spinner(s):
r = re.compile('{([^{}]*)}')
while True:
s, n = r.subn(select, s)
if n == 0: break
return s.strip()
It simply replaces all the deepest choices it meets, then iterates until no choice remains. subn
returns a tuple with the result and how many replacements were made, which is convenient to detect the end of the processing.
My version of select()
can be replaced by Bobince's that uses random.choice()
and is more elegant if you just want to stick to a random selector. If you want to build a choice tree, you could extend the above function, but you will need global variables to keep track of where you are, so moving the functions into a class would make sense. This is just a hint, I won't develop that idea since it was not really the orginial question.
Note finally that you should use r.subn(select, s, re.U)
if you need unicode strings (s = u"{...}"
)
Example:
>>> s = "{{Hello|Hi|Hey} {world|earth} | {Goodbye|farewell} {noobs|n3wbz|n00blets}}"
>>> print spinner(s)
'farewell n3wbz'
Edit: Replaced sub
by subn
to avoid infinite loop (thanks to Bobince to point it out) and make it more efficient, and replaced {([^{}]+)}
by {([^{}]*)}
to extract empty curly brackets as well. That should make it more robust to ill-formatted patterns.
For people who like to put as much as possible on one line (which I personally wouldn't encourage):
def spin(s):
while True:
s, n = re.subn('{([^{}]*)}',
lambda m: random.choice(m.group(1).split("|")),
s)
if n == 0: break
return s.strip()