[Python] Генераторы, min/max, пустая последовательность
От: slava_phirsov Россия  
Дата: 05.04.10 06:15
Оценка:
Доброго времени суток всем читающим.

Вот такой вопрос: требуется найти min (или max) последовательности, генерируемой функцией-генератором, что-то вроде

def foo(bar):
  for i in bar:
    .....
    yield buzz

x = min(foo(y))


Проблема в том, что foo() может генерировать и пустую последовательность, т.е. вывалиться без единого захода в ветку содержащую yield. Поскольку min (и max) от пустой последовательности — это полный нонсенс , то в этом случае вылетаем с исключением ValueError:

ValueError: min() arg is an empty sequence


Можно конечно сделать вот так:

x = ()
try:
  x = min(foo(y))
except ValueError:
  pass


Но это как-то не того

Еще вариант:

def foo(bar):
  for i in bar:
    .....
    yield buzz
  yield ()

x = max(foo(y))


Пустая последовательность, сгенерированная на выходе из foo(), насколько я понимаю, меньше любой другой последовательности. Отмечу также, что в этом случае потребовалось слегка переработать алгоритм с тем, чтобы потребовалось искать не min а max (min в таком варианте, очевидно, даст нам пустую последовательность). Если алгоритм не допускает такую переработку — тем хуже для алгоритма.

А есть ли более кошерные варианты решения задачи?
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re: [Python] Генераторы, min/max, пустая последовательность
От: Temoto  
Дата: 05.04.10 10:30
Оценка:
_>Вот такой вопрос: требуется найти min (или max) последовательности, генерируемой функцией-генератором, что-то вроде

_>Проблема в том, что foo() может генерировать и пустую последовательность, т.е. вывалиться без единого захода в ветку содержащую yield. Поскольку min (и max) от пустой последовательности — это полный нонсенс , то в этом случае вылетаем с исключением ValueError:


И какой результат вы ожидаете от min от пустой последовательности? Пустой тупл?

try:
  x = min(foo())
except ValueError:
  x = ()


К сожалению, она поймает не только ValueError max, но и любой другой ValueError, который могла кинуть, в т.ч. и функция foo.
Re[2]: [Python] Генераторы, min/max, пустая последовательнос
От: slava_phirsov Россия  
Дата: 05.04.10 10:45
Оценка:
Здравствуйте, Temoto, Вы писали:

T>И какой результат вы ожидаете от min от пустой последовательности? Пустой тупл?


Потому и "полный нонсенс". В конкретном случае ситуация, когда генератор создает пустую последовательность должна обрабатываться особо (в примере — да, пустой кортеж). Если бы к генератору можно было применить len(), то:

gen = foo(y)
if (len(gen)):
  x = min(gen)
else:
  x = ()


Но это не прокатит ни разу.

T>
T>try:
T>  x = min(foo())
T>except ValueError:
T>  x = ()
T>


T>К сожалению, она поймает не только ValueError max, но и любой другой ValueError, который могла кинуть, в т.ч. и функция foo.


Потому и было сказано, что это не есть хорошо.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[3]: [Python] Генераторы, min/max, пустая последовательнос
От: Temoto  
Дата: 05.04.10 11:16
Оценка:
T>>И какой результат вы ожидаете от min от пустой последовательности? Пустой тупл?
_>Потому и "полный нонсенс". В конкретном случае ситуация, когда генератор создает пустую последовательность должна обрабатываться особо (в примере — да, пустой кортеж). Если бы к генератору можно было применить len(), то:

То есть вам сначала надо определить пустой ли генератор, и обработать этот случай особо, а потом найти минимум, если он таки не пустой?
Re[4]: [Python] Генераторы, min/max, пустая последовательнос
От: slava_phirsov Россия  
Дата: 05.04.10 12:12
Оценка:
Здравствуйте, Temoto, Вы писали:

T>То есть вам сначала надо определить пустой ли генератор, и обработать этот случай особо, а потом найти минимум, если он таки не пустой?


Вообще да, как уже писал выше, функция может дать пустой генератор, а может и непустой. Если генератор пустой, то return в вызывающий код, если же нет — найти min, обработать его и затем уже return.

Что-то вроде такого:
g = foo(y)
if g пустой:
  return
else:
  x = min(foo(y))
  # Обработка х
  ...
  return


Можно конечно забить на генераторы, и сделать вот так:
L = [i for i in foo(y)]
if not L :
  return
else:
  x = min(L)
  # Обработка х
  ...
  return


ИМХО, list comprehension добавит тормозов.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[5]: [Python] Генераторы, min/max, пустая последовательнос
От: Temoto  
Дата: 05.04.10 12:28
Оценка:
T>>То есть вам сначала надо определить пустой ли генератор, и обработать этот случай особо, а потом найти минимум, если он таки не пустой?

_>Вообще да, как уже писал выше, функция может дать пустой генератор, а может и непустой. Если генератор пустой, то return в вызывающий код, если же нет — найти min, обработать его и затем уже return.


_>Можно конечно забить на генераторы, и сделать вот так:

_>
_>L = [i for i in foo(y)]
_>


_>ИМХО, list comprehension добавит тормозов.


Именно компрехеншон здесь не нужен. Можно просто L = list(foo(y)).
Да, в любом случае (list или list comprehension) создание нового списка займёт какое-то время и память. И, скорее всего, для вашей задачи это абсолютно приемлимо.

Но если действительно нужно непременно сохранить процессор и память, то можно сделать так:

g = foo() # запускаем генератор
try:
  first = g.next()
except StopIteration:
  # специальный случай - пустой итератор
  return
x = min(itertools.chain([first], g))
# обработка минимального значения


И это будет эффективнее *только если* реализация min() сама не делает list().
Re: [Python] Генераторы, min/max, пустая последовательность
От: quodum  
Дата: 05.04.10 16:17
Оценка: 3 (2)
Здравствуйте, slava_phirsov,

Вот ещё вариант в дополнение к уже сказанному.

Подготовка (такой класс может и ещё где-нибудь пригодиться):
class AnyComparable (object) :
    def __init__( self, cmp_result ) :
        self.__result = cmp_result
    def __cmp__( self, other ) :
        return self.__result
    
gGreaterThanAny = AnyComparable(1)
gLesserThanAny = AnyComparable(-1)

Использование:
r_min = min(itertools.chain(GetOurIter(), [gGreaterThanAny]))
if r_min is gGreaterThanAny :
    print "it was empty..."

r_max = max(itertools.chain(GetOurIter(), [gLesserThanAny]))
if r_max is gLesserThanAny :
    print "it was empty..."
Re[2]: [Python] Генераторы, min/max, пустая последовательнос
От: slava_phirsov Россия  
Дата: 07.04.10 05:47
Оценка:
Здравствуйте, quodum, Вы писали:

class AnyComparable (object) :
  def __init__( self, cmp_result ) :
    self.__result = cmp_result
  def __cmp__( self, other ) :
    return self.__result

gGreaterThanAny = AnyComparable(1)


После чего:

def foo(bar):
  for i in bar:
    .....
    yield buzz
  yield gGreaterThanAny

x = min(foo(y))
if x is not gGreaterThanAny:
  .....



Да с этой палочкой я в кого хочешь могу превратиться!

Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.