один лайнер в go через повторяющийся (генератор) - PullRequest
4 голосов
/ 16 июня 2020

Я столкнулся с некоторым кодом, похожим на:

[func(val) for val in iterable]

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

Что мне не нравится в этом подходе, так это то, что создается временный list, который может потреблять довольно много памяти, если генератор выдает множество значений.

Если возвращаемое значение func всегда оценивается как False, то работает следующее:

any(func(val) for val in iterable)

Если возвращаемое значение fun c всегда оценивается как True, затем следующие работы:

all(func(val) for val in iterable)

Что мне делать, если возвращаемое значение fun c может оцениваться как True или False

Что может быть лучше, чем принудительное присвоение значения False?

Лучшее, что я придумал:

any(func(val) and False for val in iterable)

или

all(func(val) or True for val in iterable)

Ответы [ 3 ]

4 голосов
/ 16 июня 2020

Вероятно, просто

for val in iterable:
   func(val)

является наиболее ясным.

for val in iterable: func(val)

доступно, если однострочник действительно необходим.

1 голос
/ 17 июня 2020

Просто синтез и временной анализ предоставленных ответов / возможных решений

Кажется, что ответ @ LeopardShark - самый короткий и самый читаемый ответ. и среди самых быстрых. (время неточно, и я не смотрел на байтовые коды)

Мудрая скорость - ответ @ LeopardShark - второе предложение @ywbaek - исходный код, который я нашел, если N не слишком велико (~ 10000 ) - предложения, которые я опубликовал в своем вопросе

У исходного кода есть недостаток в том, что выделяется освобождение памяти даром.

Код, который я предложил в моем вопросе, имеет недостаток, заключающийся в том, что он менее интуитивно понятен чтобы понять, и если испортить комбинацию (all, any) и (and False, or True), можно не выполнить все, как ожидалось, а также будет немного менее производительным

@ ywbaek's решение безопаснее, чем мои предложения, и примерно так же интуитивно понятно, но выполняется немного быстрее.

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

Мой код для измерения времени:

N=10000
M=500

called = 0
def func(v):
    global called
    called += 1
    v * v * v * v * v *v / (v+0.1)

def iterable(N):
    for v in range(N):
        v * 2
        yield v

def testrun():
    global called
    called=0
    print(timeit(test, number=M), end=" ")
    print(called)

print("consume some CPU")
timeit(lambda: 3**.5 **.2) # it seems, that timeit is a little more predictable if I let the process warm up a little

print("Start measures")

def test():
    for val in iterable(N): func(val)
testrun()

def test():
    {None for val in iterable(N) if func(val)}
testrun()

def test():
    [func(val) for val in iterable(N)]
testrun()

def test():
    all(func(val) or True for val in iterable(N))
testrun()

def test():
    any(func(val) and False for val in iterable(N))
testrun()

результаты на моем старом P C:

consume some CPU
Start measures
3.864932143012993 5000000
3.916696268017404 5000000
4.0817033689818345 5000000
4.293206526956055 5000000
4.319622751965653 5000000

1 голос
/ 16 июня 2020

Как насчет использования set с функцией bool?

{bool(func(val)) for val in iterable}

EDITED:
Посмотрев на анализ @ gelonida, я считаю, что следующее немного быстрее.

{None for val in iterable(N) if func(val)}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...