From f8178bf7d154dc3392b58704a712d5c6b5f3cf3c Mon Sep 17 00:00:00 2001 From: Nikolay Gagarinov Date: Mon, 1 Jun 2026 18:32:22 +0500 Subject: [PATCH] feat(50-loops): add debug lesson adapted from Python version New 90-debug lesson covering debugging: stack traces, error types (syntax/programming/logic), and print debugging via console.log. Exercise: compress() RLE string compression. Includes ru/en/es localizations. Co-Authored-By: Claude Opus 4.8 (1M context) --- modules/50-loops/90-debug/Makefile | 2 + modules/50-loops/90-debug/description.es.yml | 140 +++++++++++++++++++ modules/50-loops/90-debug/en/EXERCISE.md | 18 +++ modules/50-loops/90-debug/en/README.md | 110 +++++++++++++++ modules/50-loops/90-debug/en/data.yml | 4 + modules/50-loops/90-debug/es/EXERCISE.md | 18 +++ modules/50-loops/90-debug/es/README.md | 110 +++++++++++++++ modules/50-loops/90-debug/es/data.yml | 4 + modules/50-loops/90-debug/index.js | 31 ++++ modules/50-loops/90-debug/ru/EXERCISE.md | 18 +++ modules/50-loops/90-debug/ru/README.md | 110 +++++++++++++++ modules/50-loops/90-debug/ru/data.yml | 4 + modules/50-loops/90-debug/test.js | 11 ++ 13 files changed, 580 insertions(+) create mode 100644 modules/50-loops/90-debug/Makefile create mode 100644 modules/50-loops/90-debug/description.es.yml create mode 100644 modules/50-loops/90-debug/en/EXERCISE.md create mode 100644 modules/50-loops/90-debug/en/README.md create mode 100644 modules/50-loops/90-debug/en/data.yml create mode 100644 modules/50-loops/90-debug/es/EXERCISE.md create mode 100644 modules/50-loops/90-debug/es/README.md create mode 100644 modules/50-loops/90-debug/es/data.yml create mode 100644 modules/50-loops/90-debug/index.js create mode 100644 modules/50-loops/90-debug/ru/EXERCISE.md create mode 100644 modules/50-loops/90-debug/ru/README.md create mode 100644 modules/50-loops/90-debug/ru/data.yml create mode 100644 modules/50-loops/90-debug/test.js diff --git a/modules/50-loops/90-debug/Makefile b/modules/50-loops/90-debug/Makefile new file mode 100644 index 00000000..d0d0a48c --- /dev/null +++ b/modules/50-loops/90-debug/Makefile @@ -0,0 +1,2 @@ +test: + @ test.sh diff --git a/modules/50-loops/90-debug/description.es.yml b/modules/50-loops/90-debug/description.es.yml new file mode 100644 index 00000000..0ca4c480 --- /dev/null +++ b/modules/50-loops/90-debug/description.es.yml @@ -0,0 +1,140 @@ +--- + +name: Depuración +theory: | + + Incluso a los desarrolladores más experimentados rara vez les funciona el código a la perfección al primer intento. Cuanto más experimentado es el desarrollador, con más confianza lo **depura**, es decir, analiza los errores y los corrige. + + La habilidad de depuración no aparece por sí sola. Hay que desarrollarla, y cuanto antes mejor. A lo largo del aprendizaje realizarás tareas y practicarás, y con el tiempo el análisis de errores se convertirá en un hábito. + + ## Cómo encontrar un error en el código + + Depurar a base de prueba y error lleva mucho tiempo. Es mucho más productivo entender primero qué salió mal y luego corregir la causa. Todo trabajo con errores comienza con la comprensión. + + Lo primero es estudiar la **pila de llamadas** (stack trace). Contiene una lista de todas las llamadas a funciones desde el punto del error hasta el inicio del programa. Muestra qué funciones se ejecutaron y dónde surgió el problema. Cada entrada indica un archivo, una línea y la función que se estaba ejecutando. + + Imagina que escribiste código en un archivo `users.js` y llamaste a la función `main()`, que internamente llama a una `create()` inexistente: + + ```javascript + const main = () => { + create(); + }; + + main(); + ``` + + Al ejecutarlo verás una salida así: + + ```bash + ReferenceError: create is not defined + at main (users.js:2:3) + at users.js:5:1 + ``` + + La primera línea es el **mensaje de error**. Aquí `ReferenceError: create is not defined` dice que el nombre `create` no está definido. Este error suele significar un error tipográfico en el nombre. + + Debajo está la propia pila de llamadas. Se ve que el programa llegó a `main()` (línea 5), dentro de ella llamó a `create()` (línea 2) y aquí se topó con el error. Cada entrada `at ...` indica un archivo y una línea. + + ## Tipos de errores + + Los errores más comprensibles son los **errores de sintaxis**. Surgen cuando el código está mal formateado, por ejemplo, por un paréntesis sin cerrar o comillas que no coinciden. La salida siempre contiene `SyntaxError`: + + ```bash + console.log("Hello" + 'world); + ^^^^^^^^ + SyntaxError: Invalid or unexpected token + ``` + + Los más difíciles de corregir son los **errores de programación**. Entre ellos están llamar a una función inexistente, usar una variable no declarada o pasar argumentos de tipo incorrecto. Suelen aparecer en un lugar distinto al de la causa real, lo que dificulta el diagnóstico. + + Los más difíciles de combatir son los **errores lógicos**. El programa funciona sin excepciones, pero produce un resultado incorrecto con ciertos datos de entrada. No hay ningún mensaje de error, solo una salida inesperada. Por ejemplo, una función que debería calcular la suma pero calcula la diferencia: + + ```javascript + // La función debería calcular la suma de los números, pero calcula la diferencia: + const sum = (a, b) => a - b; + + // Con esta llamada el error no es evidente, porque + // tanto la suma como la resta dan el mismo resultado + sum(4, 0); // 4 + ``` + + ## Métodos de depuración + + En la base de cualquier método de depuración está la observación de las variables durante la ejecución. Veamos una función concreta. + + A continuación, una función que calcula la suma de los números desde `start` hasta `finish`. Con `start=3` y `finish=5` debería calcular `3 + 4 + 5`. + + ```javascript + const sumOfSeries = (start, finish) => { + let result = 0; + let n = start; + while (n < finish) { + result = result + n; + n = n + 1; + } + + return result; + }; + ``` + + Las variables clave de esta función son `n` y `result`. Para encontrar el error, hay que ver qué valores toman en cada iteración. + + Para ello existen los **depuradores visuales**. Se integran en los editores de código populares y permiten ejecutar el programa paso a paso, observando las variables en tiempo real. Puedes encontrar uno buscando «JavaScript debuggers» en Google. + + En el entorno de Hexlet, en lugar de un depurador se usa la **impresión de depuración**. El principio es el mismo, solo que los valores de las variables se imprimen con el habitual `console.log`. Lo que se imprime aparece en la pestaña `OUTPUT`. + + ```javascript + const sumOfSeries = (start, finish) => { + let result = 0; + let n = start; + while (n < finish) { + console.log('new iteration !!!!'); + console.log(n); + result = result + n; + n = n + 1; + console.log(result); + } + + return result; + }; + + sumOfSeries(3, 5); + + // new iteration !!!! + // 3 + // 3 + // new iteration !!!! + // 4 + // 7 + ``` + + La salida muestra que hay una iteración menos de las necesarias. El cinco (`finish`) no entró en la suma. La condición usa `n < finish` en lugar de `n <= finish`. Hay que reemplazar el signo `<` por `<=`. + + Los principiantes a menudo se molestan por los errores y se consideran descuidados. Todos cometen errores, tanto los junior como los senior. La diferencia está en con cuánta seguridad los encuentras. + + Los principiantes piensan que un buen desarrollador mira el código y entiende de inmediato qué está mal. Esto rara vez funciona en la práctica. Un fragmento de código sin contexto dice muy poco. **Si quieres pedir consejo a un desarrollador experimentado, lo primero es mostrarle el mensaje de error.** + +instructions: | + + Implementa la función `compress(str)` que comprime una cadena usando RLE (Run-Length Encoding). + + Algoritmo: si un carácter se repite consecutivamente, se reemplaza por el carácter y la cantidad. Los caracteres individuales se escriben sin número. + + ```javascript + compress('aaabcccc'); // => 'a3bc4' + compress('abcd'); // => 'abcd' + compress('aabbaa'); // => 'a2b2a2' + compress(''); // => '' + ``` + + Este algoritmo se utiliza en formatos reales de compresión de datos, por ejemplo, en antiguos protocolos de fax y archivos BMP. + + ### Algoritmo + + 1. Recorrer la cadena contando la cantidad de caracteres idénticos consecutivos. + 2. Cuando el carácter cambie, escribir el carácter anterior y el contador (si es mayor que 1). + 3. No olvidar procesar el último grupo después de que termine el bucle. + +tips: [] + +definitions: [] diff --git a/modules/50-loops/90-debug/en/EXERCISE.md b/modules/50-loops/90-debug/en/EXERCISE.md new file mode 100644 index 00000000..592f0189 --- /dev/null +++ b/modules/50-loops/90-debug/en/EXERCISE.md @@ -0,0 +1,18 @@ +Implement the `compress(str)` function that compresses a string using RLE (Run-Length Encoding). + +Algorithm: if a character repeats consecutively, replace it with the character and the count. Single characters are written without a number. + +```javascript +compress('aaabcccc'); // => 'a3bc4' +compress('abcd'); // => 'abcd' +compress('aabbaa'); // => 'a2b2a2' +compress(''); // => '' +``` + +This algorithm is used in real compression formats — for example, in old fax protocols and BMP files. + +### Algorithm + +1. Walk through the string, counting consecutive identical characters. +2. When the character changes — write the previous character and counter (if greater than 1). +3. Don't forget to handle the last group after the loop ends. diff --git a/modules/50-loops/90-debug/en/README.md b/modules/50-loops/90-debug/en/README.md new file mode 100644 index 00000000..b9b617a6 --- /dev/null +++ b/modules/50-loops/90-debug/en/README.md @@ -0,0 +1,110 @@ +Even experienced developers rarely get their code working perfectly on the first try. The more experienced the developer, the more confidently they **debug** it — that is, analyze errors and fix them. + +Debugging skills don't appear on their own. They must be developed, and the sooner you start, the better. During your studies you'll complete assignments and practice, and over time analyzing errors will become a habit. + +## How to find a bug in code + +Debugging by trial and error takes a lot of time. It's far more productive to first understand what exactly went wrong, and only then fix the cause. Any work with errors starts with understanding. + +First, study the **stack trace**. It contains a list of all function calls from the point of the error back to the start of the program. It shows which functions ran and where the problem occurred. Each entry points to a file, a line, and the function being executed. + +Imagine you wrote code in a file `users.js` and called the function `main()`, which internally calls a non-existent `create()`: + +```javascript +const main = () => { + create(); +}; + +main(); +``` + +When you run it, you'll see output like this: + +```bash +ReferenceError: create is not defined + at main (users.js:2:3) + at users.js:5:1 +``` + +The first line is the **error message**. Here `ReferenceError: create is not defined` says that the name `create` is not defined. This error most often means a typo in the name. + +Below it is the stack trace itself. You can see that the program reached `main()` (line 5), called `create()` inside it (line 2), and ran into the error here. Each `at ...` entry points to a file and a line. + +## Types of errors + +The simplest errors are **syntax errors**. They occur when the code is formatted incorrectly, for example because of an unclosed bracket or mismatched quotes. The output always contains `SyntaxError`: + +```bash +console.log("Hello" + 'world); + ^^^^^^^^ +SyntaxError: Invalid or unexpected token +``` + +The hardest to fix are **programming errors**. These include calling a non-existent function, using an undeclared variable, or passing arguments of the wrong type. They usually occur far from where the real cause is, which makes diagnosis harder. + +The most difficult are **logic errors**. The program runs without exceptions but produces an incorrect result for certain inputs. There's no error message, only unexpected output. For example, a function that should calculate the sum but calculates the difference: + +```javascript +// The function should calculate the sum of numbers, but calculates the difference: +const sum = (a, b) => a - b; + +// With this call the error is not obvious, because +// both addition and subtraction give the same result +sum(4, 0); // 4 +``` + +## Debugging methods + +At the heart of every debugging method lies observing variables as the code runs. Let's look at a specific function. + +Below is a function that calculates the sum of numbers from `start` to `finish`. With `start=3` and `finish=5` it should calculate `3 + 4 + 5`. + +```javascript +const sumOfSeries = (start, finish) => { + let result = 0; + let n = start; + while (n < finish) { + result = result + n; + n = n + 1; + } + + return result; +}; +``` + +The key variables in this function are `n` and `result`. To find the bug, we need to see what values they take on each iteration. + +For this there are **visual debuggers**. They are built into popular code editors and let you run the program step by step, watching variables in real time. You can find one by searching Google for "JavaScript debuggers". + +In the Hexlet environment, instead of a debugger we use **print debugging**. The principle is the same, only variable values are printed with the usual `console.log`. What is printed appears in the `OUTPUT` tab. + +```javascript +const sumOfSeries = (start, finish) => { + let result = 0; + let n = start; + while (n < finish) { + console.log('new iteration !!!!'); + console.log(n); + result = result + n; + n = n + 1; + console.log(result); + } + + return result; +}; + +sumOfSeries(3, 5); + +// new iteration !!!! +// 3 +// 3 +// new iteration !!!! +// 4 +// 7 +``` + +The output shows there is one fewer iteration than needed. The five (`finish`) wasn't included in the addition. The condition uses `n < finish` instead of `n <= finish`. We need to replace the `<` sign with `<=`. + +Beginners often get upset about errors and think they are inattentive. Everyone makes errors — both juniors and seniors. The difference is how confidently you find them. + +Beginners think that a good developer looks at code and immediately understands what's wrong. This rarely works in practice. A snippet of code without context says very little. **If you want to ask an experienced developer for advice, first show them the error message.** diff --git a/modules/50-loops/90-debug/en/data.yml b/modules/50-loops/90-debug/en/data.yml new file mode 100644 index 00000000..5d0df0fd --- /dev/null +++ b/modules/50-loops/90-debug/en/data.yml @@ -0,0 +1,4 @@ +--- +name: Debugging +tips: [] +definitions: [] diff --git a/modules/50-loops/90-debug/es/EXERCISE.md b/modules/50-loops/90-debug/es/EXERCISE.md new file mode 100644 index 00000000..57e66a89 --- /dev/null +++ b/modules/50-loops/90-debug/es/EXERCISE.md @@ -0,0 +1,18 @@ +Implementa la función `compress(str)` que comprime una cadena usando RLE (Run-Length Encoding). + +Algoritmo: si un carácter se repite consecutivamente, se reemplaza por el carácter y la cantidad. Los caracteres individuales se escriben sin número. + +```javascript +compress('aaabcccc'); // => 'a3bc4' +compress('abcd'); // => 'abcd' +compress('aabbaa'); // => 'a2b2a2' +compress(''); // => '' +``` + +Este algoritmo se utiliza en formatos reales de compresión de datos, por ejemplo, en antiguos protocolos de fax y archivos BMP. + +### Algoritmo + +1. Recorrer la cadena contando la cantidad de caracteres idénticos consecutivos. +2. Cuando el carácter cambie, escribir el carácter anterior y el contador (si es mayor que 1). +3. No olvidar procesar el último grupo después de que termine el bucle. diff --git a/modules/50-loops/90-debug/es/README.md b/modules/50-loops/90-debug/es/README.md new file mode 100644 index 00000000..7ecadd79 --- /dev/null +++ b/modules/50-loops/90-debug/es/README.md @@ -0,0 +1,110 @@ +Incluso a los desarrolladores más experimentados rara vez les funciona el código a la perfección al primer intento. Cuanto más experimentado es el desarrollador, con más confianza lo **depura**, es decir, analiza los errores y los corrige. + +La habilidad de depuración no aparece por sí sola. Hay que desarrollarla, y cuanto antes mejor. A lo largo del aprendizaje realizarás tareas y practicarás, y con el tiempo el análisis de errores se convertirá en un hábito. + +## Cómo encontrar un error en el código + +Depurar a base de prueba y error lleva mucho tiempo. Es mucho más productivo entender primero qué salió mal y luego corregir la causa. Todo trabajo con errores comienza con la comprensión. + +Lo primero es estudiar la **pila de llamadas** (stack trace). Contiene una lista de todas las llamadas a funciones desde el punto del error hasta el inicio del programa. Muestra qué funciones se ejecutaron y dónde surgió el problema. Cada entrada indica un archivo, una línea y la función que se estaba ejecutando. + +Imagina que escribiste código en un archivo `users.js` y llamaste a la función `main()`, que internamente llama a una `create()` inexistente: + +```javascript +const main = () => { + create(); +}; + +main(); +``` + +Al ejecutarlo verás una salida así: + +```bash +ReferenceError: create is not defined + at main (users.js:2:3) + at users.js:5:1 +``` + +La primera línea es el **mensaje de error**. Aquí `ReferenceError: create is not defined` dice que el nombre `create` no está definido. Este error suele significar un error tipográfico en el nombre. + +Debajo está la propia pila de llamadas. Se ve que el programa llegó a `main()` (línea 5), dentro de ella llamó a `create()` (línea 2) y aquí se topó con el error. Cada entrada `at ...` indica un archivo y una línea. + +## Tipos de errores + +Los errores más comprensibles son los **errores de sintaxis**. Surgen cuando el código está mal formateado, por ejemplo, por un paréntesis sin cerrar o comillas que no coinciden. La salida siempre contiene `SyntaxError`: + +```bash +console.log("Hello" + 'world); + ^^^^^^^^ +SyntaxError: Invalid or unexpected token +``` + +Los más difíciles de corregir son los **errores de programación**. Entre ellos están llamar a una función inexistente, usar una variable no declarada o pasar argumentos de tipo incorrecto. Suelen aparecer en un lugar distinto al de la causa real, lo que dificulta el diagnóstico. + +Los más difíciles de combatir son los **errores lógicos**. El programa funciona sin excepciones, pero produce un resultado incorrecto con ciertos datos de entrada. No hay ningún mensaje de error, solo una salida inesperada. Por ejemplo, una función que debería calcular la suma pero calcula la diferencia: + +```javascript +// La función debería calcular la suma de los números, pero calcula la diferencia: +const sum = (a, b) => a - b; + +// Con esta llamada el error no es evidente, porque +// tanto la suma como la resta dan el mismo resultado +sum(4, 0); // 4 +``` + +## Métodos de depuración + +En la base de cualquier método de depuración está la observación de las variables durante la ejecución. Veamos una función concreta. + +A continuación, una función que calcula la suma de los números desde `start` hasta `finish`. Con `start=3` y `finish=5` debería calcular `3 + 4 + 5`. + +```javascript +const sumOfSeries = (start, finish) => { + let result = 0; + let n = start; + while (n < finish) { + result = result + n; + n = n + 1; + } + + return result; +}; +``` + +Las variables clave de esta función son `n` y `result`. Para encontrar el error, hay que ver qué valores toman en cada iteración. + +Para ello existen los **depuradores visuales**. Se integran en los editores de código populares y permiten ejecutar el programa paso a paso, observando las variables en tiempo real. Puedes encontrar uno buscando «JavaScript debuggers» en Google. + +En el entorno de Hexlet, en lugar de un depurador se usa la **impresión de depuración**. El principio es el mismo, solo que los valores de las variables se imprimen con el habitual `console.log`. Lo que se imprime aparece en la pestaña `OUTPUT`. + +```javascript +const sumOfSeries = (start, finish) => { + let result = 0; + let n = start; + while (n < finish) { + console.log('new iteration !!!!'); + console.log(n); + result = result + n; + n = n + 1; + console.log(result); + } + + return result; +}; + +sumOfSeries(3, 5); + +// new iteration !!!! +// 3 +// 3 +// new iteration !!!! +// 4 +// 7 +``` + +La salida muestra que hay una iteración menos de las necesarias. El cinco (`finish`) no entró en la suma. La condición usa `n < finish` en lugar de `n <= finish`. Hay que reemplazar el signo `<` por `<=`. + +Los principiantes a menudo se molestan por los errores y se consideran descuidados. Todos cometen errores, tanto los junior como los senior. La diferencia está en con cuánta seguridad los encuentras. + +Los principiantes piensan que un buen desarrollador mira el código y entiende de inmediato qué está mal. Esto rara vez funciona en la práctica. Un fragmento de código sin contexto dice muy poco. **Si quieres pedir consejo a un desarrollador experimentado, lo primero es mostrarle el mensaje de error.** diff --git a/modules/50-loops/90-debug/es/data.yml b/modules/50-loops/90-debug/es/data.yml new file mode 100644 index 00000000..3bd22dc5 --- /dev/null +++ b/modules/50-loops/90-debug/es/data.yml @@ -0,0 +1,4 @@ +--- +name: Depuración +tips: [] +definitions: [] diff --git a/modules/50-loops/90-debug/index.js b/modules/50-loops/90-debug/index.js new file mode 100644 index 00000000..98b2ec9b --- /dev/null +++ b/modules/50-loops/90-debug/index.js @@ -0,0 +1,31 @@ +// BEGIN +const compress = (str) => { + if (str === '') { + return ''; + } + + let result = ''; + let count = 1; + + for (let i = 1; i < str.length; i += 1) { + if (str[i] === str[i - 1]) { + count += 1; + } else { + result += str[i - 1]; + if (count > 1) { + result += count; + } + count = 1; + } + } + + result += str[str.length - 1]; + if (count > 1) { + result += count; + } + + return result; +}; +// END + +export default compress; diff --git a/modules/50-loops/90-debug/ru/EXERCISE.md b/modules/50-loops/90-debug/ru/EXERCISE.md new file mode 100644 index 00000000..e00b0567 --- /dev/null +++ b/modules/50-loops/90-debug/ru/EXERCISE.md @@ -0,0 +1,18 @@ +Реализуйте функцию `compress(str)`, которая сжимает строку методом RLE (Run-Length Encoding). + +Алгоритм: если символ повторяется несколько раз подряд, он заменяется на символ и количество повторений. Одиночные символы записываются без цифры. + +```javascript +compress('aaabcccc'); // => 'a3bc4' +compress('abcd'); // => 'abcd' +compress('aabbaa'); // => 'a2b2a2' +compress(''); // => '' +``` + +Этот алгоритм используется в реальных форматах сжатия данных — например, в старых факс-протоколах и BMP-файлах. + +### Алгоритм + +1. Идти по строке, считая количество подряд идущих одинаковых символов. +2. Как только символ меняется — записать предыдущий символ и счётчик (если больше 1). +3. Не забыть обработать последнюю группу после окончания цикла. diff --git a/modules/50-loops/90-debug/ru/README.md b/modules/50-loops/90-debug/ru/README.md new file mode 100644 index 00000000..26cdc4a6 --- /dev/null +++ b/modules/50-loops/90-debug/ru/README.md @@ -0,0 +1,110 @@ +Даже у самых опытных разработчиков код редко работает идеально с первого раза. Чем опытнее разработчик, тем увереннее он **отлаживает** его, то есть анализирует ошибки и устраняет их. + +Навык отладки сам по себе не появится. Его нужно развивать, причём как можно раньше. По ходу обучения вы будете выполнять задания и практиковаться, и со временем анализ ошибок войдёт в привычку. + +## Как найти ошибку в коде + +Отладка методом тыка занимает много времени. Гораздо продуктивнее сначала понять, что именно пошло не так, а потом устранять причину. С понимания начинается любая работа с ошибками. + +Первым делом изучите **стек вызовов** (stack trace). Он содержит список всех вызовов функций от места ошибки к началу программы. По нему видно, какие функции выполнялись и где возникла проблема. Каждая запись указывает на файл, строчку и выполняемую функцию. + +Представим, что вы написали код в файле `users.js` и вызвали функцию `main()`, которая внутри вызывает несуществующую `create()`: + +```javascript +const main = () => { + create(); +}; + +main(); +``` + +При запуске вы увидите такой вывод: + +```bash +ReferenceError: create is not defined + at main (users.js:2:3) + at users.js:5:1 +``` + +Первая строка — это **сообщение об ошибке**. Здесь `ReferenceError: create is not defined` говорит, что имя `create` не определено. Такая ошибка чаще всего означает опечатку в имени. + +Ниже идёт сам стек вызовов. Видно, что программа дошла до `main()` (строка 5), внутри неё вызвала `create()` (строка 2) и здесь столкнулась с ошибкой. Каждая запись `at ...` указывает на файл и строчку. + +## Типы ошибок + +Самые понятные ошибки — **синтаксические**. Они возникают, когда код оформлен неверно, например из-за незакрытой скобки или несовпадающих кавычек. В выводе всегда присутствует `SyntaxError`: + +```bash +console.log("Hello" + 'world); + ^^^^^^^^ +SyntaxError: Invalid or unexpected token +``` + +Труднее всего исправлять **ошибки программирования**. Сюда входят вызов несуществующей функции, использование необъявленной переменной, передача аргументов неверного типа. Обычно они возникают не в том месте, где настоящая причина, что и усложняет диагностику. + +Сложнее всего бороться с **логическими ошибками**. Программа работает без исключений, но выдаёт неверный результат при некоторых входных данных. Никакого сообщения об ошибке нет, только неожиданный вывод. Например, функция должна считать сумму, но считает разность: + +```javascript +// Функция должна считать сумму чисел, но считает разность: +const sum = (a, b) => a - b; + +// При таком вызове ошибка неочевидна, потому что +// и при сложении, и при вычитании будет один и тот же результат +sum(4, 0); // 4 +``` + +## Способы отладки + +В основе любого метода отладки лежит наблюдение за переменными в процессе выполнения. Посмотрим на конкретную функцию. + +Ниже функция, которая считает сумму чисел от `start` до `finish`. При `start=3` и `finish=5` она должна вычислить `3 + 4 + 5`. + +```javascript +const sumOfSeries = (start, finish) => { + let result = 0; + let n = start; + while (n < finish) { + result = result + n; + n = n + 1; + } + + return result; +}; +``` + +В функции ключевые переменные `n` и `result`. Чтобы найти ошибку, нужно посмотреть, какие значения они принимают на каждой итерации. + +Для этого существуют **визуальные отладчики**. Они встраиваются в популярные редакторы кода и позволяют выполнить программу по шагам, наблюдая за переменными в реальном времени. Найти подходящий можно по запросу «JavaScript debuggers» в Google. + +На Хекслете вместо отладчика используется **отладочная печать**. Принцип тот же, только значения переменных выводятся через обычный `console.log`. То, что печатается, отображается во вкладке `OUTPUT`. + +```javascript +const sumOfSeries = (start, finish) => { + let result = 0; + let n = start; + while (n < finish) { + console.log('new iteration !!!!'); + console.log(n); + result = result + n; + n = n + 1; + console.log(result); + } + + return result; +}; + +sumOfSeries(3, 5); + +// new iteration !!!! +// 3 +// 3 +// new iteration !!!! +// 4 +// 7 +``` + +Вывод показывает, что итераций на одну меньше, чем нужно. Пятёрка (`finish`) в сложение не попала. В условии стоит `n < finish` вместо `n <= finish`. Нужно заменить знак `<` на `<=`. + +Начинающие разработчики часто расстраиваются из-за ошибок и считают себя невнимательными. Ошибки есть у всех, и у джунов, и у сеньоров. Разница в том, насколько уверенно ты их находишь. + +Начинающие думают, что хороший разработчик смотрит на код и сразу понимает, что не так. Это редко работает на практике. Фрагмент кода без контекста мало что говорит. **Если хотите попросить совет у опытного разработчика, первым делом покажите сообщение об ошибке.** diff --git a/modules/50-loops/90-debug/ru/data.yml b/modules/50-loops/90-debug/ru/data.yml new file mode 100644 index 00000000..32222bd2 --- /dev/null +++ b/modules/50-loops/90-debug/ru/data.yml @@ -0,0 +1,4 @@ +--- +name: Отладка +tips: [] +definitions: [] diff --git a/modules/50-loops/90-debug/test.js b/modules/50-loops/90-debug/test.js new file mode 100644 index 00000000..7708354a --- /dev/null +++ b/modules/50-loops/90-debug/test.js @@ -0,0 +1,11 @@ +import { expect, test } from 'vitest'; +import f from './index.js'; + +test('test', () => { + expect(f('aaabcccc')).toBe('a3bc4'); + expect(f('abcd')).toBe('abcd'); + expect(f('aabbaa')).toBe('a2b2a2'); + expect(f('a')).toBe('a'); + expect(f('')).toBe(''); + expect(f('zzz')).toBe('z3'); +});