Comments 38
Автору оригинальной статьи хорошо бы освоить технику инверсии проверок, когда не if (...) { <что-то важное> }
, а if !(...) return; <что-то важное>
. Очень помогает бороться с цикломатической сложностью.
Это называется guard clause, jfyi
Вот очень не люблю такой код. Если делать именно исключения - то всё ок. Если логику вносить - он путает. Поначалу ничего плохого, но вот потом...
Очень просто перенести логическую проверку с верхнего уровня на вызов функции, выставить ранний выход и радоваться выполненной задаче. А потом кто-то ещё будет работать с этой функцией, возможно из множества мест, а проверка будет приводить к преждевременному выходу. Из-за этого условие усложнится и туда добавят исключение(возможно даже протащив в функцию дополнительный опциональный параметр).
Ну и с логической стороны, если функцию вызываешь, то ожидаешь результат, а не тихое завершения без результата. В случае ошибки как раз всё ок - тихого завершения не будет. Да и при чтении такой код можно просто пропустить. А вот логику - нельзя.
Если вы не мало проектов глубоко перерабатывали, то возможно не раз и не два видели такое. У меня в практике встречалось чуть ли не в половине проектов. В двух проектах такой код разрастался до 20 проверок на функцию. Правда, те проекты весьма стары были, ещё с колбекным программированием.
Вот человеку выше поставили минусы без всяких аргументов. А мне очень интересно кто больше прав, он или минусующие. Но аргументы тут только с одной стороны...
Человек выше (@SAWER) по моему мнению написал какую-то бессмыслицу, а не аргументы.
Написал следующий абзац в самом в конце, но перетащил наверх, т.к. это самая суть:
Вообще, это всего лишь фича для улучшения читаемости кода, она ВООБЩЕ не меняет логику. А если всё-таки меняет, значит инвертирование условия выполнено некорректно и это уже не попадает под "радоваться выполненной задаче", т.к. на ревью тебе с вопросом "ты понимаешь, что это не эквивалентный код?" вернут задачу на доработку.
Далее имхо:
Я думаю, что если бы автор обсуждаемого комментария попытался написать примеры кода, иллюстрирующие его ошибочные аргументы, то он сумел бы сам себе их и опровергнуть. Мне тяжело представить работающий код, в котором были бы актуальны перечисленные проблемы.
К тому же выше речь не обязательно о "тихом выходе", когда if (!...) { return; } // some stuff
, там может возвращаться пустой массив, или просто короткий кусок логики.
В общем виде я это вижу как "много логики внутри if, нужно попытаться инвертировать относительно мЕньшего количества логики вне if", а будет ли там пустой return, о котором сокрушается автор комментария выше или что-либо другое — это всегда вопрос логики и конкретного примера.
Я каждый день вижу много хорошего и плохого кода, простого и переусложнённого, и регулярно что-то перерабатываю и рефакторю, а также периодически возвращаю на доработку задачи с ошибками после ревью, и я никогда не сталкивался со сложностью понимания и переиспользования таких инверсий с "тихим" выходом из функции. Проект у нас на миллионы строк кода, поэтому Простора для возникновения описанных автором проблем там полно, однако самих Проблем всё же нет.
Так бы и писали) Но это единственный коммент)
Пожалуй, возвращение пустого ответа так же возможно, если только это будет обработано полноценно в дальнейшем, т.е. не является аналогом return;
, точнее того же тихого выхода. Именно так было написано в начале этой ветки, что это уменьшает цикломатическая сложность(на деле - нет). В случае, когда возвращается какое-либо значение, то разницы по сравнению с обычным описанием нет.
Проблема не том, что это сложно понять - это всё легко понимается. Проблема в том, что часто постепенно логику из вызова переносят в функцию. И, условно, если в функции 5 ранних выходов(условий) затрагивающие 5 разных фич, то для корректной правки в этих условиях нужна инфа обо всех этих 5 фичах, т.к. правка может все их затронуть.
Вот пример:if (!symbol_idleFX) return;
nextReward == 0
if (!idleFX) return;
if () return;
Расчёт вознаграждения. Одно условие тут проверяет состояние, одно нужно для обработки, а ещё одно никак нигде не фигурирует и возможно завязано на бизнес-логику. И вот каким боком тут проверка symbol_idleFX
вообще не понятно. Если новая фича будет не связана с этим параметром и функция должна будет отработать даже когда symbol_idleFX == false
, то просто вызвать функцию уже будет нельзя. Нужно будет смотреть внутрь неё и проверять из-за чего она не работает.
Почему вы с таким не сталкиваетесь, не знаю. Возможно редко переиспользуете код или переиспользуете большими кусками? Меньше интенсивность работы? У меня проекты обычно поменьше, до 150к, но за год около 300к вполне может быть пере-/написано в этом проекте.
Вы всё ещё настаиваете, что "тихий" выход может повлиять на логику выполнения метода, но я тоже повторю, что в таком случае это уже не чистое инвертирование, а либо ошибочное внесение изменений в поведение, т.е. не относятся к нашему сабжу.
Ваш пример ничего не демонстрирует, какая логика в нём перестаёт выполняться? Покажите, пожалуйста, хотя бы условный кусок кода "до" и "после" рефакторинга, когда в изменении логики виноват именно "тихий выход"? Бьюсь об заклад, что вы просто напишете неэквивалентный код, и проблема будет вовсе не в инвертировании условий с тихими выходами.
Действительно, могут быть куски кода, где 3 подряд подобных условия могли изначально влиять друг на друга или на выполнявшуюся логику, но человек, который эти условия добавляет, должен сам удостовериться в том, что все ветки кода имеют ту доступность, которая ожидается, а если какая-то ветка кода стала недостижимой из-за "тихого выхода", значит кто-то просто криво написал этот код. Не понимаю, как может быть иначе, буду признателен, если пример всё же будет.
Я считаю, что не сталкиваюсь с этими проблемами, потому что Ваши доводы нелогичны, а не по предположенным Вами причинам, но будет интересно ошибиться =)
Я не говорю, что невозможно написать эквивалентный код. Можно. Достижимой она останется, но при условия меняются.
И он бы выглядел примерно так:if (!newOption){
if (!(symbol_idleFX)) return;
Но для этого пришлось бы лезть внутрь функции и разбираться в её работе. Чем больше таких условий, тем больше шанс ошибки и меньше шанс корректной работы программиста, т.к. узнавать причины появления всех условий сложно и дорого. Поэтому программист просто напишет:
if (!idleFX) return;}
if(nextReward == 0) return;if (!newOption)return;
Т.е. это становится причиной того, что пишут кривой код. И она исходит из того, что функция не делает того, что должна по названию. В данном случае функция должна была показать награду, но она вместо непосредственно показа занималась ещё и валидацией данных(которая вообще должна быть на входе, а не тут), ну и логикой в решении, вообще показывать или нет.
>Поэтому программист просто напишет:if (!newOption)return;
Я же правильно понял, что В вашем примере "программист" напишет следующее в качестве рефакторинга:
if (newOption){return;}
if (!(symbol_idleFX)) return;
if (!idleFX) return;
if(nextReward == 0) return;
?
(если Вы не это имели ввиду, то нужно было полностью привести код "до" и "после" для наглядности проблемы)
Ну, и на каком основании программист это напишет, если ниже есть кусок кода, который перескачет в другую ветку кода? Это же логика, и в Вашем примере она просто нарушается, как можно это приравнивать к инвертированию, если это не является инвертированием, а является просто внесением изменений в логику?
Вот будет вполне корректное инвертирование в Вашем примере:
if (newOption)
{
if (nextReward == 0) return;
return; // Да, довольно глупо выглядит из-за отсутствия
// описанной логики, но зато логика выполнения
// сохранена
}
if (!(symbol_idleFX)) return;
if (!idleFX) return;
И никаких проблем я тут не вижу.
Можете в ЛС прислать листинг или ссылку на какой-то заполненный онлайн-редактор кода, если считаете проблему всё ещё актуальной, я объясню, почему там тоже можно сделать правильно без особых затрат. Мы же всего лишь говорим об условных инструкциях, а не о каких-то сложных конструкциях...
С алгоритмами сразу все было понятно, языковая модель не умеет думать. Она может понять что то, что от него просят, это частный случай решения которое он знает. Или решение похоже на другое, только термины разные.
Но и чисто техническими задачами он не всегда справляется. У меня была идея написать себе помодоро таймер, который после того, как закончится время, начнет отслеживать используемые приложения и пропищит только когда я сам переключусь на непродуктивные.
Чтобы проще было с интерфейсом, попросил его использовать electron. За час ничего путного так и не получилось, я в итоге забил. Он правильно определил проблему, что ему надо обратиться к X серверу операционки, но рабочий код написать не смог.
Спасибо за развёрнутый комментарий. Теперь я понимаю в чем вся соль :)
Чтобы проще было с интерфейсом, попросил его использовать electron
Вот так вот и рождаются всякие Балена Этчеры в ~200 Мб при живом Руфусе в 2 Мб.
На сколько я понимаю, приложение electron не может выйти за пределы браузерного движка на котором оно работает, и контролировать другие приложения (клиенты) X сервера. Задачка выглядит как предложение выйти из песочницы и найти и использовать дыру в безопасности графической системы Linux. Очень нетривиально.
Электрон запускает сразу 2 процесса. Один из них рендерер, умеет тоже, что и браузер. Второй работает на уровне системы и имеет все возможности полноценно работать с операционкой, железом и так далее.
Я в итоге попросил ChatGPT использовать высокоуровневую библиотеку. Он подсказал, какую можно применить, но почему-то рабочий код и с ней написать не смог. Хотя она очень простая, там в итоге решение на 3 строчки с этой библиотекой.
В этом вся прелесть ChatGPT, что он может сэкономить профессионалу время, как инструмент, но не может заменить специалиста. На текущем уровне, во всяком случае.
Надеюсь, это реальное ограничение ChatGPT а не его желание скрыть от разработчиков реальные возможности, чтоб не создавать волну. :)
Насчёт съэкономить это только в очнь простых решениях
Сложное ж нужно за ним 8 раз проверять и перепроверять ... даже если он и "нагуглил" , что то близкое к нужному
А перепроверка и чуть правка ... порой дольше чем с нуля зашлёпнуть что то
Да что тут доказывать, все сильно проще. Берём, копируем вставляем текст задачи Эйнштейна боту. Бот её незамедлительно решает (конечно же). Берём абсолютно то же описание, но заменяем немца на русского. Всё, бот сломался. Пытается и думать, и спекулировать, и сразу ответ говорить, но он ни разу даже не угадал. Сразу видно, что да, думать он не умеет, причем, как это очевидно, задача изменена минимально.
Никто не сказал автору оригинала, что можно засунуть а* во внешний цикл перебора посещенных квадратов с огнём. Если использовать двоичный поиск, то это умножить сложность на log L, где L это длина пути
Я не специалист, но может просто уменьшить "стоимость" огня с 1000 до 10?
если вы не можете просто назначить огню более высокую цену, вы можете рассчитывать несколько стоимостей пути по разным критериям и дополнительно проверять все стоимости всех путей на ваши критерии.
В целом, задачи довольно сложные а многие талантливые программисты-самоучки даже не знают теорию графов и не смогут решить задачу. Кстати задача с серпами - возможно её можно упростить методами линейной алгебры создав некоторый матричный оператор.
Хочу сказать что задачи намного выше уровня среднего программиста.
Вы правы, это как после сортировки по цене отсортировать равные между собой позиции по алфавиту к примеру. Огонь не "цена", это новая абстракция. Обычная дискретная математика.
Мне кажется GPT-4 справится с данной задачей, если ему подробно указать путь решения. Сам путь она не построит из-за того что требуется додумать множество нюансов, полагаясь на здравый смысл которого у неё нет.
Ну и вообще, я считаю просить выполнять такие крупные задачи её не стоит, лучше попросить её, как в случае с серпами, найти алгоритм для одной такой фигуры и уже самостоятельно додумать до множества. Она мне так множество законов физики объясняла на ура.
Я пробовал дать две задачи - написать тест по коду и наоборот. Тест по коду пишет довольно неплохо. Код по тесту, увы пишет, так, что код не проходит предлагаемый тест. Подробности я написал здесь - https://www.cyberforum.ru/delphi/thread3075870.html
Спасибо за статью. Думаю, что пока это лишь инструмент для знающего человека, который сэкономит время.
Возможно кому-то будет интересна другая статья на подобную тему: Хорошо ли ChatGPT ищет ошибки в коде?
Если честно, я не понимаю, с чем мучился автор статьи в первой задачи. Кто-нибудь может сказать, почему нельзя было просто сделать веса ребер парами? Тогда обычный алгоритм Дейкстры искал бы самый кратчайший путь, а среди таких он бы искал путь с наименьшим количеством огненных клеток. Ну или веса в паре можно перевернуть и тогда он бы искал путь с минимальным количеством огня, а среди таких выбирал бы самый короткий. Я до конца не понял, какой из этих вариантов нужен был автору.
Вполне можно. Такая измененная функция расстояния — (длина пути, количество посещенных клеток с огнем) — отлично удовлетворяет всем требованиям к расстоянию для алгоритма Дейскстры: одинаково продлить равные по длине пути — даст равные по длине пути; Удлинение пути только увеличивает длину.
Поэтому можно запускать и протую Дейкстру и А* на таком графе.
Edit:
дочитал статью. Действительно, так не получится, потому что автору нужен не кратчайший путь, с минимальным количеством огненных клеток, а путь не длиннее заданного расстояния, с наименьшим количеством огненных клеток.
farafonoff уже привел лучший вариант решения этой задачи. Перебирайте ограничение по количеству огненных клеток бинарным поиском. Внутри запускайте A*.
Решение автора, похоже, ялвяется чем-то вроде A* на кубе (координаты клетки, количество огненных клеток), поскольку может заходить в одну и ту же клетку кучу раз, когда найдет путь с другим количеством огненных клеток.
Отсюда вывод: мнение "теперь все вместо писания кода будут подчищать код после ChatGPT" - является неверным. Так как для любой нетривиальной задачи будет проще написать заново, а не пытаться поправить код из-под ChatGPT.
Вот так в очередной раз ИИ обдурил глупых человеков, якобы он не может их заменить и им не о чем беспокоиться ¯ \ _ (ツ) _ / ¯
Кто-то ожидал, что у него что-то выйдет?
Первое, что приходит в голову - штраф за клетку огня в условные 3 единицы, которые учитываются при выборе маршрута, но фактически не вычитаются из количества жизней.
Чтобы в конце пути не возникал остаток, он запрашивается в первичной конечной точке и продлевается на на не списанные огненные штрафы. Костыль конечно, но будет работать.
Интересно, принципиальное ли это ограничение для таких систем?
Интуитивно кажется, что обучаясь отвечать то что больше всего "удовлетворит" спрашивающего, нет мотивации выяснить истину. Как она может найти ответ на то что никто не знает? Скорее она попытается придумать какое-то правдоподобное фуфло, так как этим достигнет цели выдать "удовлетворяющий" ответ в подавляющем числе случаев.
Какие же вы наивные, если думали что "улучшенный" поиск (чем я и считаю ChatGPT) с псевдо поведением человека может обладать интеллектом. Если он даже на запрос рецепта пельменей, отвечает чушь.
Тоже использую ChatGPT как замену Google поиску. Он реально сокращает время поиска нужных материалов и документации. Так же он хорошо справляется с тривиальными задачами и проверками. Считаю, что с появлением таких инструментов со временем исчезнут ресурсы, похожие на stackoverflow... Но доверять решение бизнес задач и брать на веру его решения я точно не стану, и вам не советую.
Я конешн не специалист, но почему нельзя просто сделать так:
1. На количество шагов за ход забиваем - просто ищем лучший путь и потом за несколько раз доходим по нему до цели (если карта динамическая, то просто вычисляем путь каждый ход)
2. Чтобы найти лучший путь непроходимые преграды игнорим - не нужно им давать вес 9999, если только у вас карта не меньше чем 100х100
3. для огня берем вес больше чем клеток на карте - условно 9998 (9999 уже непроходимо)
4. вычисляем кратчайший путь с учетом весов стандартным алгоритмом - тут будет гарантированно выбран тот путь, которым можно дойти до цели не заходя в огонь, либо же выбран путь с наименьшим количеством заходов в огонь если нельзя без него
5. стоимость прохода по клеткам на найденом пути известна - земля или огонь =1, вода =2, можно двигаться
Может ли GPT-4 на самом деле писать код?