From f218deca6b7ec5a078e7ceccbf033586202e9d07 Mon Sep 17 00:00:00 2001 From: Nikolay Gagarinov Date: Tue, 4 Aug 2026 21:28:46 +0500 Subject: [PATCH] =?UTF-8?q?feat(90-loops):=20=D0=BB=D0=BE=D0=BA=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=20en/es=20=E2=80=94=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=B2=D0=BE=D0=B4=20=D0=BF=D0=BE=D0=B4=20=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D1=8B=D0=B9=20ru?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - en создан для всех 11 уроков модуля (модульный description.en.yml был) - es пересинхронизирован под новый ru: * 200-aggregation-strings — не было ASCII-схемы наращивания строки, абзаца про две переменные и секции «Нейтральный элемент» * 250-iteration-over-strings — не было ASCII-схемы, вводного абзаца про charAt/length и секции «Обход в обратном порядке» * 300-conditions-inside-loops — не было секций «Счетчик и условие отвечают за разное» и «Работа по шагам»; GoT-цитата была переведена ("Fear cuts deeper than swords." → "El miedo corta...") * 350-build-strings — код в теории показывал обход с конца, то есть готовое решение упражнения, вместо обхода с начала как в ru * 400-syntax-sugar — были только вводный абзац и список, не было всех четырёх секций * 500-return-from-loops — не было секции с пошаговым разбором проверки числа 5 и ASCII-блока про return; бессмысленное «hasta x - 1/2» * 550-for — не было секций «Разбор по шагам» и «Сравнение с while» (с ASCII-сравнением), примера суммы 0..9 и пояснения про i++ * 600-debug — es создан с нуля - битая es-ссылка es.wikipedia.org/wiki/Resto_(matemática) (404) → División_euclidiana; терминология es: Bucle While → Ciclo While - добавлены отсутствовавшие в es tips/definitions (200, 250, 400, 500, 550) Co-Authored-By: Claude Opus 5 (1M context) --- modules/90-loops/100-while/en/EXERCISE.md | 16 +++ modules/90-loops/100-while/en/README.md | 119 ++++++++++++++++++ modules/90-loops/100-while/en/data.yml | 5 + modules/90-loops/100-while/es/data.yml | 2 +- .../150-aggregation-numbers/en/EXERCISE.md | 8 ++ .../150-aggregation-numbers/en/README.md | 85 +++++++++++++ .../150-aggregation-numbers/en/data.yml | 3 + .../200-aggregation-strings/en/EXERCISE.md | 8 ++ .../200-aggregation-strings/en/README.md | 61 +++++++++ .../200-aggregation-strings/en/data.yml | 10 ++ .../200-aggregation-strings/es/README.md | 40 ++++-- .../200-aggregation-strings/es/data.yml | 9 +- .../250-iteration-over-strings/en/EXERCISE.md | 11 ++ .../250-iteration-over-strings/en/README.md | 63 ++++++++++ .../250-iteration-over-strings/en/data.yml | 5 + .../250-iteration-over-strings/es/README.md | 49 ++++++-- .../250-iteration-over-strings/es/data.yml | 6 +- .../en/EXERCISE.md | 9 ++ .../300-conditions-inside-loops/en/README.md | 53 ++++++++ .../300-conditions-inside-loops/en/data.yml | 3 + .../300-conditions-inside-loops/es/README.md | 56 ++++++--- .../90-loops/350-build-strings/en/EXERCISE.md | 2 + .../90-loops/350-build-strings/en/README.md | 31 +++++ .../90-loops/350-build-strings/en/data.yml | 3 + .../90-loops/350-build-strings/es/README.md | 22 ++-- .../90-loops/400-syntax-sugar/en/EXERCISE.md | 10 ++ .../90-loops/400-syntax-sugar/en/README.md | 66 ++++++++++ modules/90-loops/400-syntax-sugar/en/data.yml | 10 ++ .../90-loops/400-syntax-sugar/es/README.md | 71 +++++++++-- modules/90-loops/400-syntax-sugar/es/data.yml | 9 +- modules/90-loops/450-mutators/en/EXERCISE.md | 8 ++ modules/90-loops/450-mutators/en/README.md | 53 ++++++++ modules/90-loops/450-mutators/en/data.yml | 6 + modules/90-loops/450-mutators/es/data.yml | 4 +- .../500-return-from-loops/en/EXERCISE.md | 12 ++ .../500-return-from-loops/en/README.md | 58 +++++++++ .../500-return-from-loops/en/data.yml | 10 ++ .../500-return-from-loops/es/README.md | 34 +++-- .../500-return-from-loops/es/data.yml | 9 +- modules/90-loops/550-for/en/EXERCISE.md | 12 ++ modules/90-loops/550-for/en/README.md | 77 ++++++++++++ modules/90-loops/550-for/en/data.yml | 8 ++ modules/90-loops/550-for/es/README.md | 71 +++++++++-- modules/90-loops/550-for/es/data.yml | 5 + modules/90-loops/600-debug/en/EXERCISE.md | 17 +++ modules/90-loops/600-debug/en/README.md | 86 +++++++++++++ modules/90-loops/600-debug/en/data.yml | 12 ++ modules/90-loops/600-debug/es/EXERCISE.md | 17 +++ modules/90-loops/600-debug/es/README.md | 86 +++++++++++++ modules/90-loops/600-debug/es/data.yml | 13 ++ 50 files changed, 1363 insertions(+), 80 deletions(-) create mode 100644 modules/90-loops/100-while/en/EXERCISE.md create mode 100644 modules/90-loops/100-while/en/README.md create mode 100644 modules/90-loops/100-while/en/data.yml create mode 100644 modules/90-loops/150-aggregation-numbers/en/EXERCISE.md create mode 100644 modules/90-loops/150-aggregation-numbers/en/README.md create mode 100644 modules/90-loops/150-aggregation-numbers/en/data.yml create mode 100644 modules/90-loops/200-aggregation-strings/en/EXERCISE.md create mode 100644 modules/90-loops/200-aggregation-strings/en/README.md create mode 100644 modules/90-loops/200-aggregation-strings/en/data.yml create mode 100644 modules/90-loops/250-iteration-over-strings/en/EXERCISE.md create mode 100644 modules/90-loops/250-iteration-over-strings/en/README.md create mode 100644 modules/90-loops/250-iteration-over-strings/en/data.yml create mode 100644 modules/90-loops/300-conditions-inside-loops/en/EXERCISE.md create mode 100644 modules/90-loops/300-conditions-inside-loops/en/README.md create mode 100644 modules/90-loops/300-conditions-inside-loops/en/data.yml create mode 100644 modules/90-loops/350-build-strings/en/EXERCISE.md create mode 100644 modules/90-loops/350-build-strings/en/README.md create mode 100644 modules/90-loops/350-build-strings/en/data.yml create mode 100644 modules/90-loops/400-syntax-sugar/en/EXERCISE.md create mode 100644 modules/90-loops/400-syntax-sugar/en/README.md create mode 100644 modules/90-loops/400-syntax-sugar/en/data.yml create mode 100644 modules/90-loops/450-mutators/en/EXERCISE.md create mode 100644 modules/90-loops/450-mutators/en/README.md create mode 100644 modules/90-loops/450-mutators/en/data.yml create mode 100644 modules/90-loops/500-return-from-loops/en/EXERCISE.md create mode 100644 modules/90-loops/500-return-from-loops/en/README.md create mode 100644 modules/90-loops/500-return-from-loops/en/data.yml create mode 100644 modules/90-loops/550-for/en/EXERCISE.md create mode 100644 modules/90-loops/550-for/en/README.md create mode 100644 modules/90-loops/550-for/en/data.yml create mode 100644 modules/90-loops/600-debug/en/EXERCISE.md create mode 100644 modules/90-loops/600-debug/en/README.md create mode 100644 modules/90-loops/600-debug/en/data.yml create mode 100644 modules/90-loops/600-debug/es/EXERCISE.md create mode 100644 modules/90-loops/600-debug/es/README.md create mode 100644 modules/90-loops/600-debug/es/data.yml diff --git a/modules/90-loops/100-while/en/EXERCISE.md b/modules/90-loops/100-while/en/EXERCISE.md new file mode 100644 index 00000000..c8d2cbb0 --- /dev/null +++ b/modules/90-loops/100-while/en/EXERCISE.md @@ -0,0 +1,16 @@ + +Modify the method `printNumbers()` so that it prints the numbers in reverse order. For this you need to go from the upper bound to the lower one. That is, the counter has to be initialized with the maximum value, and in the body of the loop it has to be decreased down to the lower bound. + +An example of a call and the output: + +```java +printNumbers(4); +``` + +```text +4 +3 +2 +1 +finished! +``` diff --git a/modules/90-loops/100-while/en/README.md b/modules/90-loops/100-while/en/README.md new file mode 100644 index 00000000..a8b0cd6e --- /dev/null +++ b/modules/90-loops/100-while/en/README.md @@ -0,0 +1,119 @@ +The programs we write while studying are becoming more and more complex and voluminous. They are still very far from real programs, where the number of lines of code is measured in tens and hundreds of thousands, but the current complexity is already able to make people without experience tense up. + +Starting with this lesson, we move on to one of the most complex basic topics in programming — **loops**. + +Any applied programs serve very pragmatic goals. They help to manage employees, finances, and in the end they entertain. Despite the differences, all these programs execute the algorithms built into them, which are very similar to each other. + +An **algorithm** is a sequence of actions or instructions that leads us to some expected result. This description fits any program, but by algorithms something more specific is usually meant. + +Imagine that we have a book and we want to find some specific phrase inside it. We remember the phrase itself, but we do not know which page it is on. How do we find the needed page? + +The simplest and longest way is to look through the book sequentially until we find the needed page. In the worst case we will have to look through all the pages, but we will get the result anyway. + +Exactly this process is called an **algorithm**. It includes going through the pages and logical checks of whether we have found the phrase or not. The number of pages we will have to look at is not known in advance, but the process of looking itself repeats from time to time in a completely identical way. + +Loops are exactly what is needed for performing repeating actions. Every such repetition is called an **iteration**. + +Suppose we want to write a method. It has to print to the screen all the numbers from 1 to the number that we specified through the parameters: + +```java +App.printNumbers(3); +// 1 +// 2 +// 3 +``` + +It is impossible to implement this method with the means already studied, because the number of prints to the screen is not known in advance. But with loops this will not be a problem at all: + +```java +public static void printNumbers(int lastNumber) { + // i is a shortening of index (the ordinal number) + // By common agreement it is used in many languages as the loop counter + var i = 1; + + while (i <= lastNumber) { + System.out.println(i); + i = i + 1; + } + System.out.println("finished!"); +} + +App.printNumbers(3); +``` + +```text +1 +2 +3 +finished! +``` + +The `while` loop is used in the code of the method. It consists of three elements: + +* The **keyword** `while`. Despite the similarity to method calls, this is not a method call +* The **predicate** — the condition that is specified in parentheses after `while` and is evaluated on every iteration +* The **body of the loop** — a block of code in curly braces, analogous to the block of code in a method. All the constants or variables defined inside this block will be visible only inside this block + +The construct reads like this: "do what is specified in the body of the loop while the condition `i <= lastNumber` is true". Let's take apart the work of this code for the call `App.printNumbers(3)`: + +```java +// i is initialized +var i = 1; + +// The predicate returns true, so the body of the loop is executed +while (1 <= 3) +// System.out.println(1); +// i = 1 + 1; + +// The body of the loop has ended, so a return to the beginning happens +while (2 <= 3) +// System.out.println(2); +// i = 2 + 1; + +// The body of the loop has ended, so a return to the beginning happens +while (3 <= 3) +// System.out.println(3); +// i = 3 + 1; + +// The predicate returns false, so the execution moves past the loop +while (4 <= 3) + +// System.out.println("finished!"); +// At this stage i equals 4, but we do not need it anymore +// The method finishes +``` + +The most important thing in a loop is the ending of its work, that is, **exiting the loop**. The process that the loop generates must stop in the end. The responsibility for stopping lies entirely on the programmer. + +Usually the task comes down to introducing a variable called the **loop counter**. It works on the following principle: + +* First the counter is initialized, that is, an initial value is set for it. In the example above the counter is the instruction `var i = 1`, executed before entering the loop +* Then the condition of the loop checks whether the counter has reached its limit value. +* In the end the counter changes its value `i = i + 1` + +At this point beginners make the most mistakes. Let's imagine that the check in the predicate is written incorrectly in the code. This can lead to an **infinite loop** — a situation in which the loop works endlessly and the program never stops. + +In that case you have to terminate it forcibly: + +```java +public static void printNumbers(int lastNumber) { + var i = 1; + + // This loop will never stop + // and will always print one and the same value + while (i <= lastNumber) { + System.out.println(i); + } + System.out.println("finished!"); +} +``` + +In some cases infinite loops are useful. We do not consider such cases here, but it is useful to see what this code looks like: + +```java +while (true) { + // We do something +} +``` + +Let's sum up. When are loops really needed, and when can you do without them? It is impossible to do without loops when the algorithm of solving the task requires repeating some actions, and the number of these operations is not known in advance. That is how it was in the example with the book that we looked at in the beginning of the lesson. diff --git a/modules/90-loops/100-while/en/data.yml b/modules/90-loops/100-while/en/data.yml new file mode 100644 index 00000000..2c284111 --- /dev/null +++ b/modules/90-loops/100-while/en/data.yml @@ -0,0 +1,5 @@ +--- +name: The While loop +definitions: + - name: The While loop + description: an instruction for repeating code while some condition is satisfied. diff --git a/modules/90-loops/100-while/es/data.yml b/modules/90-loops/100-while/es/data.yml index 9fbbc39d..bcdd9c60 100644 --- a/modules/90-loops/100-while/es/data.yml +++ b/modules/90-loops/100-while/es/data.yml @@ -1,7 +1,7 @@ --- name: Ciclo While definitions: - - name: Bucle While + - name: Ciclo While description: >- una instrucción para repetir un bloque de código mientras se cumple una determinada condición. diff --git a/modules/90-loops/150-aggregation-numbers/en/EXERCISE.md b/modules/90-loops/150-aggregation-numbers/en/EXERCISE.md new file mode 100644 index 00000000..8604f1ff --- /dev/null +++ b/modules/90-loops/150-aggregation-numbers/en/EXERCISE.md @@ -0,0 +1,8 @@ + +Implement the method `multiplyNumbersFromRange()`, which multiplies the numbers in the specified range including the bounds of the range. An example of a call: + +```java +App.multiplyNumbersFromRange(1, 5); // 1 * 2 * 3 * 4 * 5 = 120 +App.multiplyNumbersFromRange(2, 3); // 2 * 3 = 6 +App.multiplyNumbersFromRange(6, 6); // 6 +``` diff --git a/modules/90-loops/150-aggregation-numbers/en/README.md b/modules/90-loops/150-aggregation-numbers/en/README.md new file mode 100644 index 00000000..56e1dbb8 --- /dev/null +++ b/modules/90-loops/150-aggregation-numbers/en/README.md @@ -0,0 +1,85 @@ +In programming there is a separate class of tasks that cannot do without loops — it is called **data aggregation**. + +Such tasks include finding: + +* The maximum value +* The minimum value +* The sum +* The arithmetic mean + +Their main feature is that the result depends on the whole set of data. To calculate the sum you need to add up **all** the numbers; to calculate the maximum you need to compare **all** the numbers. + +Everyone who works with numbers is well familiar with this topic. For example, accountants or marketers often work with such tasks in spreadsheets like Microsoft Excel or Google Sheets. + +Let's take apart the simplest example — finding the sum of a set of numbers. Let's implement a function that adds up the numbers in the specified range, including the bounds. + +In this case a **range** is a series of numbers from some beginning to a certain end. For example, the range `[1, 10]` includes all the integers from 1 to 10: + +```java +App.sumNumbersFromRange(5, 7); // 5 + 6 + 7 = 18 +App.sumNumbersFromRange(1, 2); // 1 + 2 = 3 + +// The range [1, 1] with the same beginning and end is also a range +// It includes one number — the bound of the range itself +App.sumNumbersFromRange(1, 1); // 1 +App.sumNumbersFromRange(100, 100); // 100 +``` + +To implement this code we will need a loop. We choose a loop exactly because adding numbers is an iterative process. It repeats for every number, and the number of iterations depends on the size of the range. + +To understand the topic better, try to answer the questions: + +* What value should the counter be initialized with? +* How will it change? +* When must the loop stop? + +And now look at the code below: + +```java +public static int sumNumbersFromRange(int start, int finish) { + // Technically you can change start, but input arguments should be left at their original value + // This makes the code simpler to analyze + var i = start; + var sum = 0; // Initializing the sum + + while (i <= finish) { // We move to the end of the range + sum = sum + i; // We count the sum for every number + i = i + 1; // We move on to the next number in the range + } + + // We return the resulting value + return sum; +} +``` + +The general structure of the loop here is standard: + +* The counter, which is initialized with the starting value of the range +* The loop itself with the stopping condition when the end of the range is reached +* Changing the counter at the end of the body of the loop + +The number of iterations in such a loop equals `finish - start + 1`. For example, 3 iterations are needed to count the range from 5 to 7: + +```md +7 - 5 + 1 = 3 +``` + +The main differences from ordinary processing are related to the logic of calculating the result. In aggregation tasks there is always some variable that stores inside itself the result of the loop's work. In the code above it is `sum`. + +On every iteration of the loop it changes, the next number in the range gets added: `sum = sum + i`. The whole process looks like this: + +```java +// For the call sumNumbersFromRange(2, 5); +var sum = 0; +sum = sum + 2; // 2 +sum = sum + 3; // 5 +sum = sum + 4; // 9 +sum = sum + 5; // 14 +// 14 is the result of adding the numbers in the range [2, 5] +``` + +In mathematics there is the notion of the **neutral element of an operation**. An operation with such an element does not change the value the operation is performed on: + +* In addition any number plus zero gives the number itself +* In subtraction it is the same +* Even concatenation has a neutral element — it is the empty string: `"" + "one"` will be `"one"` diff --git a/modules/90-loops/150-aggregation-numbers/en/data.yml b/modules/90-loops/150-aggregation-numbers/en/data.yml new file mode 100644 index 00000000..f747644a --- /dev/null +++ b/modules/90-loops/150-aggregation-numbers/en/data.yml @@ -0,0 +1,3 @@ +--- +name: Data aggregation (Numbers) +tips: [] diff --git a/modules/90-loops/200-aggregation-strings/en/EXERCISE.md b/modules/90-loops/200-aggregation-strings/en/EXERCISE.md new file mode 100644 index 00000000..8ed89148 --- /dev/null +++ b/modules/90-loops/200-aggregation-strings/en/EXERCISE.md @@ -0,0 +1,8 @@ + +Implement the method `joinNumbersFromRange()`, which joins all the numbers from a range into a string: + +```java +App.joinNumbersFromRange(1, 1); // "1" +App.joinNumbersFromRange(2, 3); // "23" +App.joinNumbersFromRange(5, 10); // "5678910" +``` diff --git a/modules/90-loops/200-aggregation-strings/en/README.md b/modules/90-loops/200-aggregation-strings/en/README.md new file mode 100644 index 00000000..67212396 --- /dev/null +++ b/modules/90-loops/200-aggregation-strings/en/README.md @@ -0,0 +1,61 @@ +Aggregation is applied not only to numbers, but also to strings. By string aggregation we mean tasks in which it is not known in advance what the strings contain and what size they are. + +In aggregation the string is formed dynamically. Imagine a method that repeats a string the specified number of times. Java has ready-made means for this, but here we will look at how such a repetition is arranged inside: + +```java +App.repeat("hexlet", 3); // "hexlethexlethexlet" +``` + +The principle of this method's work is quite simple. In a loop the string grows the specified number of times: + +```java +public static String repeat(String text, int times) { + // The neutral element for strings is the empty string + var result = ""; + var i = 1; + + while (i <= times) { + // Every time we add the string to the result + result = result + text; + i = i + 1; + } + + return result; +} +``` + +Let's describe the execution of this code step by step: + +```java +// For the call repeat("hexlet", 3); +var result = ""; +result = result + "hexlet"; // "hexlet" +result = result + "hexlet"; // "hexlethexlet" +result = result + "hexlet"; // "hexlethexlethexlet" +``` + +Visually the process of growing the string looks like this: + +```text +repeat("hexlet", 3): + +i=1: result = "" + "hexlet" = "hexlet" +i=2: result = "hexlet" + "hexlet" = "hexlethexlet" +i=3: result = "hexlethexlet" + "hexlet" = "hexlethexlethexlet" + └── the result +``` + +Two variables work here at once. The counter `i` controls the number of repetitions and stops the loop when the repetitions have reached `times`. The variable `result` stores the accumulated string and gives it to the calling code after the loop. + +## The neutral element + +For the growing to work, a starting value is needed. For strings the **empty string** `""` serves as it. + +It is called the neutral element, because in concatenation it does not change anything: + +```java +System.out.println("" + "abc"); // => abc +System.out.println("abc" + ""); // => abc +``` + +That is why the empty string always stands at the beginning in string aggregation. The loop starts the growing from it, and then on every iteration adds the next piece to the result. diff --git a/modules/90-loops/200-aggregation-strings/en/data.yml b/modules/90-loops/200-aggregation-strings/en/data.yml new file mode 100644 index 00000000..7b4b174c --- /dev/null +++ b/modules/90-loops/200-aggregation-strings/en/data.yml @@ -0,0 +1,10 @@ +--- +name: Data aggregation (Strings) +tips: + - | + [Iteration](https://en.wikipedia.org/wiki/Iteration) +definitions: + - name: Aggregation + description: >- + Accumulating the result during the iterations and working with it after the + loop. diff --git a/modules/90-loops/200-aggregation-strings/es/README.md b/modules/90-loops/200-aggregation-strings/es/README.md index 962b4596..53e891c9 100644 --- a/modules/90-loops/200-aggregation-strings/es/README.md +++ b/modules/90-loops/200-aggregation-strings/es/README.md @@ -1,21 +1,21 @@ -La agregación se aplica no solo a los números, sino también a las cadenas. +La agregación se aplica no solo a los números, sino también a las cadenas. Por agregación de cadenas se entienden las tareas en las que no se sabe de antemano qué contienen las cadenas ni de qué tamaño son. -Al agregar cadenas, la cadena se forma dinámicamente, es decir, no se sabe de antemano su tamaño ni qué contendrá. Imagina un método que puede multiplicar una cadena, es decir, repetirla la cantidad de veces especificada: +En la agregación, la cadena se forma dinámicamente. Imagina un método que repite una cadena la cantidad de veces indicada. En Java hay herramientas ya hechas para eso, pero aquí veremos cómo está montada esa repetición por dentro: ```java App.repeat("hexlet", 3); // "hexlethexlethexlet" ``` -El principio de funcionamiento de este método es bastante simple. En un bucle, la cadena se incrementa la cantidad de veces especificada: +El principio de funcionamiento de este método es bastante simple. En el ciclo se produce el crecimiento de la cadena la cantidad de veces indicada: ```java public static String repeat(String text, int times) { - // El elemento neutro para las cadenas es una cadena vacía + // El elemento neutro para las cadenas es la cadena vacía var result = ""; var i = 1; while (i <= times) { - // Agregamos la cadena al resultado cada vez + // Cada vez añadimos la cadena al resultado result = result + text; i = i + 1; } @@ -24,12 +24,38 @@ public static String repeat(String text, int times) { } ``` -Desglosemos la ejecución de este código paso a paso: +Describamos la ejecución de este código paso a paso: ```java -// Para llamar a repeat("hexlet", 3); +// Para la llamada repeat("hexlet", 3); var result = ""; result = result + "hexlet"; // "hexlet" result = result + "hexlet"; // "hexlethexlet" result = result + "hexlet"; // "hexlethexlethexlet" ``` + +Visualmente, el proceso de crecimiento de la cadena se ve así: + +```text +repeat("hexlet", 3): + +i=1: result = "" + "hexlet" = "hexlet" +i=2: result = "hexlet" + "hexlet" = "hexlethexlet" +i=3: result = "hexlethexlet" + "hexlet" = "hexlethexlethexlet" + └── resultado +``` + +Aquí trabajan a la vez dos variables. El contador `i` controla la cantidad de repeticiones y detiene el ciclo cuando las repeticiones llegan a `times`. La variable `result` guarda la cadena acumulada y la entrega al código que llama después del ciclo. + +## El elemento neutro + +Para que el crecimiento funcione hace falta un valor de partida. Para las cadenas, ese valor es la **cadena vacía** `""`. + +Se la llama elemento neutro porque en la concatenación no cambia nada: + +```java +System.out.println("" + "abc"); // => abc +System.out.println("abc" + ""); // => abc +``` + +Por eso la cadena vacía está siempre al principio en la agregación de cadenas. Desde ella el ciclo empieza el crecimiento, y después, en cada iteración, añade al resultado el siguiente trozo. diff --git a/modules/90-loops/200-aggregation-strings/es/data.yml b/modules/90-loops/200-aggregation-strings/es/data.yml index 5b63c296..2b24e82e 100644 --- a/modules/90-loops/200-aggregation-strings/es/data.yml +++ b/modules/90-loops/200-aggregation-strings/es/data.yml @@ -1,3 +1,10 @@ --- name: Agregación de datos (Cadenas) -tips: [] +tips: + - | + [Iteración](https://es.wikipedia.org/wiki/Iteraci%C3%B3n) +definitions: + - name: Agregación + description: >- + Acumulación del resultado durante las iteraciones y trabajo con él después + del ciclo. diff --git a/modules/90-loops/250-iteration-over-strings/en/EXERCISE.md b/modules/90-loops/250-iteration-over-strings/en/EXERCISE.md new file mode 100644 index 00000000..1ab80e67 --- /dev/null +++ b/modules/90-loops/250-iteration-over-strings/en/EXERCISE.md @@ -0,0 +1,11 @@ + +Implement the static method `App.printReversedNameBySymbol()`, which prints the passed word character by character, as in the example from the theory, but does it in reverse order. + +```java +var name = "Arya"; +App.printReversedNameBySymbol(name); +// 'a' +// 'y' +// 'r' +// 'A' +``` diff --git a/modules/90-loops/250-iteration-over-strings/en/README.md b/modules/90-loops/250-iteration-over-strings/en/README.md new file mode 100644 index 00000000..af798a53 --- /dev/null +++ b/modules/90-loops/250-iteration-over-strings/en/README.md @@ -0,0 +1,63 @@ +With the help of loops numbers are processed and strings are worked with. A string consists of characters, and every character can be reached by its index. Indexes start from zero, so the first character of the string `"Arya"` has index `0`, and the last one has index `length() - 1`. + +The `charAt()` method allows you to get a character by its index, and the `length()` method allows you to find out the length of the string. These two methods and a counter are enough to go through a string character by character. Below is an example of code that prints the letters of a word on separate lines: + +```java +public static void printNameBySymbol(String name) { + var i = 0; + // Such a check is performed until the end of the string, + // including the last character. Its index is `length() - 1`. + while (i < name.length()) { + // We access the character by its index + System.out.println(name.charAt(i)); + i = i + 1; + } +} + +var name = "Arya"; +App.printNameBySymbol(name); +// "A" +// "r" +// "y" +// "a" +``` + +The loop goes through every character of the string in turn: + +```text +"Arya" + │ │ │ │ + A r y a + ↓ ↓ ↓ ↓ +every character is processed in turn +``` + +The main thing in this code is to set the correct condition in `while`. This can be done in two ways: + +* `i < name.length()` +* `i <= name.length() - 1` + +Both ways lead to the same result. The first one occurs more often, because it has less arithmetic. + +## Going through in reverse order + +The counter is not obliged to grow from zero. If you start from the last index and decrease it down to zero, the string will be gone through backwards. Let's write a method that prints the characters of a word in reverse order: + +```java +public static void printReversed(String name) { + // We start from the last character + var i = name.length() - 1; + while (i >= 0) { + System.out.println(name.charAt(i)); + i = i - 1; + } +} + +App.printReversed("Arya"); +// "a" +// "y" +// "r" +// "A" +``` + +Here the counter `i` starts with the value `name.length() - 1`, moves towards zero and ends the loop when it becomes less than zero. On every step the method takes the character at the current index and prints it. The direction of going through is set only by the initial value of the counter and the way it is changed. diff --git a/modules/90-loops/250-iteration-over-strings/en/data.yml b/modules/90-loops/250-iteration-over-strings/en/data.yml new file mode 100644 index 00000000..e9786b5e --- /dev/null +++ b/modules/90-loops/250-iteration-over-strings/en/data.yml @@ -0,0 +1,5 @@ +--- +name: Going through strings +tips: + - | + [Iteration](https://en.wikipedia.org/wiki/Iteration) diff --git a/modules/90-loops/250-iteration-over-strings/es/README.md b/modules/90-loops/250-iteration-over-strings/es/README.md index 7eb9ec47..141b660a 100644 --- a/modules/90-loops/250-iteration-over-strings/es/README.md +++ b/modules/90-loops/250-iteration-over-strings/es/README.md @@ -1,14 +1,16 @@ -Los bucles no solo son útiles para trabajar con números, sino también para trabajar con cadenas de texto. Esto se debe principalmente a la capacidad de acceder a un carácter específico mediante su índice. A continuación se muestra un ejemplo de código que imprime las letras de cada palabra en líneas separadas: +Con la ayuda de los ciclos se procesan números y se trabaja con cadenas. Una cadena está formada por caracteres, y a cada carácter se puede llegar por su índice. Los índices empiezan en cero, por eso el primer carácter de la cadena `"Arya"` tiene el índice `0`, y el último, el índice `length() - 1`. + +Obtener un carácter por su índice lo permite el método `charAt()`, y conocer la longitud de la cadena, el método `length()`. Con esos dos métodos y un contador basta para recorrer la cadena carácter a carácter. Abajo hay un ejemplo de código que imprime las letras de una palabra en líneas separadas: ```java public static void printNameBySymbol(String name) { var i = 0; - // Esta condición se evaluará hasta el final de la cadena, - // incluyendo el último carácter. Su índice es `length() - 1`. + // Esta comprobación se realiza hasta el final de la cadena, + // incluido el último carácter. Su índice es `length() - 1`. while (i < name.length()) { - // Accedemos al carácter mediante su índice + // Accedemos al carácter por su índice System.out.println(name.charAt(i)); - i += 1; + i = i + 1; } } @@ -20,9 +22,42 @@ App.printNameBySymbol(name); // "a" ``` -Lo más importante en este código es establecer la condición correcta en el `while`. Esto se puede hacer de dos formas: +El ciclo recorre cada carácter de la cadena por turnos: + +```text +"Arya" + │ │ │ │ + A r y a + ↓ ↓ ↓ ↓ +cada carácter se procesa por turno +``` + +Lo más importante en este código es poner la condición correcta en el `while`. Se puede hacer de dos maneras: * `i < name.length()` * `i <= name.length() - 1` -Ambas formas conducen al mismo resultado. +Las dos maneras llevan al mismo resultado. La primera aparece más a menudo, porque tiene menos aritmética. + +## El recorrido en orden inverso + +El contador no está obligado a crecer desde cero. Si se empieza por el último índice y se va disminuyendo hasta cero, la cadena se recorrerá al revés. Escribamos un método que imprime los caracteres de una palabra en orden inverso: + +```java +public static void printReversed(String name) { + // Empezamos por el último carácter + var i = name.length() - 1; + while (i >= 0) { + System.out.println(name.charAt(i)); + i = i - 1; + } +} + +App.printReversed("Arya"); +// "a" +// "y" +// "r" +// "A" +``` + +Aquí el contador `i` arranca con el valor `name.length() - 1`, se mueve hacia cero y termina el ciclo cuando pasa a ser menor que cero. En cada paso el método toma el carácter que está en el índice actual y lo imprime. La dirección del recorrido la marcan solo el valor inicial del contador y la forma de cambiarlo. diff --git a/modules/90-loops/250-iteration-over-strings/es/data.yml b/modules/90-loops/250-iteration-over-strings/es/data.yml index 4f77d746..f05bf29d 100644 --- a/modules/90-loops/250-iteration-over-strings/es/data.yml +++ b/modules/90-loops/250-iteration-over-strings/es/data.yml @@ -1,3 +1,5 @@ --- -name: Recorrido de caracteres -tips: [] +name: Recorrido de cadenas +tips: + - | + [Iteración](https://es.wikipedia.org/wiki/Iteraci%C3%B3n) diff --git a/modules/90-loops/300-conditions-inside-loops/en/EXERCISE.md b/modules/90-loops/300-conditions-inside-loops/en/EXERCISE.md new file mode 100644 index 00000000..db27d38c --- /dev/null +++ b/modules/90-loops/300-conditions-inside-loops/en/EXERCISE.md @@ -0,0 +1,9 @@ + +The method from the theory takes the case of the letters into account. That is, `A` and `a` are different characters from its point of view. Implement a variant of the same method so that the case of the letters does not matter: + +```java +App.countChars("HexlEt", 'e'); // 2 +App.countChars("HexlEt", 'E'); // 2 +``` + +* `Character.toLowerCase()` – converts a character to lower case diff --git a/modules/90-loops/300-conditions-inside-loops/en/README.md b/modules/90-loops/300-conditions-inside-loops/en/README.md new file mode 100644 index 00000000..c8b48d55 --- /dev/null +++ b/modules/90-loops/300-conditions-inside-loops/en/README.md @@ -0,0 +1,53 @@ +The body of a loop, like the body of a method, is a place where instructions are executed. That means that everything studied earlier works inside it, including conditional constructs. This way the program repeats one action many times, but on every repetition it makes a decision. + +The loop goes through the values one after another, and the condition inside the loop decides what to do with the current value. Let's look at a method that counts how many times a letter occurs in a sentence: + +```java +App.countChars("Fear cuts deeper than swords.", 'e'); // 4 +// If nothing was found, then the result is 0 matches +App.countChars("Sansa", 'y'); // 0 +``` + +First try to answer the questions: + +* Is this operation an aggregation? +* What will the check for the occurrence of the character be? + +And now let's look at the code: + +```java +public static int countChars(String str, char ch) { + var i = 0; + var count = 0; + while (i < str.length()) { + if (str.charAt(i) == ch) { + // We count only the suitable characters + count = count + 1; + } + // The counter increases in any case + i = i + 1; + } + + return count; +} +``` + +This is an aggregating task. The method counts not all the characters, but to count the total you still have to look at every one of them. The variable `count` stores the result and grows only when the current character matched the one we are looking for. + +## The counter and the condition are responsible for different things + +In such a loop it is convenient to separate two parts. The counter `i` moves the program to the next character, and the `if` decides what to do with the current one. The counter changes on every iteration, while the action inside the `if` is not always performed. + +This is important. If you increase `i` only inside the `if`, the loop will get stuck on the first unsuitable character, because the counter will stop growing and the condition `i < str.length()` will stay true forever. The program will loop endlessly. That is why the line `i = i + 1` stands outside the `if` and is executed with any outcome of the check. + +## The work step by step + +Let's take apart the call `countChars("Sansa", 'a')`. Before the loop `i` equals `0`, and `count` equals `0`. + +**Step 1.** The condition `i < str.length()` is true. The character with index `0` is `S`. It is not equal to `a`, the `if` block is not executed. Then `i` grows to `1`. + +**Step 2.** The condition is true again. The character with index `1` is `a`. It matched the one we are looking for, so `count` grows to `1`. Then `i` grows to `2`. + +Further on the loop checks every character. The suitable ones it counts, the rest it skips. When `i` becomes equal to the length of the string, the condition of the loop will become false, and the method will return the accumulated value of `count`. + +The condition inside a loop can check anything — the evenness of a number, a match of a character, the length of a string or the value of a variable. The main thing is that the counter keeps changing and the loop can finish. diff --git a/modules/90-loops/300-conditions-inside-loops/en/data.yml b/modules/90-loops/300-conditions-inside-loops/en/data.yml new file mode 100644 index 00000000..f3d80d0a --- /dev/null +++ b/modules/90-loops/300-conditions-inside-loops/en/data.yml @@ -0,0 +1,3 @@ +--- +name: Conditions inside the body of a loop +tips: [] diff --git a/modules/90-loops/300-conditions-inside-loops/es/README.md b/modules/90-loops/300-conditions-inside-loops/es/README.md index b861d06e..df7ee9f1 100644 --- a/modules/90-loops/300-conditions-inside-loops/es/README.md +++ b/modules/90-loops/300-conditions-inside-loops/es/README.md @@ -1,37 +1,53 @@ -El cuerpo de un bucle, al igual que el cuerpo de un método, es el lugar donde se ejecutan las instrucciones. Esto significa que podemos utilizar todo lo que hemos aprendido hasta ahora, incluyendo construcciones condicionales. +El cuerpo de un ciclo, igual que el cuerpo de un método, es el lugar donde se ejecutan instrucciones. Eso significa que dentro de él funciona todo lo estudiado antes, incluidas las construcciones condicionales. Así el programa repite una acción muchas veces, pero en cada repetición toma una decisión. -Veamos un método que cuenta cuántas veces aparece una letra en una oración: +El ciclo recorre los valores uno tras otro, y la condición dentro del ciclo decide qué hacer con el valor actual. Veamos un método que cuenta cuántas veces aparece una letra en una oración: ```java -countChars("El miedo corta más profundo que las espadas.", 'e'); // 4 -// Si no se encuentra ninguna coincidencia, el resultado es 0 -countChars("Sansa", 'y'); // 0 +App.countChars("Fear cuts deeper than swords.", 'e'); // 4 +// Si no se encontró nada, el resultado es 0 coincidencias +App.countChars("Sansa", 'y'); // 0 ``` -Primero, intenta responder las siguientes preguntas: +Primero intenta responder a estas preguntas: * ¿Es esta operación una agregación? -* ¿Cuál será la condición para verificar la aparición del carácter? +* ¿Cuál será la comprobación de la aparición del carácter? -Ahora veamos el fragmento de código: +Y ahora veamos el código: ```java public static int countChars(String str, char ch) { - var i = 0; - var count = 0; - while (i < str.length()) { - if (str.charAt(i) == ch) { - // Solo contamos los caracteres que coinciden - count = count + 1; + var i = 0; + var count = 0; + while (i < str.length()) { + if (str.charAt(i) == ch) { + // Contamos solo los caracteres que encajan + count = count + 1; + } + // El contador aumenta en cualquier caso + i = i + 1; } - // El contador se incrementa de todas formas - i = i + 1; - } - return count; + return count; } ``` -Este problema es una agregación. El método cuenta solo los caracteres deseados, pero aún así es necesario analizar cada carácter para calcular la suma total. +Es una tarea de agregación. El método no cuenta todos los caracteres, pero para calcular el total hay que mirar cada uno igualmente. La variable `count` guarda el resultado y crece solo cuando el carácter actual coincide con el buscado. -La diferencia clave de este bucle con los que hemos visto anteriormente es que tiene una condición dentro del cuerpo. La variable `count` solo se incrementa cuando el carácter actual coincide con el carácter esperado. De lo contrario, es un método agregado típico que devuelve la cantidad de caracteres deseados al código que lo llama. +## El contador y la condición responden de cosas distintas + +En un ciclo así conviene separar dos partes. El contador `i` lleva el programa al carácter siguiente, y el `if` decide qué hacer con el actual. El contador cambia en cada iteración, mientras que la acción dentro del `if` no se ejecuta siempre. + +Esto es importante. Si se aumenta `i` solo dentro del `if`, el ciclo se quedará atascado en el primer carácter que no encaje, porque el contador dejará de crecer y la condición `i < str.length()` seguirá siendo verdadera para siempre. El programa entrará en un ciclo infinito. Por eso la línea `i = i + 1` está fuera del `if` y se ejecuta con cualquier resultado de la comprobación. + +## El trabajo paso a paso + +Analicemos la llamada `countChars("Sansa", 'a')`. Antes del ciclo `i` es igual a `0` y `count` es igual a `0`. + +**Paso 1.** La condición `i < str.length()` es verdadera. El carácter con el índice `0` es `S`. No es igual a `a`, así que el bloque `if` no se ejecuta. Después `i` crece hasta `1`. + +**Paso 2.** La condición vuelve a ser verdadera. El carácter con el índice `1` es `a`. Coincide con el buscado, por eso `count` crece hasta `1`. Después `i` crece hasta `2`. + +Más adelante el ciclo comprueba cada carácter. Los que encajan los cuenta; los demás los salta. Cuando `i` pase a ser igual a la longitud de la cadena, la condición del ciclo se volverá falsa y el método devolverá el valor acumulado de `count`. + +La condición dentro del ciclo puede comprobar cualquier cosa: la paridad de un número, la coincidencia de un carácter, la longitud de una cadena o el valor de una variable. Lo principal es que el contador siga cambiando y que el ciclo pueda terminar. diff --git a/modules/90-loops/350-build-strings/en/EXERCISE.md b/modules/90-loops/350-build-strings/en/EXERCISE.md new file mode 100644 index 00000000..43db7632 --- /dev/null +++ b/modules/90-loops/350-build-strings/en/EXERCISE.md @@ -0,0 +1,2 @@ + +Implement the same method `reverse()`, but performing the traversal of the string not from the first element to the last one, but the other way around, from the last one to the first. The general structure of the function stays the same. What changes is the initial index, the condition of ending the loop, the assembling of the new string and the forming of the new index in the loop. diff --git a/modules/90-loops/350-build-strings/en/README.md b/modules/90-loops/350-build-strings/en/README.md new file mode 100644 index 00000000..2a29ad2b --- /dev/null +++ b/modules/90-loops/350-build-strings/en/README.md @@ -0,0 +1,31 @@ +One more use of loops is **building strings**. Such a task occurs quite often in programming. It comes down to ordinary aggregation through concatenation. + +There is one task that is popular at interviews — **reversing a string**. It can be solved in many different ways, but going through it character by character is exactly what is considered the basic one: + +```java +App.reverse("Hexlet"); // "telxeH" +``` + +The general idea of reversing is the following — you need to take the characters in turn from the beginning of the string and join them in reverse order. Let's check how this works: + +```java +public static String reverse(String str) { + var i = 0; + // The neutral element for strings is the empty string + var result = ""; + while (i < str.length()) { + // We join in reverse order + result = str.charAt(i) + result; + i += 1; + } + + return result; +} + +var name = "Bran"; +App.reverse(name); // "narB" +// Checking the neutral element +App.reverse(""); // "" +``` + +It is important to get a feel for how the string itself is assembled — every next character is attached to the resulting string on the left, and in the end the string turns out to be reversed. diff --git a/modules/90-loops/350-build-strings/en/data.yml b/modules/90-loops/350-build-strings/en/data.yml new file mode 100644 index 00000000..a6ff207c --- /dev/null +++ b/modules/90-loops/350-build-strings/en/data.yml @@ -0,0 +1,3 @@ +--- +name: Building strings in loops +tips: [] diff --git a/modules/90-loops/350-build-strings/es/README.md b/modules/90-loops/350-build-strings/es/README.md index a8929b6b..1c9c50b7 100644 --- a/modules/90-loops/350-build-strings/es/README.md +++ b/modules/90-loops/350-build-strings/es/README.md @@ -1,22 +1,22 @@ -Otro uso de los bucles es la **formación de cadenas**. Este tipo de tarea es común en programación y se reduce a una simple agregación mediante concatenación. +Otro uso de los ciclos es la **formación de cadenas**. Una tarea así aparece bastante a menudo en programación. Se reduce a la agregación habitual mediante concatenación. -Hay una tarea que es popular en las entrevistas: **invertir una cadena**. Se puede resolver de muchas formas diferentes, pero la forma más básica es recorrerla carácter por carácter: +Hay una tarea que es popular en las entrevistas: **invertir una cadena**. Se puede resolver de muchas maneras distintas, pero justamente el recorrido carácter a carácter se considera la básica: ```java App.reverse("Hexlet"); // "telxeH" ``` -La idea general de la inversión es tomar los caracteres uno por uno desde el principio de la cadena y unirlos en orden inverso. Veamos cómo funciona: +La idea general de la inversión es la siguiente: hay que tomar los caracteres por turnos desde el principio de la cadena y unirlos en orden inverso. Comprobemos cómo funciona: ```java public static String reverse(String str) { - var i = str.length() - 1; - // El elemento neutral para las cadenas es una cadena vacía + var i = 0; + // El elemento neutro para las cadenas es la cadena vacía var result = ""; - while (i >= 0) { - // Unir en orden inverso - result = result + str.charAt(i); - i -= 1; + while (i < str.length()) { + // Unimos en orden inverso + result = str.charAt(i) + result; + i += 1; } return result; @@ -24,8 +24,8 @@ public static String reverse(String str) { var name = "Bran"; App.reverse(name); // "narB" -// Comprobación del elemento neutral +// Comprobación del elemento neutro App.reverse(""); // "" ``` -Es importante comprender cómo se construye la cadena: cada carácter siguiente se une a la cadena resultante por la izquierda, y al final la cadena queda invertida. +Es importante captar cómo se construye la cadena en sí: cada carácter siguiente se pega a la cadena resultante por la izquierda, y al final la cadena queda invertida. diff --git a/modules/90-loops/400-syntax-sugar/en/EXERCISE.md b/modules/90-loops/400-syntax-sugar/en/EXERCISE.md new file mode 100644 index 00000000..34caac25 --- /dev/null +++ b/modules/90-loops/400-syntax-sugar/en/EXERCISE.md @@ -0,0 +1,10 @@ + +Implement the static method `App.filterString()`, which accepts a string and a character and returns a new string in which the passed character is removed in all of its positions. + +An example of a call: + +```java +var str = "If I look back I am lost"; +App.filterString(str, 'I'); // "f look back am lost" +App.filterString(str, 'o'); // "If I lk back I am lst" +``` diff --git a/modules/90-loops/400-syntax-sugar/en/README.md b/modules/90-loops/400-syntax-sugar/en/README.md new file mode 100644 index 00000000..d608c5aa --- /dev/null +++ b/modules/90-loops/400-syntax-sugar/en/README.md @@ -0,0 +1,66 @@ +Repeating constructs occur often in programming. In Java, as in many other languages, their notation can be shortened. Such simplifications are called **syntactic sugar**. They make writing code shorter and more pleasant, keeping the same result. + +## Shortened forms of assignment + +The value of a variable is often changed by adding or subtracting something, multiplying or dividing by a number. The basic variant looks like this: + +```java +index = index + 1; +count = count * 2; +total = total - 5; +price = price / 3; +``` + +Java allows you to write the same thing shorter, with the help of compound operators: + +```java +index += 1; // the same as index = index + 1 +count *= 2; // the same as count = count * 2 +total -= 5; // the same as total = total - 5 +price /= 3; // the same as price = price / 3 +``` + +Each such operator takes the current value of the variable, applies the operation and saves the result into the same variable. On the left there is always the name of the variable into which the new value will be written. + +## Sugar in loops + +In loops such shortenings occur especially often. In them the counter is usually changed and the result is accumulated: + +```java +var sum = 0; +var index = 1; + +while (index <= 5) { + sum += index; // the same as sum = sum + index + index += 1; // the same as index = index + 1 +} + +System.out.println(sum); // => 15 +``` + +Without the shortenings the body of the loop would be longer and more verbose: + +```java +while (index <= 5) { + sum = sum + index; + index = index + 1; +} +``` + +## Concatenating strings + +Compound operators work not only with numbers. For strings `+=` is suitable, because `+` joins strings: + +```java +var text = "Hello"; +text += " World"; // the same as text = text + " World" +// "Hello World" +``` + +That is why, when assembling a string in a loop, the counter is changed through `+=`, and the next character is appended to the result every time with the same operator. + +## The supported shortenings + +Almost every operator has a shortened form: `+=`, `-=`, `*=`, `/=`, `%=`. All of them work on one principle — they take the current value of the variable, apply the operation and put the result back. + +Changing a variable by one stands apart. The notation `index += 1` can be shortened to `index++`, and `index -= 1` to `index--`. These two operations are called increment and decrement, they have their own subtleties, and a separate conversation is dedicated to them. diff --git a/modules/90-loops/400-syntax-sugar/en/data.yml b/modules/90-loops/400-syntax-sugar/en/data.yml new file mode 100644 index 00000000..896dcf08 --- /dev/null +++ b/modules/90-loops/400-syntax-sugar/en/data.yml @@ -0,0 +1,10 @@ +--- +name: Syntactic sugar +tips: + - | + [Syntactic sugar](https://en.wikipedia.org/wiki/Syntactic_sugar) +definitions: + - name: Syntactic sugar + description: > + these are syntactic features whose use does not affect the behavior of the + program, but makes using the language more convenient for a human. diff --git a/modules/90-loops/400-syntax-sugar/es/README.md b/modules/90-loops/400-syntax-sugar/es/README.md index 8954ce28..58fa0ca7 100644 --- a/modules/90-loops/400-syntax-sugar/es/README.md +++ b/modules/90-loops/400-syntax-sugar/es/README.md @@ -1,9 +1,66 @@ -Las construcciones como `index = index + 1` se utilizan con bastante frecuencia en Java, por lo que los creadores del lenguaje agregaron una forma abreviada de escribirlo: `index += 1`. Estas abreviaciones se conocen como **azúcar sintáctico**, porque hacen que el proceso de escribir código sea un poco más fácil y agradable. +En programación aparecen a menudo construcciones repetidas. En Java, como en muchos otros lenguajes, su escritura se puede acortar. Esas simplificaciones se llaman **azúcar sintáctico**. Hacen que escribir código sea más corto y más agradable, conservando el mismo resultado. -Hay formas abreviadas para todas las operaciones aritméticas y para la concatenación de cadenas: +## Las formas abreviadas de asignación -* `a = a + 1` → `a += 1` -* `a = a - 1` → `a -= 1` -* `a = a * 2` → `a *= 2` -* `a = a / 1` → `a /= 1` -* `a = a + "foo"` → `a += "foo"` +A menudo el valor de una variable se cambia sumándole o restándole algo, multiplicándolo o dividiéndolo por un número. La variante básica se ve así: + +```java +index = index + 1; +count = count * 2; +total = total - 5; +price = price / 3; +``` + +Java permite escribir lo mismo de forma más corta, con la ayuda de los operadores compuestos: + +```java +index += 1; // lo mismo que index = index + 1 +count *= 2; // lo mismo que count = count * 2 +total -= 5; // lo mismo que total = total - 5 +price /= 3; // lo mismo que price = price / 3 +``` + +Cada uno de esos operadores toma el valor actual de la variable, aplica la operación y guarda el resultado en la misma variable. A la izquierda siempre está el nombre de la variable en la que se escribirá el nuevo valor. + +## El azúcar en los ciclos + +En los ciclos esas abreviaturas aparecen especialmente a menudo. En ellos normalmente se cambia el contador y se acumula el resultado: + +```java +var sum = 0; +var index = 1; + +while (index <= 5) { + sum += index; // lo mismo que sum = sum + index + index += 1; // lo mismo que index = index + 1 +} + +System.out.println(sum); // => 15 +``` + +Sin las abreviaturas, el cuerpo del ciclo sería más largo y más verboso: + +```java +while (index <= 5) { + sum = sum + index; + index = index + 1; +} +``` + +## La concatenación de cadenas + +Los operadores compuestos funcionan no solo con números. Para las cadenas sirve `+=`, porque `+` une cadenas: + +```java +var text = "Hello"; +text += " World"; // lo mismo que text = text + " World" +// "Hello World" +``` + +Por eso, al construir una cadena en un ciclo, el contador se cambia con `+=`, y al resultado se le añade cada vez el siguiente carácter con el mismo operador. + +## Las abreviaturas disponibles + +Casi todos los operadores tienen forma abreviada: `+=`, `-=`, `*=`, `/=`, `%=`. Todos funcionan según el mismo principio: toman el valor actual de la variable, aplican la operación y devuelven el resultado a su sitio. + +Aparte queda el cambio de una variable en una unidad. La escritura `index += 1` se puede acortar a `index++`, y `index -= 1`, a `index--`. Esas dos operaciones se llaman incremento y decremento, tienen sus matices y se les dedica una conversación aparte. diff --git a/modules/90-loops/400-syntax-sugar/es/data.yml b/modules/90-loops/400-syntax-sugar/es/data.yml index b862e281..0c599020 100644 --- a/modules/90-loops/400-syntax-sugar/es/data.yml +++ b/modules/90-loops/400-syntax-sugar/es/data.yml @@ -1,3 +1,10 @@ --- name: Azúcar sintáctico -tips: [] +tips: + - | + [Azúcar sintáctico](https://es.wikipedia.org/wiki/Az%C3%BAcar_sint%C3%A1ctico) +definitions: + - name: Azúcar sintáctico + description: > + son posibilidades sintácticas cuyo uso no afecta al comportamiento del + programa, pero hace que usar el lenguaje sea más cómodo para la persona. diff --git a/modules/90-loops/450-mutators/en/EXERCISE.md b/modules/90-loops/450-mutators/en/EXERCISE.md new file mode 100644 index 00000000..d693c29c --- /dev/null +++ b/modules/90-loops/450-mutators/en/EXERCISE.md @@ -0,0 +1,8 @@ + +Implement the static method `App.makeItFunny()`, which accepts a string and returns its copy in which every n-th element is converted to upper case. n is set as an input of the function. To determine every n-th element you will need the remainder of division `%`. Think about how it can be used. + +```java +var text = "I never look back"; +// Every third element +App.makeItFunny(text, 3); // "I NevEr LooK bAck" +``` diff --git a/modules/90-loops/450-mutators/en/README.md b/modules/90-loops/450-mutators/en/README.md new file mode 100644 index 00000000..3e293a2f --- /dev/null +++ b/modules/90-loops/450-mutators/en/README.md @@ -0,0 +1,53 @@ +Two operations migrated to Java from the C language: **increment** `++` and **decrement** `--`, which very often occur together with loops. + +These unary operations increase and decrease by one the number written into a variable: + +```java +var i = 0; +i++; // 0 +i++; // 1 + +i--; // 2 +i--; // 1 +``` + +Besides the postfix form, they also have a prefix one: + +```java +var i = 0; +++i; // 1 +++i; // 2 + +--i; // 1 +--i; // 0 +``` + +It seems that there is no difference between the postfix and prefix forms, but this is where the difficulties begin. Unlike all the other operations, increment and decrement not only return a value, but also **change the value of the variable**. + +When the prefix notation is used, first the variable is changed, and then the return happens. When the postfix notation is used it is the other way around: you can consider that first the return happens, and then the variable is changed. + +The rule works the same way for increment and decrement. For simplicity let's consider only increment: + +```java +var x = 5; + +System.out.println(++x); // => 6 +System.out.println(x); // => 6 + +System.out.println(x++); // => 6 +System.out.println(x); // => 7 +``` + +What happens in the code above: + +1. We print `++x` to the screen — the prefix increment. That is why first the value of the variable increased by 1, then the result was returned and printed to the screen +2. Since the value changed, `System.out.println(x)` printed 6 +3. Now we print `x++` to the screen — the postfix increment. That is why we got the value that the variable contained before it was increased by 1 +4. Since the value changed, `System.out.println(x)` printed 7 + +It becomes especially complicated when an increment is inserted inside other operations: `x = i++ - 7 + --h`. It is almost impossible to understand such code. + +Recommendations for use: + +* Never mix operations without side effects with operations with side effects. The same goes for methods +* Use increment and decrement only where there is no difference between the prefix and the postfix variant — separately from everything, on a separate line of code diff --git a/modules/90-loops/450-mutators/en/data.yml b/modules/90-loops/450-mutators/en/data.yml new file mode 100644 index 00000000..e9c949dc --- /dev/null +++ b/modules/90-loops/450-mutators/en/data.yml @@ -0,0 +1,6 @@ +--- +name: Increment and decrement +tips: + - > + [Division with a remainder + (%)](https://en.wikipedia.org/wiki/Euclidean_division) diff --git a/modules/90-loops/450-mutators/es/data.yml b/modules/90-loops/450-mutators/es/data.yml index caadb96c..12a60e98 100644 --- a/modules/90-loops/450-mutators/es/data.yml +++ b/modules/90-loops/450-mutators/es/data.yml @@ -2,5 +2,5 @@ name: Incremento y decremento tips: - > - [Operador de resto - (%)](https://es.wikipedia.org/wiki/Resto_(matem%C3%A1tica)) + [División con resto + (%)](https://es.wikipedia.org/wiki/Divisi%C3%B3n_euclidiana) diff --git a/modules/90-loops/500-return-from-loops/en/EXERCISE.md b/modules/90-loops/500-return-from-loops/en/EXERCISE.md new file mode 100644 index 00000000..7b4f27ac --- /dev/null +++ b/modules/90-loops/500-return-from-loops/en/EXERCISE.md @@ -0,0 +1,12 @@ + +Implement the static method `App.hasChar()`, which checks (taking the case into account) whether a string contains the specified letter. The method accepts two parameters: + +* The string +* The letter to search for + +```java +App.hasChar("Renly", 'R'); // true +App.hasChar("Renly", 'r'); // false +App.hasChar("Tommy", 'm'); // true +App.hasChar("Tommy", 'd'); // false +``` diff --git a/modules/90-loops/500-return-from-loops/en/README.md b/modules/90-loops/500-return-from-loops/en/README.md new file mode 100644 index 00000000..c1c96cda --- /dev/null +++ b/modules/90-loops/500-return-from-loops/en/README.md @@ -0,0 +1,58 @@ +Working with loops usually comes down to two scenarios. In the first one the result is accumulated during the iterations, and the work with it goes on after the loop. Such an approach is called aggregation, and reversing a string belongs to it. In the second one the loop is executed until the needed result is reached and finishes ahead of time. That is how, for example, the check of a prime number is arranged — a number that divides without a remainder only by itself and by one. + +Let's look at the algorithm of checking a number for primality. We will divide the number `x` we are interested in by all the numbers from the range from two to `x - 1` and look at the remainder. If no divisor that divides `x` without a remainder was found in this range, then we have a prime number in front of us. + +## Checking the primality of the number 5: a step-by-step analysis + +1. We take the number `x = 5`. We look for possible divisors in the range from 2 to `x - 1`, that is, from 2 to 4 +2. We divide 5 by 2. The remainder equals 1, we did not find a divisor, we continue +3. We divide 5 by 3. The remainder equals 2, we did not find a divisor, we continue +4. We divide 5 by 4. The remainder equals 1, we did not find a divisor, we finish going through + +The result: in the range from 2 to 4 not a single number was found that 5 divides by without a remainder. That means 5 is a prime number. + +It is enough to limit the search for divisors to half of the number. For example, 11 does not divide by 2, 3, 4, 5, and by numbers greater than its half it will divide even less. That means the algorithm can be optimized and the division can be checked only up to `x / 2`: + +```java +public static boolean isPrime(int number) { + if (number < 2) { + return false; + } + + var divider = 2; + + while (divider <= number / 2) { + if (number % divider == 0) { + return false; + } + + divider++; + } + + return true; +} + +App.isPrime(1); // false +App.isPrime(2); // true +App.isPrime(3); // true +App.isPrime(4); // false +``` + +*To be completely honest, checking the numbers up to the square root of `number` is enough to solve the task. But here it is important to focus on working with a condition inside a loop* + +The main technique of this lesson is exiting the method right from inside the loop: + +```text +while (...) { + if (condition) { + return value; ← exit from the method (and from the loop) + } + ... +} +───────────────────────────────── +Without return the loop reaches the end +``` + +The algorithm is built in such a way that, when dividing sequentially by the numbers up to `x / 2`, it is enough to find at least one divisor without a remainder. Then the passed argument is a composite number, and further calculations make no sense. The return of `false` stands in this place, and `return` immediately finishes both the loop and the whole method. + +And only if the loop worked through entirely and not a single divisor without a remainder was found, the execution will reach the last line. The method will return `true`, because the number turned out to be prime. diff --git a/modules/90-loops/500-return-from-loops/en/data.yml b/modules/90-loops/500-return-from-loops/en/data.yml new file mode 100644 index 00000000..b06d52ba --- /dev/null +++ b/modules/90-loops/500-return-from-loops/en/data.yml @@ -0,0 +1,10 @@ +--- +name: Returning from loops +tips: + - | + [List of prime numbers](https://en.wikipedia.org/wiki/List_of_prime_numbers) +definitions: + - name: Aggregation + description: >- + Accumulating the result during the iterations and working with it after the + loop. diff --git a/modules/90-loops/500-return-from-loops/es/README.md b/modules/90-loops/500-return-from-loops/es/README.md index d1ae8358..4983be53 100644 --- a/modules/90-loops/500-return-from-loops/es/README.md +++ b/modules/90-loops/500-return-from-loops/es/README.md @@ -1,14 +1,17 @@ +El trabajo con ciclos se reduce normalmente a dos escenarios. En el primero, el resultado se acumula durante las iteraciones y el trabajo con él se hace ya después del ciclo. Ese enfoque se llama agregación, y la inversión de una cadena pertenece a él. En el segundo, el ciclo se ejecuta hasta alcanzar el resultado necesario y termina antes de tiempo. Así está montada, por ejemplo, la comprobación de un número primo, que se divide sin resto solo por sí mismo y por uno. -El trabajo con bucles generalmente se reduce a dos escenarios: +Veamos el algoritmo de comprobación de la primalidad de un número. Dividiremos el número buscado `x` por todos los números del rango desde dos hasta `x - 1` y miraremos el resto. Si en ese rango no se encontró un divisor que divida `x` sin resto, entonces tenemos delante un número primo. -1. Agregación: acumular un resultado durante las iteraciones y trabajar con él después del bucle. Invertir una cadena es un ejemplo de este tipo de escenario. -2. Ejecutar el bucle hasta que se alcance un resultado deseado y salir. Por ejemplo, la tarea de buscar números primos. Recordemos que un número primo solo se divide exactamente por sí mismo y por uno. +## Comprobación de la primalidad del número 5: análisis paso a paso -Veamos un algoritmo simple para verificar si un número es primo. Intentaremos dividir el número buscado `x` por todos los números en el rango desde dos hasta `x - 1` y observar el resto de la división. Si no se encuentra ningún divisor en este rango que divida al número `x` sin dejar resto, entonces tenemos un número primo. +1. Tomamos el número `x = 5`. Los posibles divisores los buscamos en el rango desde 2 hasta `x - 1`, es decir, desde 2 hasta 4 +2. Dividimos 5 por 2. El resto es igual a 1, no encontramos divisor, continuamos +3. Dividimos 5 por 3. El resto es igual a 2, no encontramos divisor, continuamos +4. Dividimos 5 por 4. El resto es igual a 1, no encontramos divisor, terminamos el recorrido -Podemos notar que es suficiente verificar los números hasta `x - 1/2`. Por ejemplo, 11 no se divide por 2, 3, 4, 5. Pero garantizamos que no se dividirá por números mayores que la mitad de sí mismo. +Resultado: en el rango desde 2 hasta 4 no se encontró ningún número por el que 5 se divida sin resto. Eso significa que 5 es un número primo. -Por lo tanto, podemos realizar una pequeña optimización y verificar la división solo hasta `x / 2`: +La búsqueda de divisores basta con limitarla a la mitad del número. Por ejemplo, 11 no se divide por 2, 3, 4, 5, y por números mayores que su mitad todavía menos se dividirá. Eso significa que el algoritmo se puede optimizar y comprobar la división solo hasta `x / 2`: ```java public static boolean isPrime(int number) { @@ -35,6 +38,21 @@ App.isPrime(3); // true App.isPrime(4); // false ``` -El algoritmo está construido de tal manera que si durante la división secuencial por números hasta `x / 2` se encuentra al menos uno que divide sin dejar resto, entonces el argumento pasado no es un número primo y, por lo tanto, los cálculos posteriores no tienen sentido. En este punto, se debe devolver `false`. +*Si somos honestos hasta el final, para resolver la tarea basta con comprobar los números hasta la raíz cuadrada de `number`. Pero aquí lo importante es centrarse en el trabajo con la condición dentro del ciclo* -Y solo si el bucle se ejecuta por completo, se puede concluir que el número es primo, ya que no se encontró ningún número que divida al número sin dejar resto. +La técnica principal de esta lección es la salida del método directamente desde dentro del ciclo: + +```text +while (...) { + if (condición) { + return valor; ← salida del método (y del ciclo) + } + ... +} +───────────────────────────────── +Sin return el ciclo llega hasta el final +``` + +El algoritmo está construido de tal manera que, al dividir de forma secuencial por los números hasta `x / 2`, basta con encontrar al menos un divisor sin resto. Entonces el argumento que se pasó es un número compuesto, y los cálculos posteriores no tienen sentido. En ese lugar está la devolución de `false`, y `return` termina de inmediato tanto el ciclo como el método entero. + +Y solo si el ciclo se ejecutó por completo y no se encontró ningún divisor sin resto, la ejecución llegará a la última línea. El método devolverá `true`, porque el número resultó ser primo. diff --git a/modules/90-loops/500-return-from-loops/es/data.yml b/modules/90-loops/500-return-from-loops/es/data.yml index 2ad8fc5c..f5e08bfe 100644 --- a/modules/90-loops/500-return-from-loops/es/data.yml +++ b/modules/90-loops/500-return-from-loops/es/data.yml @@ -1,5 +1,10 @@ --- -name: Devolución de bucles +name: Devolución desde los ciclos tips: - | - [Lista de números primos](https://es.wikipedia.org/wiki/Números_primos) + [Números primos](https://es.wikipedia.org/wiki/N%C3%BAmero_primo) +definitions: + - name: Agregación + description: >- + Acumulación del resultado durante las iteraciones y trabajo con él después + del ciclo. diff --git a/modules/90-loops/550-for/en/EXERCISE.md b/modules/90-loops/550-for/en/EXERCISE.md new file mode 100644 index 00000000..111b5c9f --- /dev/null +++ b/modules/90-loops/550-for/en/EXERCISE.md @@ -0,0 +1,12 @@ + +Samwell discovered that his messages are being intercepted in the castle "The Twins" and read there. Because of this their attacks stopped being sudden. After thinking a little, he developed a program that would encrypt the messages by the following algorithm. It would take the text and swap every two consecutive characters in it. + +```java +App.encrypt("move"); // "omev" +App.encrypt("attack"); // "taatkc" +// If the number of characters is odd +// then the last character stays in its place +App.encrypt("go!"); // "og!" +``` + +Implement the static method `App.encrypt()`, which accepts the original message and returns the encrypted one. diff --git a/modules/90-loops/550-for/en/README.md b/modules/90-loops/550-for/en/README.md new file mode 100644 index 00000000..6d22f693 --- /dev/null +++ b/modules/90-loops/550-for/en/README.md @@ -0,0 +1,77 @@ +With the help of the `while` loop any traversal task is solved, but it stands out for its verbosity. For `while` you have to declare the counter separately, set the stopping condition separately and change the counter in the body of the loop separately. When there are few loops, this is tolerable. In real code loops occur at every step, and managing three parts manually is tiring, especially when the number of repetitions is known in advance. + +For such cases Java has the `for` loop. It gathers all three parts into one line. Let's look at the implementation of reversing a string through `for`: + +```java +public static String reverseString(String str) { + var result = ""; + for (var i = 0; i < str.length(); i++) { + result = str.charAt(i) + result; + } + + return result; +} + +App.reverseString("code"); // "edoc" +``` + +The notation `i++` increases the counter by one. It is the short form of `i = i + 1`, and in the header of a loop it occurs most often. The code itself can be described in words like this: + +> The loop with the index `i` starts with the value `0`, repeats while `i < str.length()`, and after every step increases `i` by one + +In the header of the `for` loop there are exactly three parts, separated by semicolons: + +1. The initial value of the counter. This code is executed once before the first iteration +2. The predicate — the condition of repetition. It is checked before every iteration, exactly as in `while` +3. Changing the counter. This code is executed at the end of every iteration + +In everything else the principle of work is the same as with `while`. + +## Analysis step by step + +Let's take apart how the loop works for the call `reverseString("go!")`. Before the loop `result` equals the empty string: + +```text +reverseString("go!") + +before the loop: result = "" + +i=0: charAt(0) = 'g' +result = 'g' + "" = "g" + +i=1: charAt(1) = 'o' +result = 'o' + "g" = "og" + +i=2: charAt(2) = '!' +result = '!' + "og" = "!og" +``` + +Every character is taken by its index from left to right and glued to the beginning of the result. That is why the string is assembled in reverse order. + +## Comparison with while + +One and the same traversal of a string with the two loops looks like this: + +```text +for (var i = 0; i < str.length(); i++) { var i = 0; + System.out.println(str.charAt(i)); while (i < str.length()) { +} System.out.println(str.charAt(i)); + i = i + 1; +│ │ } +└── the counter is set in one line └── the counter is spread over the body +``` + +The `for` loop keeps all the management of the counter in the header, so the body stays clean. When the number of repetitions is clear in advance, `for` reads better. When it is not clear in advance how many iterations there will be, for example when searching for a prime number, `while` is more convenient. + +Let's show one more example with a numeric counter. Let's add up the numbers from `0` to `9`: + +```java +var sum = 0; +for (var i = 0; i < 10; i++) { + sum = sum + i; +} + +System.out.println(sum); // => 45 +``` + +The counter starts from zero, the loop goes on while `i < 10`, and on every step `i` grows by one. Inside the body only the accumulation of the sum is left. diff --git a/modules/90-loops/550-for/en/data.yml b/modules/90-loops/550-for/en/data.yml new file mode 100644 index 00000000..86d9ddfa --- /dev/null +++ b/modules/90-loops/550-for/en/data.yml @@ -0,0 +1,8 @@ +--- +name: The For loop +tips: [] +definitions: + - name: Aggregation + description: >- + Accumulating the result during the iterations and working with it after the + loop. diff --git a/modules/90-loops/550-for/es/README.md b/modules/90-loops/550-for/es/README.md index bd7e839f..8228d8b9 100644 --- a/modules/90-loops/550-for/es/README.md +++ b/modules/90-loops/550-for/es/README.md @@ -1,28 +1,77 @@ -El ciclo `while` es ideal para situaciones en las que la cantidad de iteraciones no se conoce de antemano, por ejemplo, al buscar un número primo. +Con la ayuda del ciclo `while` se resuelve cualquier tarea de recorrido, pero lo distingue su verbosidad. Para `while` hay que declarar el contador aparte, indicar aparte la condición de parada y cambiar aparte el contador en el cuerpo del ciclo. Cuando hay pocos ciclos, es tolerable. En el código real los ciclos aparecen a cada paso, y gestionar tres partes a mano resulta agotador, sobre todo cuando la cantidad de repeticiones se conoce de antemano. -Cuando se conoce la cantidad de iteraciones, es preferible utilizar el ciclo `for`. Veamos la implementación de invertir una cadena utilizando el ciclo `for`: +Para esos casos Java tiene el ciclo `for`. Reúne las tres partes en una sola línea. Veamos la implementación de la inversión de una cadena con `for`: ```java public static String reverseString(String str) { var result = ""; - // El contador se incrementa utilizando el operador de incremento. - // Hablaremos sobre esta operación más adelante. for (var i = 0; i < str.length(); i++) { result = str.charAt(i) + result; } return result; } + +App.reverseString("code"); // "edoc" +``` + +La escritura `i++` aumenta el contador en una unidad. Es la forma corta de `i = i + 1`, y en la cabecera del ciclo es la que aparece más a menudo. El código en sí se puede describir con palabras así: + +> El ciclo con el índice `i` empieza con el valor `0`, se repite mientras `i < str.length()`, y después de cada paso aumenta `i` en una unidad + +En la cabecera del ciclo `for` hay exactamente tres partes, separadas por puntos y comas: + +1. El valor inicial del contador. Este código se ejecuta una sola vez antes de la primera iteración +2. El predicado: la condición de repetición. Se comprueba antes de cada iteración, exactamente igual que en `while` +3. El cambio del contador. Este código se ejecuta al final de cada iteración + +En lo demás, el principio de funcionamiento es el mismo que en `while`. + +## Análisis paso a paso + +Veamos cómo se ejecuta el ciclo en la llamada `reverseString("go!")`. Antes del ciclo, `result` es igual a la cadena vacía: + +```text +reverseString("go!") + +antes del ciclo: result = "" + +i=0: charAt(0) = 'g' +result = 'g' + "" = "g" + +i=1: charAt(1) = 'o' +result = 'o' + "g" = "og" + +i=2: charAt(2) = '!' +result = '!' + "og" = "!og" ``` -Este código se puede describir de la siguiente manera: +Cada carácter se toma por su índice de izquierda a derecha y se pega al principio del resultado. Por eso la cadena se construye en orden inverso. -> El ciclo con el índice `i` se repite mientras `i < str.length()`, y después de cada paso, `i` se incrementa en 1. +## Comparación con while + +Un mismo recorrido de una cadena con los dos ciclos se ve así: + +```text +for (var i = 0; i < str.length(); i++) { var i = 0; + System.out.println(str.charAt(i)); while (i < str.length()) { +} System.out.println(str.charAt(i)); + i = i + 1; +│ │ } +└── contador en una sola línea └── contador repartido por el cuerpo +``` -En la definición del ciclo `for` se encuentran: +El ciclo `for` mantiene toda la gestión del contador en la cabecera, por eso el cuerpo queda limpio. Cuando la cantidad de repeticiones se sabe de antemano, `for` se lee mejor. Cuando no está claro de antemano cuántas iteraciones habrá, por ejemplo al buscar un número primo, resulta más cómodo `while`. -1. El valor inicial del contador. Este código se ejecuta exactamente una vez antes de la primera iteración. -2. El predicado: la condición de repetición del ciclo. Se evalúa en cada iteración, al igual que en el `while`. -3. La descripción del cambio del contador. Este código se ejecuta al final de cada iteración. +Mostremos otro ejemplo con un contador numérico. Sumemos los números de `0` a `9`: + +```java +var sum = 0; +for (var i = 0; i < 10; i++) { + sum = sum + i; +} + +System.out.println(sum); // => 45 +``` -Por lo demás, el principio de funcionamiento es exactamente el mismo que el del ciclo `while`. +El contador empieza en cero, el ciclo avanza mientras `i < 10`, y en cada paso `i` crece en una unidad. Dentro del cuerpo queda solo la acumulación de la suma. diff --git a/modules/90-loops/550-for/es/data.yml b/modules/90-loops/550-for/es/data.yml index 11075cbd..455d2a69 100644 --- a/modules/90-loops/550-for/es/data.yml +++ b/modules/90-loops/550-for/es/data.yml @@ -1,3 +1,8 @@ --- name: Ciclo For tips: [] +definitions: + - name: Agregación + description: >- + Acumulación del resultado durante las iteraciones y trabajo con él después + del ciclo. diff --git a/modules/90-loops/600-debug/en/EXERCISE.md b/modules/90-loops/600-debug/en/EXERCISE.md new file mode 100644 index 00000000..5d1f9983 --- /dev/null +++ b/modules/90-loops/600-debug/en/EXERCISE.md @@ -0,0 +1,17 @@ +Implement the method `compress()`, which compresses a string with the RLE method (Run-Length Encoding). + +The algorithm is as follows: if a character repeats several times in a row, it is replaced with the character itself and the number of repetitions. Single characters are written without a number. + +```java +App.compress("aaabcccc"); // => "a3bc4" +App.compress("abcd"); // => "abcd" +App.compress("aabbaa"); // => "a2b2a2" +App.compress(""); // => "" +``` + +Hints: + +- go through the string and count how many identical characters go in a row; +- as soon as the character changes, append the previous character and its counter (if it is greater than one), and reset the counter; +- do not forget to process the last group of characters after the loop finishes; +- if something goes wrong, add debug printing of the values of `i`, the current character and the counter — this will help to see where the logic breaks. diff --git a/modules/90-loops/600-debug/en/README.md b/modules/90-loops/600-debug/en/README.md new file mode 100644 index 00000000..def8ca2c --- /dev/null +++ b/modules/90-loops/600-debug/en/README.md @@ -0,0 +1,86 @@ +Even for the most experienced developers code rarely works perfectly the first time. The more experienced a developer is, the more confidently they **debug** code, that is, analyze errors and eliminate them. + +The skill of debugging will not appear on its own. It has to be developed, and as early as possible. In the course of studying you will do exercises and practice, and over time analyzing errors will become a habit. + +## How to find an error in code + +Debugging by poking around takes a lot of time. It is much more productive to first understand what exactly went wrong, and then eliminate the cause. + +If the program crashed with an exception, the first thing to study is the **call stack** (stack trace). It contains the chain of method calls from the place of the error and up to the start of the program. Every entry points to a class, a method, a file and a line. From the stack you can see which methods were executed and where the problem occurred: + +```bash +Exception in thread "main" java.lang.ArithmeticException: / by zero + at App.divide(App.java:6) + at App.main(App.java:2) +``` + +In the first line there is the type of the exception and the error message: `ArithmeticException` with the explanation `/ by zero` (division by zero). Then goes the stack itself: the error occurred in the `divide` method on line 6, and it was called from `main` on line 2. From the names of the classes you can also understand whether the problem is in your code or in a connected library. + +**If you want to ask an experienced developer for advice, the first thing to show is the error message and the call stack.** A fragment of code without context says little, while the text of the error points to the cause right away. + +## Types of errors + +The most understandable errors are the **syntactic** ones. They occur when the code is written incorrectly: a bracket or a semicolon is missing, the quotes do not match. Such code does not compile, and the compiler points directly at the place: + +```bash +App.java:2: error: ';' expected + System.out.println("Hello") + ^ +``` + +**Runtime errors** are harder to fix. The code compiles, but when launched it crashes with an exception: division by zero, accessing `null`, going out of the bounds of a string or an array. They show up already during the work of the program, and the cause is not always where it crashed. + +The hardest to fight are **logical errors**. The program works through without exceptions, but produces an incorrect result. There is no error message — only unexpected output. For example, a method should count the sum, but counts the difference: + +```java +// The method should count the sum of the numbers, but counts the difference +public static int sum(int a, int b) { + return a - b; +} +``` + +## Ways of debugging + +At the base of any debugging method lies observing the variables during execution. Let's look at a method that counts the sum of the numbers from `start` to `finish`. With `start = 3` and `finish = 5` it should calculate `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; +} +``` + +The key variables here are `n` and `result`. To find the error you need to look at which values they take on every iteration. For this there are **visual debuggers**, built into code editors: they let you execute the program step by step, watching the variables in real time. + +At Hexlet, **debug printing** is used instead of a debugger. The principle is the same, only the values of the variables are printed with the ordinary `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 +``` + +The output shows that there is one iteration fewer than needed: the five (`finish`) did not get into the addition. The condition has `n < finish` instead of `n <= finish`. The sign `<` has to be replaced with `<=`. + +Beginning developers often get upset because of errors and consider themselves inattentive. Everyone has errors — both juniors and seniors. The difference is only in how confidently you find them. diff --git a/modules/90-loops/600-debug/en/data.yml b/modules/90-loops/600-debug/en/data.yml new file mode 100644 index 00000000..59b795df --- /dev/null +++ b/modules/90-loops/600-debug/en/data.yml @@ -0,0 +1,12 @@ +--- +name: Debugging +tips: + - | + [Debugging](https://en.wikipedia.org/wiki/Debugging) +definitions: + - name: Debugging + description: the process of finding and eliminating errors in a program. + - name: Call stack (stack trace) + description: >- + the chain of method calls that a program prints when an exception occurs; + it helps to find the place and the cause of the error. diff --git a/modules/90-loops/600-debug/es/EXERCISE.md b/modules/90-loops/600-debug/es/EXERCISE.md new file mode 100644 index 00000000..3acaa1fb --- /dev/null +++ b/modules/90-loops/600-debug/es/EXERCISE.md @@ -0,0 +1,17 @@ +Implementa el método `compress()`, que comprime una cadena con el método RLE (Run-Length Encoding). + +El algoritmo es el siguiente: si un carácter se repite varias veces seguidas, se sustituye por el propio carácter y la cantidad de repeticiones. Los caracteres sueltos se escriben sin número. + +```java +App.compress("aaabcccc"); // => "a3bc4" +App.compress("abcd"); // => "abcd" +App.compress("aabbaa"); // => "a2b2a2" +App.compress(""); // => "" +``` + +Pistas: + +- recorre la cadena y cuenta cuántos caracteres iguales van seguidos; +- en cuanto el carácter cambie, añade el carácter anterior y su contador (si es mayor que uno) y reinicia el contador; +- no te olvides de procesar el último grupo de caracteres después de terminar el ciclo; +- si algo no sale bien, añade impresión de depuración de los valores de `i`, del carácter actual y del contador: eso ayudará a ver dónde se rompe la lógica. diff --git a/modules/90-loops/600-debug/es/README.md b/modules/90-loops/600-debug/es/README.md new file mode 100644 index 00000000..f28b7706 --- /dev/null +++ b/modules/90-loops/600-debug/es/README.md @@ -0,0 +1,86 @@ +Incluso a los desarrolladores más experimentados el código les funciona pocas veces a la perfección desde el primer intento. Cuanto más experimentado es un desarrollador, con más seguridad **depura** el código, es decir, analiza los errores y los elimina. + +La habilidad de depurar no aparece por sí sola. Hay que desarrollarla, y cuanto antes, mejor. A lo largo del aprendizaje harás ejercicios 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é es exactamente lo que salió mal y después eliminar la causa. + +Si el programa se cayó con una excepción, lo primero que hay que estudiar es la **pila de llamadas** (stack trace). Contiene la cadena de llamadas a métodos desde el lugar del error hasta el arranque del programa. Cada entrada indica la clase, el método, el archivo y la línea. Por la pila se ve qué métodos se ejecutaron y dónde surgió el problema: + +```bash +Exception in thread "main" java.lang.ArithmeticException: / by zero + at App.divide(App.java:6) + at App.main(App.java:2) +``` + +En la primera línea está el tipo de la excepción y el mensaje de error: `ArithmeticException` con la aclaración `/ by zero` (división por cero). Después va la pila en sí: el error surgió en el método `divide`, en la línea 6, y lo llamaron desde `main`, en la línea 2. Por los nombres de las clases también se puede entender si el problema está en tu código o en una biblioteca conectada. + +**Si quieres pedir consejo a un desarrollador con experiencia, lo primero que debes mostrar es el mensaje de error y la pila de llamadas.** Un fragmento de código sin contexto dice poco, mientras que el texto del error orienta de inmediato hacia la causa. + +## Tipos de errores + +Los errores más comprensibles son los **sintácticos**. Surgen cuando el código está escrito de forma incorrecta: falta un paréntesis o un punto y coma, no coinciden las comillas. Ese código no compila, y el compilador indica directamente el lugar: + +```bash +App.java:2: error: ';' expected + System.out.println("Hello") + ^ +``` + +Más difíciles de corregir son los **errores de tiempo de ejecución**. El código compila, pero al ejecutarlo se cae con una excepción: división por cero, acceso a `null`, salida de los límites de una cadena o de un array. Se manifiestan ya durante el trabajo del programa, y la causa no siempre está allí donde se cayó. + +Lo más complicado es luchar con los **errores lógicos**. El programa se ejecuta sin excepciones, pero da un resultado incorrecto. No hay mensaje de error, solo una salida inesperada. Por ejemplo, un método debería calcular la suma, pero calcula la diferencia: + +```java +// El método debería calcular la suma de los números, pero calcula la diferencia +public static int sum(int a, int b) { + return a - b; +} +``` + +## Formas de depurar + +En la base de cualquier método de depuración está la observación de las variables durante la ejecución. Veamos un método que calcula la suma de los números desde `start` hasta `finish`. Con `start = 3` y `finish = 5` debería calcular `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; +} +``` + +Las variables clave aquí son `n` y `result`. Para encontrar el error hay que mirar qué valores toman en cada iteración. Para eso existen los **depuradores visuales**, integrados en los editores de código: permiten ejecutar el programa paso a paso, observando las variables en tiempo real. + +En 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 muestran con el habitual `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 +``` + +La salida muestra que hay una iteración menos de las que hacen falta: el cinco (`finish`) no entró en la suma. En la condición está `n < finish` en lugar de `n <= finish`. Hay que cambiar el signo `<` por `<=`. + +Los desarrolladores que empiezan se disgustan a menudo por los errores y se consideran despistados. Errores los tiene todo el mundo, tanto los junior como los senior. La diferencia está solo en la seguridad con la que los encuentras. diff --git a/modules/90-loops/600-debug/es/data.yml b/modules/90-loops/600-debug/es/data.yml new file mode 100644 index 00000000..f36704d5 --- /dev/null +++ b/modules/90-loops/600-debug/es/data.yml @@ -0,0 +1,13 @@ +--- +name: Depuración +tips: + - > + [Depuración de + programas](https://es.wikipedia.org/wiki/Depuraci%C3%B3n_de_programas) +definitions: + - name: Depuración + description: proceso de búsqueda y eliminación de errores en un programa. + - name: Pila de llamadas (stack trace) + description: >- + cadena de llamadas a métodos que el programa imprime cuando surge una + excepción; ayuda a encontrar el lugar y la causa del error.