Как стать автором
Обновить

Комментарии 36

Камрад, не умаляя значения проделанной работы, хочу напомнить о наличии в стандартной библиотеке itertools, в том числе функций product(), permutations(), combinations(), combinations_with_permutations().
Я уже написал о изобретения велосипеда, предвосхищая ваш коментарий. Зачем комениторовать не полность прочитанный текст…
Каюсь, не заметил.
Также совершенно нe к месту assert в теле функции, который работает лишь при заданном __debug__.
Наоборот, он не работает при включенных оптимизациях (никогда не видел, чтобы кто-то их включал). Более того, это очень правильный способ проверить, что что-то не так.
assert в данном случае используется неправильно, по очень простой причине: он производит валидацию значений входных данных, чем должна заниматься бизнес-логика. assert нужен в качестве инструмента для поиска ошибок программиста, например:

assert isinstance(k, int), 'Invalid input "k" argument type in template(), should be int, received %s' % type(k)
А я видел. Например, anki под win32.
Отладка была мучительной, потому что внутри assert происходил какой-то побочный эффект.

Итого, проверять assert'ом нужно только то, что не должно происходить в программе никогда. Неправильные аргументы функции нужно проверять if'ом с вызовом исключения в случае необходимости.
Выразительная простота python


Я не осилил этот код в течении нескольких минут не смотря на то, что сам когда то ради интереса писал подобное. Не осилил потому что постоянно натыкался на странные слова template, reducer, assertion и пытался понять а что это за хрень такая и зачем она может быть нужна. Возможно с более интуитивно понятными именами получилось бы нагляднее.
    n = len(s)
    ...
    for i in range(n):
        c = s[i]

Откройте для себя enumerate.

На самом деле Ваш код просто ужасен. Автор, извините, но у Вас C++ головного мозга, на python так не пишут. Pythonic-код — это понятный, легко-читаемый код с нормальными именами переменных, Вы же, видимо, стремились к максимально короткому и насыщенному лишними абстракциями коду.
Не могли бы вы написать это на питоне как надо? Я сам о питоне только слышал и поэтому любопытствую.
Конкретно этот код автора вот так можно переписать:

for i, c in enumerate(s):
    ...

Особо в код не вчитывался, может там вообще всё можно переписать. Сейчас код автора выглядит плохо. На Python так не пишут.
А как надо можно узнать?
Но в общем, переменные с ничего не говорящими именами s, k, c, new_s — это уровень первого курса. Если Вы сейчас закроете эту страницу и откроете её через пару месяцев, то может и Вам код покажется нечитаемым и непонятным. Очень рекомендую «Code complete» («Совершенный код» в русском переводе) для улучшения навыков написания красивого кода.
То есть ваши претезии только к форме, а не к содержанию? Насчет названия переменнных, что такое k, n должно быть понятно из таблицы над кодом(это стандартные обозначения в комбинаторике), а s для стороки и с для символа тоже не редкость
Нет, к содержанию тоже есть претензии. Я бы изначально написал каждую функцию по-отдельности, не используя общие template, с передачей в него пары лямбда-выражений.
А можно продолжить мысль и объяснить почему?
Такой код было бы гораздо легче читать. Т.е. представьте себя на месте человека, который не особенно знаком с комбинаторикой и разбирает Ваш код. Он начинает с permutation_norep(). Тут же он сталкивается с тем, что далее идёт вызов template() да ещё с двумя аргументами-функциями. Хорошо, идём дальше в глубину template()… Опа, assertion(). Так, а что там за assertion() был? Ага, n и k дожны быть положительными, либо равны нулю, причём n >= k. Далее — reducer(). А что там этот reducer делает?.. Думаю, Вы поняли мысль.

Вот как например будет выглядеть целостная permutation_norep:

def permutation_norep(s, k):
    len_s = len(s)
    is_valid_input = len_s > 0 and len_s >= k and k >= 0

    if not is_valid_input:
        raise ValueError('Invalid input: s={}, k={}'.format(s, k))

    if k == 0:
        yield ""
        return
    k <= 1:
        yield from s
        return

    new_k = k - 1

    for i in range(len_s):
        new_s = s[:i] + s[i + 1:]
        for res in permutation_norep(new_s, new_k):
            yield s[i] + res
Хорошо… Но задача написать не одну функцию, а четыре. Как я упомянул в своем посте, сначала были написанны все 4 функции подобным образом. Когда перед тобой 4 почти одинаковые функции, а различия в состоят в двух строках кода, появляется естественное желание выделить общее. Такой код намного проще сопровождать, Что бы заменить assert на if(как вы предлагаете ), например, мне нужно одно изменение, а не в четыре. Шаблоны проектирования придуманы не мной, и не один я ими пользуюсь… Так что может ваш код и более «питонистый», но с точки зрения вопроса проектирования я вижу большой минус.
Я думал об этом, когда писал предыдущий ответ и решил, что я в данном случае я остановился бы на 4-х функциях. Спросите себя, неужто так много усилий стоит поддерживать конкретно эти функции? Скорее всего Вы напишете их и забудете надолго, до тех пор, пока не надо будет внести кардинальные изменения. Опять таки, это не должно занять так много времени, ведь функции по-сути одинаковые.
При всём, я согласен, что в других случаях такой подход совершенно неприемлим и логичнее использовать пострение через шаблон.
Если взглянуть на itertools.combinations(iterable, r) то там тоже присутствует однабуквенная переменная…
Хотел бы взглянуть на просто прекрасный код…
>> поэтому он позволяет проще и выразительнее писать код, который бы на других языках выглядел бы более громоздко и запутаннее
Не обощайте: www.ruby-doc.org/core-2.1.2/Array.html#method-i-permutation
В python эти функции есть в стандартной библиотеке, о чем сказано в первом комментарии, автор просто попытался реализовать их самостоятельно.
Согласен, но сути не меняет. Python не самый выразительный язык, код на других я языках не будет выглядеть более громоздким и запутанным.
Ок, пусть так, только давайте не будет устраивать холивар Ruby vs. Python )
Ruby vs Python был бы самым бессмысленным холиваром :)
Может с пыхой тогда? А то скучновато в субботу с утра.
Господа критики, критика должна быть конструктивной. Выражения типа «код автора выглядит плохо. На Python так не пишут», «Ваш код просто ужасен» и т.д. без конкретных замечаний и реализаций смотрятся странно. Критикуя-предлагай.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.