Python: Create dynamic loop based on pattern - python

I'm still learning to code in Python
I want to generate a string based on pattern, the only way I know is by using for loop.
In example code below, I create a loop for "vcvcv" pattern. c=consonant, v=vowel
How to create a dynamic loop, based on pattern that I provide to the script?
eg. if pattern is "cvcvc" the loop should be build to produce the string
Help appeciated.
Thanks.
#!/bin/env python
vowel="aeiou"
consonant="bcdfghjklmnpqrstvwxyz"
lvowel=list(vowel)
lconsonant=list(consonant)
# pattern for "vcvcv" = ababa
for a in lvowel:
for b in lconsonant:
for c in lvowel:
for d in lconsonant:
for e in lvowel:
myname=a+b+c+d+e
print myname
# pattern for "cvcvc" = babab
# how to make the loop dynamic based on pattern ?

Something like this should work:
import itertools
mapping = {
'v': 'aeiou',
'c': 'bcdfghjklmnpqrstvwxyz'
}
pattern = 'vcvcv'
for thing in itertools.product(*map(mapping.get, pattern)):
print ''.join(thing)
Here's roughly how it works:
map(mapping.get, pattern) just converts 'vcv' to ['aeiou', 'bcdfghjklmnpqrstvwxyz', 'aeiou']. It replaces each letter with the corresponding list of characters.
*map(...) unpacks the argument list.
itertools.product() is like a bunch of nested for loops.
''.join(thing) joins the list of characters into a single string.
If you want to do this without itertools, you'll have to make a recursive function.

If you're just getting into programming and want to see a more general solution than the itertools one listed above, then recursion is your best bet, allowing you to arbitrarily nest loops.
There is a slight complication here, which you could use Python generators for, or else use simpler (but messier) constructs. An example of the latter is shown below.
Something like
def continuePattern(pat, strSoFar):
if pat == '':
print strSoFar
elif pat[0] == 'v':
for c in lvowel:
continuePattern(pat[1:], strSoFar + c)
elif pat[0] == 'c':
for c in lconsonant:
continuePattern(pat[1:], strSoFar + c)
This is one of several possible implementations, and one of the two most naive ones I can imagine.

A somewhat more elaborate but easily customizable version for the first n permutations is given below,
def gen_pattern( seq, op = "" ):
vowel="aeiou"
consonant="bcdfghjklmnpqrstvwxyz"
lvowel=list(vowel)
lconsonant=list(consonant)
if ( not seq ):
print op
return
if ( seq[0] == 'v' ):
for v in lvowel:
gen_pattern( seq[1:], op+v )
elif ( seq[0] == 'c' ):
for c in lconsonant:
gen_pattern( seq[1:],op+c )
if __name__ == "__main__":
gen_pattern("vcvcv")
I agree it is more work though!

Related

Not able to add "/" in output

Create a function based on the input and output. Look at the examples, there is a pattern.
My code:
def secret(a):
b=a[:-2]
a_list=list(a)
last_number=int(a_list[-1])
Final_recurr=last_number+last_number
d="{} ".format(b)*Final_recurr
j=d.split()
for i,k in enumerate(j):
if int(i)%2!=0:
m="{}".format("</" + b+ ">")*Final_recurr
return m
if int(i)%2==0:
m="{}".format("<" + b+ ">")*Final_recurr
return m
I am not able to put / in every second iteration. Please tell me the mistake in current code and short version of this.
You can use str.split or str.partition to split the string into separate parts:
>>> "div*2".partition("*")
('div', '*', '2')
>>> tag_name, _, mult = "div*2".partition("*")
>>> tag_name
'div'
>>> mult
'2'
Use string formatting to insert tag_name into a template:
>>> "<{}></{}>".format(tag_name, tag_name)
'<div></div>'
"multiply" the formatted string with int(mult):
>>> "<{}></{}>".format(tag_name, tag_name) * int(mult)
'<div></div><div></div>'
>>>
You probably should familiarize more with programming basics before trying to solve challenges.
In this case, you have to understand that a return statement inside a function just exits the function even if it is in a loop.
Thus, at the first iteration of your for i, k in enumerate(j): loop, the code passes in the second condition and exits the function when it reaches the return statement.
So there is never a second loop and your loop is just inoperant.
Just fixing this problem won't be sufficient to get a working solution. I would advice to clarify in your head the steps needed to achieve what your want and even write them in the form of comments in an empty function (you could take steps suggested by #Qwerty in comment):
def secret(expression):
# split the argument into the bits before and after *
# combine the required string
# return the resulting string
Then try to address each comment, one at a time. Other responses should help you a lot in doing this.
Here are some links to the documentation of functions and concepts that could help you:
str.split
str.format
unpacking
This is the function that can help you, try it out:
s = 'div*2'
>>> def secret(s):
tag, n = s.split('*') # parse it into two parts
ans = f'<{tag}></{tag}>' # get complementary part
return ans * int(n) # final result
>>> secret(s)
'<div></div><div></div>'
>>> secret('p*1')
'<p></p>'
>>>

Error when trying to build logical parser

So i have these strings stored in database and i want to convert them to python expression to use them with if statement. I will store these strings into list and will loop over them.
For example:
string = "#apple and #banana or #grapes"
i am able to convert this string by replacing # with "a==" and # with "b==" to this :
if a == apple and b == banana or b == grapes
hash refers to a
# refers to b
But when i use eval it throws up error "apple is not defined" because apple is not in quotes. so what i want is this:
if a == "apple" and b == "banana" or b == "grapes"
Is there any way i can do this ?
The strings stored in DB can have any type of format, can have multiple and/or conditions.
Few examples:
string[0] = "#apple and #banana or #grapes"
string[1] = "#apple or #banana and #grapes"
string[2] = "#apple and #banana and #grapes"
There will be else condition where no condition is fullfilled
Thanks
If I understand correctly you are trying so setup something of a logical parser - you want to evaluate if the expression can possibly be true, or not.
#word or #otherword
is always true since it's possible to satisfy this with #=word for example, but
#word and #otherword
is not since it is impossible to satisfy this. The way you were going is using Python's builtin interpreter, but you seem to "make up" variables a and b, which do not exist. Just to give you a starter for such a parser, here is one bad implementation:
from itertools import product
def test(string):
var_dict = {}
word_dict = {}
cur_var = ord('a')
expression = []
for i,w in enumerate(string.split()):
if not i%2:
if w[0] not in var_dict:
var_dict[w[0]] = chr(cur_var)
word_dict[var_dict[w[0]]] = []
cur_var += 1
word_dict[var_dict[w[0]]].append(w[1:])
expression.append('{}=="{}"'.format(var_dict[w[0]],w[1:]))
else: expression.append(w)
expression = ' '.join(expression)
result = {}
for combination in product(
*([(v,w) for w in word_dict[v]] for v in word_dict)):
exec(';'.join('{}="{}"'.format(v,w) for v,w in combination)+';value='+expression,globals(),result)
if result['value']: return True
return False
Beyond not checking if the string is valid, this is not great, but a place to start grasping what you're after.
What this does is create your expression in the first loop, while saving a hash mapping the first characters of words (w[0]) to variables named from a to z (if you want more you need to do better than cur_var+=1). It also maps each such variable to all the words it was assigned to in the original expression (word_dict).
The second loop runs a pretty bad algorithm - product will give all the possible paring of variable and matching word, and I iterate each combination and assign our fake variables the words in an exec command. There are plenty of reasons to avoid exec, but this is easiest for setting the variables. If I found a combination that satisfies the expression, I return True, otherwise False. You cannot use eval if you want to assign stuff (or for if,for,while etc.).
Not this can drastically be improved on by writing your own logical parser to read the string, though it will probably be longer.
#Evaluted as (#apple and #banana) or #grapes) by Python - only #=apple #=banana satisfies this.
>>> test("#apple and #banana or #grapes")
True
#Evaluted as #apple or (#banana and #grapes) by Python - all combinations satisfy this as # does not matter.
>>> test("#apple or #banana and #grapes")
True
#demands both #=banana and #=grapes - impossible.
>>> test("#apple and #banana and #grapes")
False
I am not sure of what you are asking here, but you can use the replace and split functions :
string = "#apple and #banana"
fruits = string.replace("#", "").split("and")
if a == fruits[0] and b == fruits[1]:
Hope this helps

Python elegant way to map string structure

Let's say I know beforehand that the string
"key1:key2[]:key3[]:key4" should map to "newKey1[]:newKey2[]:newKey3"
then given "key1:key2[2]:key3[3]:key4",
my method should return "newKey1[2]:newKey2[3]:newKey3"
(the order of numbers within the square brackets should stay, like in the above example)
My solution looks like this:
predefined_mapping = {"key1:key2[]:key3[]:key4": "newKey1[]:newKey2[]:newKey3"}
def transform(parent_key, parent_key_with_index):
indexes_in_parent_key = re.findall(r'\[(.*?)\]', parent_key_with_index)
target_list = predefined_mapping[parent_key].split(":")
t = []
i = 0
for elem in target_list:
try:
sub_result = re.subn(r'\[(.*?)\]', '[{}]'.format(indexes_in_parent_key[i]), elem)
if sub_result[1] > 0:
i += 1
new_elem = sub_result[0]
except IndexError as e:
new_elem = elem
t.append(new_elem)
print ":".join(t)
transform("key1:key2[]:key3[]:key4", "key1:key2[2]:key3[3]:key4")
prints newKey1[2]:newKey2[3]:newKey3 as the result.
Can someone suggest a better and elegant solution (around the usage of regex especially)?
Thanks!
You can do it a bit more elegantly by simply splitting the mapped structure on [], then interspersing the indexes from the actual data and, finally, joining everything together:
import itertools
# split the map immediately on [] so that you don't have to split each time on transform
predefined_mapping = {"key1:key2[]:key3[]:key4": "newKey1[]:newKey2[]:newKey3".split("[]")}
def transform(key, source):
mapping = predefined_mapping.get(key, None)
if not mapping: # no mapping for this key found, return unaltered
return source
indexes = re.findall(r'\[.*?\]', source) # get individual indexes
return "".join(i for e in itertools.izip_longest(mapping, indexes) for i in e if i)
print(transform("key1:key2[]:key3[]:key4", "key1:key2[2]:key3[3]:key4"))
# newKey1[2]:newKey2[3]:newKey3
NOTE: On Python 3 use itertools.zip_longest() instead.
I still think you're over-engineering this and that there is probably a much more elegant and far less error-prone approach to the whole problem. I'd advise stepping back and looking at the bigger picture instead of hammering out this particular solution just because it seems to be addressing the immediate need.

Replace multiple characters in a string using a set of rules

Rules in applyRules is going to be asked to users, but it does not work what it should work which is [“character1:substitution”, “character2:substitution”]
When user put ['b:c','c:d'], it prints c,c. However, it should print d when char is b
The function takes a single character and a set of rules as a list.
And this is what I wrote so far
def applyRules(char, rules):
for rule_list in (rule.split(':') for rule in rules):
char = char.replace(rule_list[0], rule_list[1])
return char
What should I add to make it work appropriately?
If you have multiple rules, this becomes cumbersome. As long as your replacements are done on single characters, you can make this easy with str.translate. That, however, doesn't solve the problem of chained replacements, so you'll have to make use of a while loop that runs until there are no more changes.
def applyRules(string, rules):
mapping = str.maketrans(dict(x.split(':') for x in rules))
while True:
new = string.translate(mapping)
if string == new:
break
string = new
return new
In [1308]: applyRules('bbbbb', ['b:c', 'c:d'])
Out[1308]: 'ddddd'
Single Character Replacement
For replacement involving a single character, the solution simplifies. You could use a dictionary with get:
def applyRules(char, rules):
mapping = dict(x.split(':') for x in rules)
while True:
new = mapping.get(char, char)
if char == new:
break
char = new
return new
This should be much simpler.
You need to apply each and every rule and also, preserve the new string at the same time since strings are immutable in python. You can use translate of str class. Following code works
def applyRules(char, rules):
modified = char
for rule in rules:
r = rule.split(':')
table = str.maketrans({r[0]:r[1]})
modified = modified.translate(table)
return modified
print(applyRules('bbbbb',['b:c','c:d'])) #prints 'ddddd'
print(applyRules('abdecbc',['b:c','c:d'])) #prints 'addeddd'

Is there a better way to create dynamic functions on the fly, without using string formatting and exec?

I have written a little program that parses log files of anywhere between a few thousand lines to a few hundred thousand lines. For this, I have a function in my code which parses every line, looks for keywords, and returns the keywords with the associated values.
These log files contain of little sections. Each section has some values I'm interested in and want to store as a dictionary.
I have simplified the sample below, but the idea is the same.
My original function looked like this, it gets called between 100 and 10000 times per run, so you can understand why I want to optimize it:
def parse_txt(f):
d = {}
for line in f:
if not line:
pass
elif 'apples' in line:
d['apples'] = True
elif 'bananas' in line:
d['bananas'] = True
elif line.startswith('End of section'):
return d
f = open('fruit.txt','r')
d = parse_txt(f)
print d
The problem I run into, is that I have a lot of conditionals in my program, because it checks for a lot of different things and stores the values for it. And when checking every line for anywhere between 0 and 30 keywords, this gets slow fast. I don't want to do that, because, not every time I run the program I'm interested in everything. I'm only ever interested in 5-6 keywords, but I'm parsing every line for 30 or so keywords.
In order to optimize it, I wrote the following by using exec on a string:
def make_func(args):
func_str = """
def parse_txt(f):
d = {}
for line in f:
if not line:
pass
"""
if 'apples' in args:
func_str += """
elif 'apples' in line:
d['apples'] = True
"""
if 'bananas' in args:
func_str += """
elif 'bananas' in line:
d['bananas'] = True
"""
func_str += """
elif line.startswith('End of section'):
return d"""
print func_str
exec(func_str)
return parse_txt
args = ['apples','bananas']
fun = make_func(args)
f = open('fruit.txt','r')
d = fun(f)
print d
This solution works great, because it speeds up the program by an order of magnitude and it is relatively simple. Depending on the arguments I put in, it will give me the first function, but without checking for all the stuff I don't need.
For example, if I give it args=['bananas'], it will not check for 'apples', which is exactly what I want to do.
This makes it much more efficient.
However, I do not like it this solution very much, because it is not very readable, difficult to change something and very error prone whenever I modify something. Besides that, it feels a little bit dirty.
I am looking for alternative or better ways to do this. I have tried using a set of functions to call on every line, and while this worked, it did not offer me the speed increase that my current solution gives me, because it adds a few function calls for every line. My current solution doesn't have this problem, because it only has to be called once at the start of the program. I have read about the security issues with exec and eval, but I do not really care about that, because I'm the only one using it.
EDIT:
I should add that, for the sake of clarity, I have greatly simplified my function. From the answers I understand that I didn't make this clear enough.
I do not check for keywords in a consistent way. Sometimes I need to check for 2 or 3 keywords in a single line, sometimes just for 1. I also do not treat the result in the same way. For example, sometimes I extract a single value from the line I'm on, sometimes I need to parse the next 5 lines.
I would try defining a list of keywords you want to look for ("keywords") and doing this:
for word in keywords:
if word in line:
d[word] = True
Or, using a list comprehension:
dict([(word,True) for word in keywords if word in line])
Unless I'm mistaken this shouldn't be much slower than your version.
No need to use eval here, in my opinion. You're right in that an eval based solution should raise a red flag most of the time.
Edit: as you have to perform a different action depending on the keyword, I would just define function handlers and then use a dictionary like this:
def keyword_handler_word1(line):
(...)
(...)
def keyword_handler_wordN(line):
(...)
keyword_handlers = { 'word1': keyword_handler_word1, (...), 'wordN': keyword_handler_wordN }
Then, in the actual processing code:
for word in keywords:
# keyword_handlers[word] is a function
keyword_handlers[word](line)
Use regular expressions. Something like the next:
>>> lookup = {'a': 'apple', 'b': 'banane'} # keyword: characters to look for
>>> pattern = '|'.join('(?P<%s>%s)' % (key, val) for key, val in lookup.items())
>>> re.search(pattern, 'apple aaa').groupdict()
{'a': 'apple', 'b': None}
def create_parser(fruits):
def parse_txt(f):
d = {}
for line in f:
if not line:
pass
elif line.startswith('End of section'):
return d
else:
for testfruit in fruits:
if testfruit in line:
d[testfruit] = True
This is what you want - create a test function dynamically.
Depending on what you really want to do, it is, of course, possibe to remove one level of complexity and define
def parse_txt(f, fruits):
[...]
or
def parse_txt(fruits, f):
[...]
and work with functools.partial.
You can use set structure, like this:
fruit = set(['cocos', 'apple', 'lime'])
need = set (['cocos', 'pineapple'])
need. intersection(fruit)
return to you 'cocos'.

Categories