From 7175d9b7021121cc7f103bd5ca27549c092488c9 Mon Sep 17 00:00:00 2001 From: Nikolay Gagarinov Date: Thu, 25 Jun 2026 20:04:21 +0500 Subject: [PATCH] =?UTF-8?q?feat(90-loops):=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=B8=D1=82=D1=8C=20=D1=83=D1=80=D0=BE=D0=BA=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=20=D0=BE=D1=82=D0=BB=D0=B0=D0=B4=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Перенос DEFER-темы из Python (50-loops/90-debug), адаптация под Java. Урок 600-debug (RU-only): стек вызовов (stack trace), типы ошибок (синтаксические/времени выполнения/логические), отладочная печать через System.out.println. Упражнение: RLE-сжатие строки (compress) на for/charAt/StringBuilder. Co-Authored-By: Claude Opus 4.8 (1M context) --- modules/90-loops/600-debug/App.java | 31 ++++++++ modules/90-loops/600-debug/Makefile | 2 + modules/90-loops/600-debug/Test.java | 12 ++++ modules/90-loops/600-debug/ru/EXERCISE.md | 17 +++++ modules/90-loops/600-debug/ru/README.md | 86 +++++++++++++++++++++++ modules/90-loops/600-debug/ru/data.yml | 13 ++++ 6 files changed, 161 insertions(+) create mode 100644 modules/90-loops/600-debug/App.java create mode 100644 modules/90-loops/600-debug/Makefile create mode 100644 modules/90-loops/600-debug/Test.java create mode 100644 modules/90-loops/600-debug/ru/EXERCISE.md create mode 100644 modules/90-loops/600-debug/ru/README.md create mode 100644 modules/90-loops/600-debug/ru/data.yml diff --git a/modules/90-loops/600-debug/App.java b/modules/90-loops/600-debug/App.java new file mode 100644 index 00000000..b640d851 --- /dev/null +++ b/modules/90-loops/600-debug/App.java @@ -0,0 +1,31 @@ +public class App { + public static String compress(String string) { + // BEGIN + if (string.isEmpty()) { + return ""; + } + + StringBuilder result = new StringBuilder(); + int count = 1; + + for (int i = 1; i < string.length(); i++) { + if (string.charAt(i) == string.charAt(i - 1)) { + count++; + } else { + result.append(string.charAt(i - 1)); + if (count > 1) { + result.append(count); + } + count = 1; + } + } + + result.append(string.charAt(string.length() - 1)); + if (count > 1) { + result.append(count); + } + + return result.toString(); + // END + } +} diff --git a/modules/90-loops/600-debug/Makefile b/modules/90-loops/600-debug/Makefile new file mode 100644 index 00000000..d0d0a48c --- /dev/null +++ b/modules/90-loops/600-debug/Makefile @@ -0,0 +1,2 @@ +test: + @ test.sh diff --git a/modules/90-loops/600-debug/Test.java b/modules/90-loops/600-debug/Test.java new file mode 100644 index 00000000..9cb66584 --- /dev/null +++ b/modules/90-loops/600-debug/Test.java @@ -0,0 +1,12 @@ +import static org.assertj.core.api.Assertions.assertThat; + +class Test { + public static void main(String[] args) { + assertThat(App.compress("aaabcccc")).isEqualTo("a3bc4"); + assertThat(App.compress("abcd")).isEqualTo("abcd"); + assertThat(App.compress("aabbaa")).isEqualTo("a2b2a2"); + assertThat(App.compress("a")).isEqualTo("a"); + assertThat(App.compress("")).isEqualTo(""); + assertThat(App.compress("zzz")).isEqualTo("z3"); + } +} diff --git a/modules/90-loops/600-debug/ru/EXERCISE.md b/modules/90-loops/600-debug/ru/EXERCISE.md new file mode 100644 index 00000000..78d2ad14 --- /dev/null +++ b/modules/90-loops/600-debug/ru/EXERCISE.md @@ -0,0 +1,17 @@ +Реализуйте метод `compress()`, который сжимает строку методом RLE (Run-Length Encoding). + +Алгоритм такой: если символ повторяется несколько раз подряд, он заменяется на сам символ и количество повторений. Одиночные символы записываются без цифры. + +```java +App.compress("aaabcccc"); // => "a3bc4" +App.compress("abcd"); // => "abcd" +App.compress("aabbaa"); // => "a2b2a2" +App.compress(""); // => "" +``` + +Подсказки: + +- идите по строке и считайте, сколько одинаковых символов идет подряд; +- как только символ меняется, дописывайте предыдущий символ и его счетчик (если он больше единицы), а счетчик сбрасывайте; +- не забудьте обработать последнюю группу символов после завершения цикла; +- если что-то идет не так, добавьте отладочную печать значений `i`, текущего символа и счетчика — это поможет увидеть, где ломается логика. diff --git a/modules/90-loops/600-debug/ru/README.md b/modules/90-loops/600-debug/ru/README.md new file mode 100644 index 00000000..6fec0c84 --- /dev/null +++ b/modules/90-loops/600-debug/ru/README.md @@ -0,0 +1,86 @@ +Даже у самых опытных разработчиков код редко работает идеально с первого раза. Чем опытнее разработчик, тем увереннее он **отлаживает** код, то есть анализирует ошибки и устраняет их. + +Навык отладки сам по себе не появится. Его нужно развивать, причем как можно раньше. По ходу обучения вы будете выполнять задания и практиковаться, и со временем анализ ошибок войдет в привычку. + +## Как найти ошибку в коде + +Отладка методом тыка занимает много времени. Гораздо продуктивнее сначала понять, что именно пошло не так, а потом устранять причину. + +Если программа упала с исключением, первым делом изучите **стек вызовов** (stack trace). Он содержит цепочку вызовов методов от места ошибки и до старта программы. Каждая запись указывает на класс, метод, файл и строчку. По стеку видно, какие методы выполнились, а где возникла проблема: + +```bash +Exception in thread "main" java.lang.ArithmeticException: / by zero + at App.divide(App.java:6) + at App.main(App.java:2) +``` + +В первой строке — тип исключения и сообщение об ошибке: `ArithmeticException` с пояснением `/ by zero` (деление на ноль). Дальше идет сам стек: ошибка возникла в методе `divide` на 6-й строке, а вызвали его из `main` на 2-й строке. По названиям классов также можно понять, в вашем ли коде проблема или в подключенной библиотеке. + +**Если хотите попросить совет у опытного разработчика, первым делом покажите сообщение об ошибке и стек вызовов.** Фрагмент кода без контекста говорит мало, а текст ошибки сразу направляет к причине. + +## Типы ошибок + +Самые понятные ошибки — **синтаксические**. Они возникают, когда код оформлен неверно: пропущена скобка или точка с запятой, не совпали кавычки. Такой код не компилируется, и компилятор прямо указывает на место: + +```bash +App.java:2: error: ';' expected + System.out.println("Hello") + ^ +``` + +Труднее исправлять **ошибки времени выполнения**. Код компилируется, но при запуске падает с исключением: деление на ноль, обращение к `null`, выход за границы строки или массива. Они проявляются уже во время работы программы, и причина не всегда там, где упало. + +Сложнее всего бороться с **логическими ошибками**. Программа отрабатывает без исключений, но выдает неверный результат. Сообщения об ошибке нет — только неожиданный вывод. Например, метод должен считать сумму, но считает разность: + +```java +// Метод должен считать сумму чисел, но считает разность +public static int sum(int a, int b) { + return a - b; +} +``` + +## Способы отладки + +В основе любого метода отладки лежит наблюдение за переменными в процессе выполнения. Посмотрим на метод, который считает сумму чисел от `start` до `finish`. При `start = 3` и `finish = 5` он должен вычислить `3 + 4 + 5`: + +```java +public static int sumOfSeries(int start, int finish) { + int result = 0; + int n = start; + while (n < finish) { + result = result + n; + n = n + 1; + } + return result; +} +``` + +Ключевые переменные здесь — `n` и `result`. Чтобы найти ошибку, нужно посмотреть, какие значения они принимают на каждой итерации. Для этого существуют **визуальные отладчики**, встроенные в редакторы кода: они позволяют выполнять программу по шагам, наблюдая за переменными в реальном времени. + +На Хекслете вместо отладчика используется **отладочная печать**. Принцип тот же, только значения переменных выводятся обычным `System.out.println()`: + +```java +public static int sumOfSeries(int start, int finish) { + int result = 0; + int n = start; + while (n < finish) { + System.out.println("new iteration !!!!"); + System.out.println(n); + result = result + n; + n = n + 1; + System.out.println(result); + } + return result; +} + +// new iteration !!!! +// 3 +// 3 +// new iteration !!!! +// 4 +// 7 +``` + +Вывод показывает, что итераций на одну меньше, чем нужно: пятерка (`finish`) в сложение не попала. В условии стоит `n < finish` вместо `n <= finish`. Нужно заменить знак `<` на `<=`. + +Начинающие разработчики часто расстраиваются из-за ошибок и считают себя невнимательными. Ошибки есть у всех — и у джунов, и у сеньоров. Разница лишь в том, насколько уверенно ты их находишь. diff --git a/modules/90-loops/600-debug/ru/data.yml b/modules/90-loops/600-debug/ru/data.yml new file mode 100644 index 00000000..b44b8415 --- /dev/null +++ b/modules/90-loops/600-debug/ru/data.yml @@ -0,0 +1,13 @@ +--- +name: Отладка +tips: + - > + [Как просить помощь с + кодом](https://guides.hexlet.io/ru/how-to-ask-questions/) +definitions: + - name: Отладка + description: процесс поиска и устранения ошибок в программе. + - name: Стек вызовов (stack trace) + description: >- + цепочка вызовов методов, которую программа печатает при возникновении + исключения; помогает найти место и причину ошибки.