Генератор Python против функции обратного вызова

У меня есть класс, который решает проблему точного покрытия, используя рекурсивный алгоритм поиска с возвратом. Первоначально я реализовал класс с помощью функции обратного вызова, которую я передал объекту во время инициализации. Этот обратный вызов вызывается всякий раз, когда найдено решение. Глядя на чью-то реализацию той же проблемы, я увидел, что они использовали операторы yield для передачи решения, другими словами, их код был генератором Python. Я подумал, что это интересная идея, поэтому сделал новую версию своего класса, чтобы использовать урожайность. Затем я провел сравнительные тесты между двумя версиями и, к своему удивлению, обнаружил, что версия генератора работает в 5 раз медленнее, чем версия обратного вызова. Обратите внимание, что, за исключением переключения доходности для обратного вызова, код идентичен.

Что здесь происходит? Я предполагаю, что, поскольку генератору необходимо сохранить информацию о состоянии, прежде чем уступить, а затем восстановить это состояние при перезапуске при следующем вызове, именно это сохранение / восстановление является причиной того, что версия генератора работает намного медленнее. Если это так, сколько информации о состоянии генератор должен сохранить и восстановить?

Есть идеи от экспертов по Python?

- Отредактировано 7:40 PDT

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

   def solve(self):
      for tp in self.pieces:
         if self.inuse[tp.name]: continue

         self.inuse[tp.name] = True
         while tp.next_orientation() is not None:
            if tp.insert_piece():
               self.n_trials += 1
               self.pieces_in += 1
               self.free_cells -= tp.size

               if self.pieces_in == len(self.pieces) or self.free_cells == 0:
                  self.solutions += 1
                  self.haveSolution = True
                  yield True
                  self.haveSolution = False
               else:
                  self.table.next_base_square()
                  for tf in self.solve():
                     yield tf

               tp.remove_piece()
               self.pieces_in -= 1
               self.table.set_base_square(tp.base_square)
               self.free_cells += tp.size

         self.inuse[tp.name] = False
         tp.reset_orientation()

Почтовый цикл, который вызывает решатель (после инициализации, конечно)

   start_time = time.time()
   for tf in s.solve():
      printit(s)

   end_time = time.time()
   delta_time = end_time - start_time

В версии обратного вызова цикл завершается с помощью всего лишь одного вызова для решения.

6
задан sizzzzlerz 18 April 2011 в 14:44
поделиться