Как можно программировать с помощью ChatGРТ

ИИ вызывает всё больший интерес со стороны разработчиков. Особенно интересно предположение, что скоро ИИ сможет заменить разработчика.

Проверим это!

Весь диалог с ИИ происходит на сайте openai.

Процесс выглядит следующим образом:

  • Даём описание нужного кода на русском языке, с описанием входных данных, и алгоритма решения
  • Анализируем результат на предмет корректности кода
  • Делаем корректировку алгоритма, если ИИ свернул не туда

Изначально задача, хотим запрограммировать алгоритм вычисления некого KPI, который вычисляется на основании динамики ошибок инспектора кода в нашем проекте. Файлы проекта проверяются после каждого комита разработчика. Изначально оценка 500. Если кол-во ошибок уменьшилось, увеличим оценку. Если кол-во увеличилось, оценка снижается. И сделаем градацию приоритета ошибок, за критические ошибки будет понижать оценку сильней.

Даём первоначальное описание алгоритма:

Исходный код метода на java, для расчета оценки на основании следующей информации. Есть список файлов, и для каждого файла количество критических и некритических ошибок с датой проверки. Нужно посчитать общее кол-во ошибок на первую дату. Нужно посчитать общее кол-во ошибок на вторую дату. Сравнить полученные числа, и если количество ошибок выросло, то вычесть из 500 по 5 баллов за каждую новую ошибку. Если кол-во ошибок уменьшилось, то 500 плюс 5 баллов за каждую исправленную ошибку.

import java.util.*; public class ErrorCounter { public static void main(String[] args) { // Пример данных для расчета ошибок List<FileData> files = new ArrayList<>(); files.add(new FileData(100, 20, "2022-03-01")); files.add(new FileData(50, 10, "2022-03-02")); // Расчет общего количества ошибок на первую дату int firstDayErrors = 0; for (FileData file : files) { if (file.date.equals(files.get(0).date)) { firstDayErrors += file.criticalErrors + file.nonCriticalErrors; } } System.out.println("Общее количество ошибок на первую дату: " + firstDayErrors); // Расчет общего количества ошибок на вторую дату int secondDayErrors = 0; for (FileData file : files) { if (file.date.equals(files.get(1).date)) { secondDayErrors += file.criticalErrors + file.nonCriticalErrors; } } System.out.println("Общее количество ошибок на вторую дату: " + secondDayErrors); // Сравнение количества ошибок и вычисление баллов int score = 500; if (secondDayErrors > firstDayErrors) { int difference = secondDayErrors - firstDayErrors; score -= difference * 5; } else if (secondDayErrors < firstDayErrors) { int difference = firstDayErrors - secondDayErrors; score += difference * 5; } System.out.println("Баллы: " + score); } // Класс, представляющий данные о файле static class FileData { int criticalErrors; int nonCriticalErrors; String date; public FileData(int criticalErrors, int nonCriticalErrors, String date) { this.criticalErrors = criticalErrors; this.nonCriticalErrors = nonCriticalErrors; this.date = date; } } } В этом примере для расчета ошибок используется список объектов FileData, каждый из которых содержит информацию о количестве критических и некритических ошибок в файле, а также дату проверки. Сначала вычисляется общее количество ошибок на первую дату, затем на вторую дату. Затем сравниваются эти значения и, в зависимости от результата, вычисляются баллы. Если количество ошибок на вторую дату больше, чем на первую, то из 500 вычитается 5 за каждую новую ошибку. Если количество ошибок на вторую дату меньше, чем на первую, то к 500 прибавляется 5 за каждую исправленную ошибку. Если количество ошибок не изменилось, то баллы остаются равными 700.

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

Решаем что алгоритм слишком простой, и усложняем его.

Исходный код метода на java, для расчета числа на основании следующей информации. Есть список файлов, и для каждого файла количество критических и некритических ошибок с датой проверки. Дата проверки может быть любой. При поиске количества ошибок в файле на дату, надо будет найти запись с максимальной датой меньше или равной заданной. Нужно посчитать общее кол-во ошибок на первую дату. Нужно посчитать общее кол-во ошибок на вторую дату. Сравнить полученные числа, и если количество ошибок выросло, то вычесть из 500 по 5 за каждую новую ошибку. Если кол-во ошибок уменьшилось, то 500 плюс 5 баллов за каждую исправленную ошибку.

import java.util.*; public class ErrorCounter { public static void main(String[] args) { // Пример данных для расчета ошибок List<FileData> files = new ArrayList<>(); files.add(new FileData(100, 20, "2022-03-01")); files.add(new FileData(50, 10, "2022-03-02")); files.add(new FileData(20, 5, "2022-03-05")); // Расчет общего количества ошибок на первую дату String firstDate = "2022-03-01"; int firstDayErrors = getTotalErrors(files, firstDate); System.out.println("Общее количество ошибок на первую дату: " + firstDayErrors); // Расчет общего количества ошибок на вторую дату String secondDate = "2022-03-02"; int secondDayErrors = getTotalErrors(files, secondDate); System.out.println("Общее количество ошибок на вторую дату: " + secondDayErrors); // Сравнение количества ошибок и вычисление баллов int score = 500; if (secondDayErrors > firstDayErrors) { int difference = secondDayErrors - firstDayErrors; score -= difference * 5; } else if (secondDayErrors < firstDayErrors) { int difference = firstDayErrors - secondDayErrors; score += difference * 5; } System.out.println("Баллы: " + score); } // Класс, представляющий данные о файле static class FileData { int criticalErrors; int nonCriticalErrors; String date; public FileData(int criticalErrors, int nonCriticalErrors, String date) { this.criticalErrors = criticalErrors; this.nonCriticalErrors = nonCriticalErrors; this.date = date; } } // Метод для нахождения общего количества ошибок на заданную дату static int getTotalErrors(List<FileData> files, String date) { int totalErrors = 0; Date searchDate; try { searchDate = new SimpleDateFormat("yyyy-MM-dd").parse(date); } catch (ParseException e) { e.printStackTrace(); return 0; } for (FileData file : files) { Date fileDate; try { fileDate = new SimpleDateFormat("yyyy-MM-dd").parse(file.date); } catch (ParseException e) { e.printStackTrace(); return 0; } if (fileDate.compareTo(searchDate) <= 0) { totalErrors += file.criticalErrors + file.nonCriticalErrors; } } return totalErrors; } } В этом примере также используется список объектов FileData, содержащий информацию о количестве ошибок в файле и дате проверки. Однако здесь для нахождения общего количества ошибок на заданную дату используется отдельный метод getTotalErrors(). Этот метод принимает список файлов и заданную дат

Видим, что код метода стал более структурированным, появился метод подсчета ошибок на дату getTotalErrors, но мы всё ещё оперируем одним числом кол-во ошибок без градации по приоритету.

Ещё немного усложняем алгоритм

Исходный код метода на java, для расчета числа на основании следующей информации. Есть список файлов, и для каждого файла количество критических и некритических ошибок с датой проверки. Дата проверки может быть любой. При поиске количества ошибок в файле на дату, надо будет найти запись с максимальной датой меньше или равной заданной. Нужно посчитать кол-во ошибок на первую дату. Нужно посчитать кол-во ошибок на вторую дату. Сравнить полученные числа, и если количество ошибок выросло, то вычесть из 500 по 5 за каждую новую некритическую ошибку, и 100 за каждую критичную. Если кол-во ошибок уменьшилось, то 500 плюс 5 баллов за любую исправленную ошибку.

public void calculateErrors(List<File> fileList, Date date1, Date date2) { int numErrors1 = 0; int numErrors2 = 0; for (File file : fileList) { int criticalErrors1 = 0; int nonCriticalErrors1 = 0; int criticalErrors2 = 0; int nonCriticalErrors2 = 0; Date lastCheckedDate = null; for (ErrorRecord record : file.getErrorRecords()) { if (record.getDate().compareTo(date1) <= 0) { lastCheckedDate = record.getDate(); criticalErrors1 = record.getCriticalErrors(); nonCriticalErrors1 = record.getNonCriticalErrors(); } if (record.getDate().compareTo(date2) <= 0) { criticalErrors2 = record.getCriticalErrors(); nonCriticalErrors2 = record.getNonCriticalErrors(); } } if (lastCheckedDate == null || lastCheckedDate.compareTo(date1) < 0) { // file wasn't checked before date1 continue; } if (criticalErrors2 > criticalErrors1 || nonCriticalErrors2 > nonCriticalErrors1) { int numNewCriticalErrors = criticalErrors2 - criticalErrors1; int numNewNonCriticalErrors = nonCriticalErrors2 - nonCriticalErrors1; int penalty = numNewCriticalErrors * 100 + numNewNonCriticalErrors * 5; int newScore = 500 - penalty; // do something with the new score } else if (nonCriticalErrors2 < nonCriticalErrors1) { int numFixedErrors = nonCriticalErrors1 - nonCriticalErrors2; int bonus = numFixedErrors * 5; int newScore = 500 + bonus; // do something with the new score } } } Этот метод принимает список файлов fileList, каждый из которых имеет свой список записей об ошибках. Даты date1 и date2 задают период, на который нужно рассчитать количество ошибок. Для каждого файла метод находит запись об ошибках с максимальной датой, меньшей или равной date1 и date2. Если такой записи нет, то файл пропускается. Если запись найдена, то для нее рассчитывается количество критических и некритических ошибок на даты date1 и date2. Затем метод сравнивает количество ошибок на этих двух датах. Если количество ошибок выросло, то метод вычитает из 700 сумму штрафов за новые ошибки. Штраф за каждую новую некритическую ошибку равен 5, а за каждую новую критическую ошибку равен 100. Если количество ошибок уменьшилось, то метод прибавляет к 700 сумму бонусов за исправленные ошибки. Бонус за каждую исправленную некритическую ошибку равен 5. Заметим, что этот метод не возвращает новый счет, а только расс

Реализация снова изменилась, почему то пропал метод getTotalErrors, да и в целом код не выглядит рабочим.

Что ж, попробуем по другому дать описание. Ну и снова немного усложним алгоритм.

Исходный код метода на java, для расчета числа на основании следующей информации. Есть список файлов, и для каждого файла количество критических и некритических ошибок с датой проверки. Дата проверки может быть любой. При поиске количества ошибок в файле на дату, надо будет найти запись с максимальной датой меньше или равной заданной. Нужно посчитать кол-во ошибок на первую дату. Нужно посчитать кол-во ошибок на вторую дату. Сравнить полученные числа. Начальная оценка 500. Если количество некритичных ошибок выросло, то вычесть из начальной оценки по 5 за каждую новую некритическую ошибку. Если кол-во ошибок некритичных уменьшилось, то 500 плюс 5 баллов за любую исправленную ошибку. Если количество критичных ошибок выросло или не изменилось, то нужно из оценки вычесть 100 за каждую критичную ошибку.

public int calculateScore(List<FileData> fileList, Date date1, Date date2) { int errorCount1 = 0; int errorCount2 = 0; int initialScore = 500; for (FileData fileData : fileList) { int criticalErrors1 = getCriticalErrors(fileData, date1); int criticalErrors2 = getCriticalErrors(fileData, date2); int nonCriticalErrors1 = getNonCriticalErrors(fileData, date1); int nonCriticalErrors2 = getNonCriticalErrors(fileData, date2); errorCount1 += criticalErrors1 + nonCriticalErrors1; errorCount2 += criticalErrors2 + nonCriticalErrors2; if (nonCriticalErrors2 > nonCriticalErrors1) { int newNonCriticalErrors = nonCriticalErrors2 - nonCriticalErrors1; int scoreReduction = newNonCriticalErrors * 5; initialScore -= scoreReduction; } else if (nonCriticalErrors2 < nonCriticalErrors1) { int correctedErrors = nonCriticalErrors1 - nonCriticalErrors2; int scoreIncrease = correctedErrors * 5; initialScore += scoreIncrease; } if (criticalErrors2 >= criticalErrors1) { int newCriticalErrors = criticalErrors2 - criticalErrors1; int scoreReduction = newCriticalErrors * 100; initialScore -= scoreReduction; } } return (errorCount2 > errorCount1) ? initialScore - 5 * (errorCount2 - errorCount1) : initialScore; } private int getCriticalErrors(FileData fileData, Date date) { return fileData.getCriticalErrors(date); } private int getNonCriticalErrors(FileData fileData, Date date) { return fileData.getNonCriticalErrors(date); } Этот метод принимает список объектов FileData, каждый из которых содержит информацию о количестве критических и некритических ошибок для конкретного файла и даты их проверки. Метод также принимает две даты - date1 и date2, на основании которых будут производиться расчеты. Сначала метод инициализирует переменные errorCount1, errorCount2 и initialScore значением 0 и 500 соответственно. Затем производится итерация по списку fileList, для каждого элемента которого вычисляется количество критических и некритических ошибок на датах date1 и date2. Результаты суммируются с переменными errorCount1 и errorCount2. Далее производится проверка на изменение количества некритических ошибок. Если их количество на date2 больше, чем на date1, то вычисляется разница между ними, и из initialScore вычитается соответствующее количество очков (5 очков за каждую новую ошибку). Если количество некритических ошибок на date2 меньше, чем на date1, то

Причем результаты между запусками могут различаться:

import java.util.*; public class ErrorCalculator { private int initialScore = 500; private Map<String, List<ErrorRecord>> errorRecords = new HashMap<>(); // Метод для добавления записи об ошибке в соответствующий список для файла public void addErrorRecord(String fileName, ErrorRecord errorRecord) { if (!errorRecords.containsKey(fileName)) { errorRecords.put(fileName, new ArrayList<>()); } errorRecords.get(fileName).add(errorRecord); } // Метод для расчета количества ошибок на определенную дату public int calculateErrorsOnDate(String fileName, Date date) { int criticalErrors = 0; int nonCriticalErrors = 0; List<ErrorRecord> records = errorRecords.get(fileName); if (records != null && !records.isEmpty()) { // Сортируем список записей по дате проверки в порядке убывания Collections.sort(records, Collections.reverseOrder()); for (ErrorRecord record : records) { if (record.getDate().compareTo(date) <= 0) { criticalErrors = record.getCriticalErrors(); nonCriticalErrors = record.getNonCriticalErrors(); break; } } } int score = initialScore; if (nonCriticalErrors > 0) { // Вычитаем по 5 баллов за каждую новую некритическую ошибку score -= (nonCriticalErrors * 5); } else { // Добавляем по 5 баллов за каждую исправленную некритическую ошибку score += (Math.abs(nonCriticalErrors) * 5); } if (criticalErrors > 0) { // Вычитаем по 100 баллов за каждую критическую ошибку score -= (criticalErrors * 100); } return score; } } // Класс для хранения записи об ошибке class ErrorRecord implements Comparable<ErrorRecord> { private Date date; private int criticalErrors; private int nonCriticalErrors; public ErrorRecord(Date date, int criticalErrors, int nonCriticalErrors) { this.date = date; this.criticalErrors = criticalErrors; this.nonCriticalErrors = nonCriticalErrors; } public Date getDate() { return date; } public int getCriticalErrors() { return criticalErrors; } public int getNonCriticalErrors() { return nonCriticalErrors; } // Реализуем метод для сравнения записей по дате проверки @Override public int compareTo(ErrorRecord o) { return this.date.compareTo(o.getDate()); } } Пример использования: java Copy code public static void main(String[] args) { ErrorCalculator calculator = new ErrorCalculator(); // Добавляем записи об ошибках для файла "file1" calculator.addErrorRecord("file1", new ErrorRecord(new Date(2023, 3, 1), 2, 3)); calculator.addErrorRecord("file1", new ErrorRecord(new Date(2023, 3, 3), 3, 2)); calculator.addErrorRecord("file1", new ErrorRecord(new Date(2023, 3, 5), 1, 4)); //

А как же юнит тесты?

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

Даём задачу:

«Также нужно сгенерить юнит тесты для всех методов выше.»

import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.Date; class ErrorCalculatorTest { @Test void addErrorRecord_shouldAddRecordToList() { ErrorCalculator calculator = new ErrorCalculator(); ErrorRecord record = new ErrorRecord(new Date(), 1, 2); calculator.addErrorRecord("file1", record); Assertions.assertEquals(1, calculator.getErrorRecords().get("file1").size()); Assertions.assertEquals(record, calculator.getErrorRecords().get("file1").get(0)); } @Test void calculateErrorsOnDate_shouldCalculateCorrectScore() { ErrorCalculator calculator = new ErrorCalculator(); calculator.addErrorRecord("file1", new ErrorRecord(new Date(2023, 3, 1), 2, 3)); calculator.addErrorRecord("file1", new ErrorRecord(new Date(2023, 3, 3), 3, 2)); calculator.addErrorRecord("file1", new ErrorRecord(new Date(2023, 3, 5), 1, 4)); // Проверяем результат для даты 2023-03-02 Assertions.assertEquals(510, calculator.calculateErrorsOnDate("file1", new Date(2023, 2, 2))); // Проверяем результат для даты 2023-03-04 Assertions.assertEquals(495, calculator.calculateErrorsOnDate("file1", new Date(2023, 2, 4))); // Проверяем результат для даты 2023-03-06 Assertions.assertEquals(400, calculator.calculateErrorsOnDate("file1", new Date(2023, 2, 6))); // Добавляем новую некритическую ошибку и проверяем результат для даты 2023-03-06 calculator.addErrorRecord("file1", new ErrorRecord(new Date(2023, 3, 6), 0, 1)); Assertions.assertEquals(395, calculator.calculateErrorsOnDate("file1", new Date(2023, 2, 6))); // Исправляем некритическую ошибку и проверяем результат для даты 2023-03-06 calculator.addErrorRecord("file1", new ErrorRecord(new Date(2023, 3, 6), 0, -1)); Assertions.assertEquals(400, calculator.calculateErrorsOnDate("file1", new Date(2023, 2, 6))); // Добавляем новую критическую ошибку и проверяем результат для даты 2023-03-06 calculator.addErrorRecord("file1", new ErrorRecord(new Date(2023, 3, 6), 1, 0)); Assertions.assertEquals(300, calculator.calculateErrorsOnDate("file1", new Date(2023, 2, 6))); } } Обратите внимание, что в юнит-тестах используются методы, доступные через геттеры, такие как getErrorRecords(), для проверки внутреннего состояния объекта. Это может быть полезно при тестировании сложных алгоритмов. Однако, в реальном коде, доступ к внутренним

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

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

Продолжаем наблюдение!

22
2 комментария

ChatGPT, также известный как GPT-3, является искусственным интеллектом, способным генерировать текст на основе заданных параметров. Хотя он не предназначен для написания кода, вы можете использовать его для создания текстовых команд и инструкций для программирования. Например, вы можете использовать ChatGPT для создания списка шагов для выполнения определенной задачи в определенном языке программирования, или для получения советов по устранению ошибок в вашем коде.

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

1

Заменить разработчика ChatGPT пока не может. Он готов и может решать несложные задачи, без знания контекста и не опираясь на существующий кода. Поэтому его применение в реальных проектах довольно ограниченно. К тому же при не слишком длительном общении выясняется что он может заблуждаться, и давать синтаксически неверный код. С использованием несуществующих контрактов методов и классов.
Но даже в текущем виде, выглядит очень многообещающе.
Также есть альтернатива, в виде COPILOT, ИИ предназначенный для помощи разработчику, который анализирует контекст проекта, и даёт более осмысленные подсказки.
Но не смотря на всё это, при прямом сравнении, COPILOT меня впечатлил меньше.