2014-01-02 2 views
4

У меня есть пункт меню в качестве ключа и цена как значение. Там может существовать сочетание предметов, которые будут немного дешевле, чем отдельные предметы. Для 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, 
} 

Теперь предположим, что я заказал несколько пунктов, то выход будет мин сумма данного заказа:

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) 

здесь код я попытался так для всех цен, которые я буду использовать в качестве генераторов :

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) 

, но когда я запускаю это он говорит об ошибке типа:

for each in rest_min_price(order_item): 
    print each 

TypeError: unsupported operand type(s) for +: 'int' and 'generator' 
+0

Обратите внимание, что 'list.remove' удаляет только первое найденное совпадение, вы можете использовать LC здесь:' new_order = [x для x в порядке [1:], если x не в элементе] ' –

ответ

5

Спасибо всем за ваш ответ. где-то я использовал ваше предложение, и теперь я решил его по тому же подходу с другой реализацией.

вот мой код:

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) 

Еще раз спасибо. :)

0

Ваша проблема возникает в вашей попытке объединить генератор с рекурсией:

yield price + rest_min_price(new_order) 

здесь price является int, но rest_min_price() является generator (потому что вам yield, а не return, его результат), поэтому вы не можете добавьте их вместе и получите TypeError.

Вы должны перебрать элементы в вашем генераторе:

for item in rest_min_price(order_item): 
    # process the item 
    # yield result with price 
+1

при обращении к ошибка, которая не решит задачу OP сталкивается с – alko

+1

@alko Если я не ошибаюсь, OP может нуждаться в решении на основе DP здесь. –

2

Вы поняли yield. Функция, которая содержит yield, волшебным образом становится генератором. Если вы просто назовете это, вы получите объект-генератор, который вы не можете использовать напрямую; вам нужно вызвать его метод next(), чтобы вынуть из него значения. Чаще всего вы помещаете его в цикл for.

>>> 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 
>>> _ 

Ваш код является просто нормальной рекурсивной функцией; он может использовать return.

Генераторы удобны, когда вы не хотите получать всю последовательность в памяти; это имеет смысл с очень длинными и бесконечными последовательностями. Вы можете генерировать перестановки до тех пор, пока не найдете нужную вам информацию или не создадите случайные числа, пока не найдете нужную вам информацию, или не прочитайте ее из сокета, пока не получите сообщение о выходе, и т. Д.

Что касается подмножеств: вы не можете проверить для подмножеств лениво, если ваши данные не упорядочены одинаково. С наборами данных у вас будет лучше использовать встроенный set.

+0

спасибо, что ответ я попробую с возвратом.для второго комплимента: на самом деле в комбинированных элементах может быть больше одного элемента в одном и том же порядке, как и у элемента заказа, может быть более 1 подсчета одного и того же типа ... поэтому we_cant используется здесь здесь .... :) – Roshan

+0

Ваши объекты набора это не просто продукты, а пары (еда, количество). Лучшее представление - это, вероятно, dict с именами в виде ключей и величин в качестве значений. Еще лучше что-то вроде ['defaultdict (int)'] (http://docs.python.org/2/library/collections.html#collections.defaultdict), который автоматически устанавливает значение count в 0 для любого запрошенного элемента. Вы можете напрямую сравнивать такие дикты. – 9000

+1

@roshan: ... или вы могли бы подумать о [Counter] (http://docs.python.org/2/library/collections.html#counter-objects). –

Смежные вопросы