Pull to refresh

Comments 9

Дочитал до update_constr и остановился. Почему вы выбрали кортеж для хранения данных?

all или any с generator expression хорошо подойдут для реализации satisfied

for direction in ('horizontal','vertical'): for move in (+1,-1):

Можно все четыре варианта прописать руками и тогда будет один цикл вместо двух. Код будет чуть проще.

Я такой цикл предпочитаю заменить на for dir, mov in get_neighboars(...): которая сгенерирует все возможные варианты. Туда же уйдёт фильтрация значений выходящих за границы поля.

В коде очень много отступов, что делает его сложным для чтения. Я бы порекомендовал измерить цикломатичискую сложность и упростить код, чтобы она не превышала 10. Я обычно стараюсь в 5ку уложиться.

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

Вернулся из отпуска, разгрёб дела и вот решение.

Глобальные отличия:

  • Я предпочитаю координаты хранить в порядке (row, col) , обычно поле это двумерный массив где первый индекс это ряд. value = field[row][col].

  • Вместо чистой рекурсии, я использую "Поиск с возвратом" (англ. backtracking). Я конечно предпочитаю иммутабл объекты, но в алогитмических задачака стараюсь делать решения O(1) по памяти.

  • Во втором поле None я земенил 0. Меньше текста и поле визуально выглядит однородным.

Дополнительные требовани которые я предявляю к своему решению:

  • Проходит все проверки ruff, включая наличие аннотаций, а также проверки mypy

  • Миниму использование базовых типов. Row, Col, Point, вместо int, int, tuple. Прочитал про "Одержимость элементарными типами" https://refactoring.guru/ru/smells/primitive-obsession и решил попробовать. Добавляет многословности и кучу проблем с mypy. Еще поиграюсь в своих проектах, а вот в рабочии тащить пока не буду. Техника интересная, но я еще не разобрался для каких проектов она будет полезна. Для текущей задачи это перебор.

  • Цикломатическая сложность меньше или равна 5ти (Больше 10 считается, что код плохой и его сложно поддерживать).

Вот метрики для моего и Вашего кода:

54:4: 'Field.__init__' 1
62:4: 'Field._get_row_side' 2
68:4: 'Field.is_empty' 1
74:4: 'Field._can_move' 1
79:4: 'Field._update' 1
83:4: 'Field._in_field' 1
86:4: 'Field.make_next_move' 5
107:0: 'traverse' 5
127:0: 'find_path' 1
136:0: 'extract_message' 4


1:0: 'search' 2
35:0: 'depth_first_search' 6
81:0: 'update_constr' 1
93:0: 'satisfied' 3
106:0: 'find_adjacent_cells_with_constr' 13
210:0: 'extract_message' 4

Чтобы получить низкую сложность я использовал инкапсуляцию.
Логика работы с полем собранна в Field. Там спрятанна и чехарда с двумя частями поля и проверка на потраченные ходы. В самой рекурсии вы просто получаете следующий ход.

Код здесь:
https://gist.github.com/Cjkjvfnby/7766935f710aa9d854cfb4826905d3af

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

По количеству строк у меня вышло более короткое решение, обычно это не так. Возможно это из-за массового дублирования кода. У вас нормальные имена переменных им комментарии не нужны. В случае комментирования recrf кода, можно сделать отдельную функцию и использовать комментарий для её имени.

# finish cell
finish = (15, 5)


# next col is inside grid
if next_col >= 0 and next_col < num_cols:
    next_row = cell_row
else:
    continue

Спасибо. Вы очень лаконично и профессионально решили.

Вот хорошая дискуссия про сложение строк: https://stackoverflow.com/q/39622122/1310066

В контексте решения алгоритмических задач, алгоритмическая сложность предпочтительней читаемости.

Sign up to leave a comment.

Articles