2013-11-06 4 views
0

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

Например:

Определить функцию, включая печать, чтобы проверить, будет ли функция была выполнена

In [33]: def func(x): 
    ....:  print x 
    ....:  return bool(x) 
    ....: 

Pass operator.or_ как функция снижения

In [34]: import operator 
In [35]: reduce(operator.or_, [func(1), func(0)]) 
1 
0 
Out[35]: True 

Затем мы обнаружили, что второй функция была выполнена даже тогда, когда первая функция возвращает True.

Если я использую или выполняю операцию непосредственно, он немедленно вернется, как только он найдет, что один из них вернет True.

In [36]: func(1) or func(0) 
1 
Out[36]: True 

Однако я не могу этого сделать, если у меня есть большой список.

Есть ли элегантный способ сделать это? Или мне нужно, чтобы проверить цикл?

Update

Происхождение способ я использую для любого является

In [26]: any([func(1), func(0)]) 
1 
0 
Out[26]: True 

Это действительно оценить все функции.

Ответ Via @Martijn Pieters, теперь я знаю, что могу использовать его неправильно. Извините за непонимание.

+2

Не замыкает ли короткое замыкание? –

+0

'any()' не имеет плохой производительности, это то, что вы хотите здесь. – arshajii

+0

Почему 'any()' slow? –

ответ

4

any() является точно то, что вам нужно здесь, в сочетании с выражением генератора:

any(func(i) for i in big_list) 

Это остановить итерацию для первого значения, где func(i) возвращает истинное значение. Как только будет найдено значение True, вы доказали, что значение a истинно во входной последовательности («есть любое значение, которое является истинным?» -> yup, мы нашли хотя бы один) ,

Для and, вы бы использовать вместо all():

all(func(i) for i in big_list) 

который вернет False данный момент найдено значение по func(i) falsey. Если найдено одно ложное значение, то вы доказали, что существует не менее значение, это не так, поэтому они не могут быть правдивыми.

Обратите внимание, что эти две функции дают выражение генератора:

(func(i) for i in big_list) 

Это оцененную лениво; каждый раз, когда вы запрашиваете следующее значение выражения генератора, он будет оценивать цикл и выполнять одно выражение func(i).Он не будет производить весь список сразу, он будет производить предметы один за другим.

Ваше выражение reduce(operator.or_, [func(1), func(0)])имеет, чтобы собрать весь список входных данных, прежде чем он сможет позвонить reduce(). Метод reduce() будет обрабатывать полный список , он не будет закорочен, поскольку он не знает, какая операция применяется к входным значениям. Вы также можете дать reduce() выражение генератора, но оно не остановит повторение после того, как будет установлен исход (по первому истинному значению для or или по первому ложному значению для and), опять же потому, что reduce() не имеет специальных знаний о операции, являющейся выполнено.

+1

Возможно, вы должны указать разницу между созданием списка, который выполняет func() для каждого элемента списка, и создания итератора, который будет выполнять func() только для элементов, к которым обращаются. – bj0

+0

Спасибо! Но мне интересно, почему 'any (func (i) для i в big_list)' и 'any (big_list)' имеют другое поведение. – waitingkuo

+0

@waitingkuo: та же самая причина, по которой 'и' и 'or' имеют другое поведение; это разные операции. –

1

Добавление к другой ответ, проблема с этим:

reduce(operator.or_, [func(1), func(0)]) 

Это аргументы всегда оценивали перед функция вызывается, так как Python не делает lazy evaluation. Использование итератора (как в ответе Martijn) позволяет избежать этого, поскольку он генерирует список по мере необходимости, а не сразу.

+0

Спасибо, что ответили, это мне очень помогает :) – waitingkuo

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