Как уведомить родительский поток о завершении работы в Python - PullRequest
0 голосов
/ 24 июня 2019

Я хотел бы использовать следующий код, чтобы найти определенное количество перестановок в списке.

def permutations(ls, prefix, result):
    if ls:
        for i in range(len(ls)):
            permutations([*ls[0:i], *ls[i + 1:]], [*prefix, ls[i]], result)
    else:
        result.append(prefix)

    return result

Моя проблема в том, что я не могу просто включить другой параметр для подсчета количества найденных перестановок. Я не могу этого сделать, потому что каждый рекурсивный вызов permutations() «разбивается» на новую «версию» (это как разветвление на дороге со старым счетчиком, и каждая ветвь считает свое количество найденных перестановок, не связываясь с другие). Другими словами, это не сработает:

def permutations(ls, prefix, result, count, limit):
    if count > limit:
        return 

    if ls:
        for i in range(len(ls)):
            permutations([*ls[0:i], *ls[i + 1:]], [*prefix, ls[i]], result)
    else:
        count += 1
        result.append(prefix)

    return result

Итак, что я хотел бы сделать вместо включения параметра count в сигнатуру функции, это уведомлять какую-то другую часть моей программы каждый раз, когда обнаруживается новая перестановка, и таким образом отслеживать счет. Это может быть хорошим использованием потоков, но я хотел бы сделать это без распараллеливания, если это возможно (или, по крайней мере, самое простое распараллеленное решение).

Я понимаю, что мне придется создавать новый поток при каждом вызове permutations([*ls[0:i], *ls[i + 1:]], [*prefix, ls[i]], result) в цикле for.

Я надеюсь, что кто-то будет так любезен, чтобы указать мне правильное направление или сообщить мне, если есть лучший способ сделать это в Python.

1 Ответ

1 голос
/ 24 июня 2019

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

Причина в том, что чем проще и прямее вы можете решить проблему, тем легче думать.

В качестве второго совета, всякий раз, когда вы обнаруживаете, что перебираете перестановки, вам, вероятно, следует найти лучший подход. Причина в том, что число перестановок длины n растет как n!, и в зависимости от того, что вы делаете / вашего терпения, компьютеры достигают вершины где-то между n=10 и n=15. Таким образом, поиск способов считать без фактической итерации становится необходимым. Как это сделать, конечно, зависит от вашей проблемы.

Но вернемся к проблеме, как указано. Я лично решил бы этот тип проблемы в Python, используя generators . То есть у вас есть код, который может генерировать следующий элемент списка в генераторе, а затем в другом месте вы можете иметь код, который его обрабатывает. Это позволяет сразу начать обработку списка и не хранить его в памяти.

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

Если вы работаете с другой формой совместной многозадачности, используйте ее вместо этого. Так, например, в JavaScript вы должны выяснить, как координировать действия с помощью Promises. (К счастью, синтаксис async / await позволяет вам сделать это и сделать его похожим на генераторный подход. Обратите внимание, что вы можете сразу получить большие части набора данных в памяти. Как избежать этого - тема сама по себе .) Для другого примера, в Go вы должны использовать каналы и программы.

Я бы обратился только к глобальным переменным в качестве крайней меры. И если вы это сделаете, помните, что вам нужно достаточно памяти, чтобы сохранить весь набор данных, который вы перебрали в памяти сразу. Это может быть много памяти!

Я предпочитаю все это обычному многопоточному подходу.

...