Программирование с ИИ
ENRU
4.4

Сужаем зону поиска

деление пополам вместо чтения всего подряд

Что это

Когда непонятно, где ошибка, читать весь код подряд — худший способ её найти. Есть приём надёжнее: разделить путь данных пополам, проверить середину и выбросить ту половину, где всё хорошо.

Так зона поиска сокращается вдвое на каждом шаге. Из тысячи строк до нужной — десять проверок.

Как выглядит в коде

Схема из книги — будет перерисована для сайта
Каждая проверка в середине выбрасывает половину. Из тысячи строк до одной — десять шагов.

На живом примере

Цепочка из четырёх функций: загрузить строки, разобрать, превратить в числа, посчитать среднее. Падает где-то внутри.

def load_rows(raw: str) -> list[str]: ...
def parse_rows(rows: list[str]) -> list[dict]: ...
def to_numbers(items: list[dict]) -> list[dict]: ...
def average(items: list[dict]) -> float: ...

print(average(to_numbers(parse_rows(load_rows(RAW)))))

Шаг 1. Печатаем в середине. Не в начале и не в конце — ровно посередине цепочки:

rows = load_rows(RAW)
items = parse_rows(rows)
print("после parse_rows:", items)
измерено
после parse_rows: [{'city': 'Oslo', 'temp': '4'},
                   {'city': 'Bergen', 'temp': '7'},
                   {'city': 'Tromso', 'temp': '-2.5'}]

Данные в порядке. Значит, первая половина цепочки чистая, и её можно забыть. Осталось две функции из четырёх.

Шаг 2. Делим остаток. Проверяем to_numbers на тех же данных, по элементам:

for it in items:
    try:
        int(it["temp"])
    except ValueError as e:
        print("падает на:", it, "->", e)
измерено
падает на: {'city': 'Tromso', 'temp': '-2.5'} -> invalid literal for int() with base 10: '-2.5'

Две проверки — и найдены и строка, и конкретные данные. Причина видна сразу: код предполагал целые температуры, а в данных дробная.

Где делить

Что ищемГде середина
Ошибка в цепочке функциймежду функциями, по данным
Ошибка в длинной функциимежду логическими блоками
Ошибка появилась после правокgit log — между коммитами
Ошибка на одном из тысячи элементовпо половине списка

Третья строка — отдельный приём: если код работал вчера и сломался сегодня, делить надо не код, а историю коммитов. У git для этого есть git bisect, но часто хватает и ручного переключения на коммит из середины.

Как сказать ИИ

Запрос
Вот цепочка из четырёх функций, где-то внутри падает.
Куда поставить одну печать, чтобы разделить цепочку пополам?
Код не чини, скажи только место.
Запрос
Я напечатал данные после parse_rows, они в порядке.
Значит, ошибка дальше. Что проверить следующим?

Такой диалог экономит время: ИИ хорошо подсказывает, где середина, а выполняешь и смотришь ты.

На что смотреть

Печать в начале и в конце вместо середины. Начало обычно в порядке, конец обычно сломан — обе проверки не сужают зону.

ИИ предлагает чинить, не сузив зону. Типичный ответ на «не работает» — три гипотезы сразу. Любая может быть верной, проверять придётся все. Сузь сам, потом проси починку.

Зона не сужается, а растёт. Если после каждой проверки появляются новые подозреваемые, значит, проверяются следствия, а не данные. Вернись к вопросу «данные здесь ещё целые?».

Отладка без записи. Проверил три места, забыл результаты, проверяешь заново. Записывай по строке: «после parse_rows — нормально».

Шпаргалка

  • Дели путь данных пополам, проверяй середину, выбрасывай чистую половину.

  • Вопрос на каждом шаге: «данные здесь ещё целые?»

  • Из тысячи строк до одной — десять проверок.

  • Сломалось после правок — дели историю коммитов, не код.

  • Печать в начале и в конце зону не сужает.

  • Записывай результаты проверок, иначе будешь ходить по кругу.