I need to make a user created madlib where the user would input a madlib for someone else to use. The input would be something like this:
The (^noun^) and the (^adj^) (^noun^)
I need to pull anything between (^ and ^) so I can use the word to code so I get another input prompt to complete the madlib.
input('Enter "word in-between the characters":')
This is my code right now
madlib = input("Enter (^madlib^):")
a = "(^"
b = "^)"
start = madlib.find(a) len(a)
end = madlib.find(b)
substring = madlib[start:end]
def mad():
if "(^" in madlib:
substring = madlib[start:end]
m = input("Enter " substring ":")
mad = madlib.replace(madlib[start:end],m)
return mad
print(mad())
What am I missing?
CodePudding user response:
You can use re.finditer()
to do this fairly cleanly by collecting the .span()
of each match!
import re
# collect starting madlib
madlib_base = input('Enter madlib base with (^...^) around words like (^adj^)): ')
# list to put the collected blocks of spans and user inputs into
replacements = []
# yield every block like (^something^) by matching each end and `not ^` inbetween
for match in re.finditer(r"\(\^([^\^] )\^\)", madlib_base):
replacements.append({
"span": match.span(), # position of the match in madlib_base
"sub_str": input(f"enter a {match.group(1)}: "), # replacement str
})
# replacements mapping and madlib_base can be saved for later!
def iter_replace(base_str, replacements_mapping):
# yield alternating blocks of text and replacement
# skip the replacement span from the text when yielding
base_index = 0 # index in base str to begin from
for block in replacements_mapping:
head, tail = block["span"] # unpack span
yield base_str[base_index:head] # next value up to span
yield block["sub_str"] # string the user gave us
base_index = tail # start from the end of the span
# collect the iterable into a single result string
# this can be done at the same time as the earlier loop if the input is known
result = "".join(iter_replace(madlib_base, replacements))
Demonstration
...
enter a noun: Madlibs
enter a adj: rapidly
enter a noun: house
...
>>> result
'The Madlibs and the rapidly house'
>>> replacements
[{'span': (4, 12), 'sub_str': 'Madlibs'}, {'span': (21, 28), 'sub_str': 'rapidly'}, {'span': (29, 37), 'sub_str': 'house'}]
>>> madlib_base
'The (^noun^) and the (^adj^) (^noun^)'
CodePudding user response:
Your mad()
function only does one substitution, and it's only called once. For your sample input with three required substitutions, you'll only ever get the first noun
. In addition, mad()
depends on values that are initialized outside the function, so calling it multiple times won't work (it'll keep trying to operate on the same substring
, etc).
To fix it, you need to make it so that mad()
does one substitution on whatever text you give it, regardless of any other state outside of the function; then you need to call it until it's substituted all the words. You can make this easier by having mad
return a flag indicating whether it found anything to substitute.
def mad(text):
start = text.find("(^")
end = text.find("^)")
substring = text[start 2:end] if start > -1 and end > start else ""
if substring:
m = input(f"Enter {substring}: ")
return text.replace(f"(^{substring}^)", m, 1), True
return text, False
madlib, do_mad = input("Enter (^madlib^):"), True
while do_mad:
madlib, do_mad = mad(madlib)
print(madlib)
Enter (^madlib^):The (^noun^) and the (^adj^) (^noun^)
Enter noun: cat
Enter adj: lazy
Enter noun: dog
The cat and the lazy dog