2

Just a little background: I'm making a program where a user inputs a skeleton text, two numbers (lower and upper limit), and a list of words. The outputs are a series of modifications on the skeleton text.

Sample inputs:

text = "Player # likes @." (replace # with inputted integers and @ with words in list)
lower = 1
upper = 3
list = "apples, bananas, oranges"

The user can choose to iterate over numbers first:

Player 1 likes apples.
Player 2 likes apples.
Player 3 likes apples.

Or words first:

Player 1 likes apples.
Player 1 likes bananas.
Player 1 likes oranges.

I chose to split these two methods of outputs by creating a different type of dictionary based on either number keys (integers inputted by the user) or word keys (from words in the inputted list) and then later iterating over the values in the dictionary.

Here are the two types of dictionary creation:

def numkey(dict): # {1: ['Player 1 likes apples', 'Player 1 likes...' ] }

    text, lower, upper, list = input_sort(dict)
    d = {}

    for num in range(lower,upper+1):
        l = []
        for i in list:
            l.append(text.replace('#', str(num)).replace('@', i))
        d[num] = l
    return d

def wordkey(dict): # {'apples': ['Player 1 likes apples', 'Player 2 likes apples'..] }

    text, lower, upper, list = input_sort(dict)
    d = {}

    for i in list:
        l = []
        for num in range(lower,upper+1):
            l.append(text.replace('#', str(num)).replace('@', i))
        d[i] = l
    return d

It's fine that I have two separate functions for creating different types of dictionaries but I see a lot of repetition between the two. Is there any way I could make one dictionary function and pass in different values to it that would change the order of the nested for loops to create the specific {key : value} pairs I'm looking for?

I'm not sure how this would be done. Is there anything related to functional programming or other paradigms that might help with this? The question is a little abstract and more stylistic/design-oriented than anything.

3 Answers 3

2

You don't need a dictionary to generate the output. You could use something like:

import itertools

numbers = range(lower, upper + 1)
words = "a, b, c".split(", ")

data = (numbers, words) if numbers_first else (words, numbers)
for n, w in itertools.product(*data):
    if not numbers_first: n, w = w, n
    print("Player %d likes %s." % (n, w))

To avoid the if inside the loop you could generate the format string dynamically e.g.:

template = "Player # likes @."
subs = ("{n}", "{w}") if numbers_first else ("{w}", "{n}")
format = make_format(template, subs) # escape {}, replace # and @

# ...
for n, w in product(*data):
    print(format.format(n=n, w=w))
Sign up to request clarification or add additional context in comments.

2 Comments

Noting for OP that numbers_first is an argument you would add to your method, i.e. def output_permutations(numbers_first=True):
@mVChr: the arguments are: numbers, words, template, numbers_first.
1

I think in this case, rather than forcing the two processes into one function, just make each function much shorter:

def numkey(dict):
    text, lower, upper, list = input_sort(dict)
    d = {x: [text.replace('#',str(x)).replace('@',item) 
            for item in list.split(', ')] for x in xrange(lower,upper+1)}
    return d

def wordkey(dict):
    text, lower, upper, list = input_sort(dict)
    d = {item: [text.replace('#',str(x)).replace('@',item) 
            for x in xrange(lower,upper+1)] for item in list.split(', ')}
    return d

You could in theory refactor out the dict creation using a make_dict(dict, outer, inner) or something like that, but I think that ends up being less clear for such short bits of code, especially given that you would then have to preprocess the ints in some way.

Comments

1

Here's a more generic way:

from itertools import product

def generate(skeleton, replacements):
    values = (product([k], v) for k, v in replacements.items())
    for p in product(*values):
        s = skeleton
        for a, b in p:
            s = s.replace(a, str(b))
        yield s

replacements is expected to be a dictionary {placeholder: list of values}, for example:

gen = generate("Player # likes @.", {'#': range(1,3), '@': ['apples', 'bananas']})    
for phrase in gen:
    print phrase

prints

Player 1 likes apples.
Player 2 likes apples.
Player 1 likes bananas.
Player 2 likes bananas.

If you need one of the values to be "static", just provide a single-element list:

gen = generate("Player # likes @.", {'#': [1], '@': ['apples', 'bananas']})    

Note that this works with arbitrary number of placeholders:

gen = generate("Player # likes @ and can $", {
    '#': range(1,3), 
    '@': ['apples', 'bananas'],
    '$': ['swim', 'run', 'jump']
})    

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.