Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions modules/90-loops/600-debug/App.java
Original file line number Diff line number Diff line change
@@ -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
}
}
2 changes: 2 additions & 0 deletions modules/90-loops/600-debug/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
test:
@ test.sh
12 changes: 12 additions & 0 deletions modules/90-loops/600-debug/Test.java
Original file line number Diff line number Diff line change
@@ -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");
}
}
17 changes: 17 additions & 0 deletions modules/90-loops/600-debug/ru/EXERCISE.md
Original file line number Diff line number Diff line change
@@ -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`, текущего символа и счетчика — это поможет увидеть, где ломается логика.
86 changes: 86 additions & 0 deletions modules/90-loops/600-debug/ru/README.md
Original file line number Diff line number Diff line change
@@ -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-й строке. По названиям классов также можно понять, в вашем ли коде проблема или в подключенной библиотеке.

**Если хотите попросить совет у опытного разработчика, первым делом покажите сообщение об ошибке и стек вызовов.** Фрагмент кода без контекста говорит мало, а текст ошибки сразу направляет к причине.

Check notice on line 19 in modules/90-loops/600-debug/ru/README.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] modules/90-loops/600-debug/ru/README.md#L19

Articles like ‘a’ are rarely followed by punctuation. A word may be missing after ‘a’, or the punctuation mark may not be necessary. (THE_PUNCT[1]) Suggestions: `a` Rule: https://community.languagetool.org/rule/show/THE_PUNCT?lang=en-US&subId=1 Category: GRAMMAR
Raw output
modules/90-loops/600-debug/ru/README.md:19:258: Articles like ‘a’ are rarely followed by punctuation. A word may be missing after ‘a’, or the punctuation mark may not be necessary. (THE_PUNCT[1])
 Suggestions: `a`
 Rule: https://community.languagetool.org/rule/show/THE_PUNCT?lang=en-US&subId=1
 Category: GRAMMAR

## Типы ошибок

Самые понятные ошибки — **синтаксические**. Они возникают, когда код оформлен неверно: пропущена скобка или точка с запятой, не совпали кавычки. Такой код не компилируется, и компилятор прямо указывает на место:

```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`. Нужно заменить знак `<` на `<=`.

Начинающие разработчики часто расстраиваются из-за ошибок и считают себя невнимательными. Ошибки есть у всех — и у джунов, и у сеньоров. Разница лишь в том, насколько уверенно ты их находишь.
13 changes: 13 additions & 0 deletions modules/90-loops/600-debug/ru/data.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
name: Отладка
tips:
- >
[Как просить помощь с
кодом](https://guides.hexlet.io/ru/how-to-ask-questions/)
definitions:
- name: Отладка
description: процесс поиска и устранения ошибок в программе.
- name: Стек вызовов (stack trace)
description: >-
цепочка вызовов методов, которую программа печатает при возникновении
исключения; помогает найти место и причину ошибки.
Loading