Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

correct me for using generators or tell me other way

I have A menu dict item as key and price as value. There may exist a combination of item that will be bit cheaper than single item. For exa:

menu = {
    ('burger',) : 5.00,
    ('pizza',) : 12.00,
    ('coke',) : 4.00,
    ('macpuff',) : 4.00,
    ('pasta',) : 3.00,
    ('french_fries',) : 2.00,
    ('burger', 'coke', 'french_fries') : 10.00,
    ('pizza', 'coke') : 15.00,
}

Now suppose i ordered few items then output will be min-amount of given order:

I/P >  burger, coke 
O/P >  9      (5.00 + 4.00) 

I/P >  burger, coke, french_fries
O/P >  10.00 

I/P >  pizza, coke, french_fries
O/P >  17.00    (15.00 + 2.00)

here is the code i tried so for all prices which i will use as generators:

def isSubset(a, b):
    """
        compare two iterable and return true if first is subset of second
    """
    b = list(b)
    if not hasattr(a, '__iter__'):
        a = [a]
    for each in a: 
        try:
            b.remove(each)
        except ValueError:
            return False
    return True

def rest_min_price(order):
    if order:
        for item, price in menu.iteritems():
            if isSubset(order[0], item):
                new_order = order[1:]
                for itm in item:
                    try:
                        new_order.remove(itm)
                    except ValueError:
                        pass
                yield price + rest_min_price(new_order)

but when i run this it's saying type error:

for each in rest_min_price(order_item):
    print each

TypeError: unsupported operand type(s) for +: 'int' and 'generator'
like image 627
Roshan Avatar asked Jan 02 '14 10:01

Roshan


2 Answers

Thank you all for your response. somewhere i used your suggestion and now i had solved it by same approach with different implementation.

here is my code :

class Rest_Menu():
    def __init__(self, menu):
        self.menu = menu
        self.all_price = [] 

    def min_price(self, order, total_price=0):
        """
        Return minm menu price by calculating all possible combination.
        """
        if order:
            for item, price in self.menu.iteritems():
                if isSubset(order[0], item):
                    new_order = [each for each in order]
                    for itm in item:
                        try:
                            new_order.remove(itm)
                        except ValueError:
                            pass
                    self.min_price(new_order, price+total_price)
        else:
            self.all_price.append(total_price)
        return min(self.all_price)

Thanks again. :)

like image 56
Roshan Avatar answered Sep 28 '22 08:09

Roshan


You misunderstood yield. A function that contains yield magically becomes a generator. If you just call it, you get a generator object that you cannot directly use; you need to call its next() method to yank values from it. Most often you put it into a for loop.

>>> def genDemo(n):
...   while n > 0:
...     yield n
...     n -= 1
... 
>>> 
>>> gd = genDemo(3)
>>> gd
<generator object genDemo at 0x7fce12152820>
>>> gd.next()
3
>>> gd.next()
2
>>> gd.next()
1
>>> gd.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> for x in genDemo(3):
...   print x
... 
3
2
1
>>> _

Your code is just a normal recursive function; it could use return.

Generators are handy when you don't want to get the whole sequence in memory; it makes most sense with very long and infinite sequences. You could generate permutations until you find the one you need, or generate random numbers until you find the one you need, or read from a socket until you get a quit message, etc.

Regarding subsets: you can't check for subsets lazily unless your data is ordered the same way. With the data sets you have, you'd be better off using built-in set.

like image 34
9000 Avatar answered Sep 28 '22 07:09

9000