From 84fdc823b686e193108ab72b879157f2b4c5a5c3 Mon Sep 17 00:00:00 2001 From: Nikolay Gagarinov Date: Tue, 4 Aug 2026 19:30:37 +0500 Subject: [PATCH] =?UTF-8?q?feat(45-logic):=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 создан для всех 6 уроков модуля + модульный description.en.yml - es пересинхронизирован под новый ru: * 10-bool-type — не было секций «Сравнение в программировании» и «Предикаты», идентификаторы esBebe/edad → isInfant/age * 20-combine-expressions — идентификаторы esPar/esPrimeraLetraMayuscula → isEven/isFirstLetterInUpperCase, калька «En ruso, esto se puede descifrar» * 25-logical-operators — не было секции «Таблицы истинности» и примера hasDiscount, вызовы шли до определения метода, идентификаторы с ñ * 28-logical-negation — была только вводная часть: не было «Двойного отрицания», «Приоритета», примера canDrive и законов де Моргана * 12-string-comparasion — в задании остались кириллические палиндромы («шалаш», «ага») → reconocer/ana для es, level/wow для en - es создан с нуля: 90-logical-expressions - добавлены отсутствовавшие в es tips/definitions (10-bool-type, 25, 28) Co-Authored-By: Claude Opus 5 (1M context) --- modules/45-logic/10-bool-type/en/EXERCISE.md | 9 ++ modules/45-logic/10-bool-type/en/README.md | 78 +++++++++++ modules/45-logic/10-bool-type/en/data.yml | 9 ++ modules/45-logic/10-bool-type/es/README.md | 78 ++++++----- modules/45-logic/10-bool-type/es/data.yml | 4 + .../12-string-comparasion/en/EXERCISE.md | 17 +++ .../12-string-comparasion/en/README.md | 122 ++++++++++++++++++ .../12-string-comparasion/en/data.yml | 2 + .../12-string-comparasion/es/EXERCISE.md | 16 +-- .../20-combine-expressions/en/EXERCISE.md | 7 + .../20-combine-expressions/en/README.md | 60 +++++++++ .../20-combine-expressions/en/data.yml | 2 + .../20-combine-expressions/es/README.md | 62 ++++----- .../25-logical-operators/en/EXERCISE.md | 19 +++ .../25-logical-operators/en/README.md | 86 ++++++++++++ .../45-logic/25-logical-operators/en/data.yml | 14 ++ .../25-logical-operators/es/README.md | 94 ++++++++------ .../45-logic/25-logical-operators/es/data.yml | 5 + .../28-logical-negation/en/EXERCISE.md | 11 ++ .../45-logic/28-logical-negation/en/README.md | 102 +++++++++++++++ .../45-logic/28-logical-negation/en/data.yml | 9 ++ .../45-logic/28-logical-negation/es/README.md | 102 +++++++++++++-- .../45-logic/28-logical-negation/es/data.yml | 4 + .../90-logical-expressions/en/EXERCISE.md | 10 ++ .../90-logical-expressions/en/README.md | 84 ++++++++++++ .../90-logical-expressions/en/data.yml | 15 +++ .../90-logical-expressions/es/EXERCISE.md | 10 ++ .../90-logical-expressions/es/README.md | 84 ++++++++++++ .../90-logical-expressions/es/data.yml | 15 +++ modules/45-logic/description.en.yml | 5 + 30 files changed, 1013 insertions(+), 122 deletions(-) create mode 100644 modules/45-logic/10-bool-type/en/EXERCISE.md create mode 100644 modules/45-logic/10-bool-type/en/README.md create mode 100644 modules/45-logic/10-bool-type/en/data.yml create mode 100644 modules/45-logic/12-string-comparasion/en/EXERCISE.md create mode 100644 modules/45-logic/12-string-comparasion/en/README.md create mode 100644 modules/45-logic/12-string-comparasion/en/data.yml create mode 100644 modules/45-logic/20-combine-expressions/en/EXERCISE.md create mode 100644 modules/45-logic/20-combine-expressions/en/README.md create mode 100644 modules/45-logic/20-combine-expressions/en/data.yml create mode 100644 modules/45-logic/25-logical-operators/en/EXERCISE.md create mode 100644 modules/45-logic/25-logical-operators/en/README.md create mode 100644 modules/45-logic/25-logical-operators/en/data.yml create mode 100644 modules/45-logic/28-logical-negation/en/EXERCISE.md create mode 100644 modules/45-logic/28-logical-negation/en/README.md create mode 100644 modules/45-logic/28-logical-negation/en/data.yml create mode 100644 modules/45-logic/90-logical-expressions/en/EXERCISE.md create mode 100644 modules/45-logic/90-logical-expressions/en/README.md create mode 100644 modules/45-logic/90-logical-expressions/en/data.yml create mode 100644 modules/45-logic/90-logical-expressions/es/EXERCISE.md create mode 100644 modules/45-logic/90-logical-expressions/es/README.md create mode 100644 modules/45-logic/90-logical-expressions/es/data.yml create mode 100644 modules/45-logic/description.en.yml diff --git a/modules/45-logic/10-bool-type/en/EXERCISE.md b/modules/45-logic/10-bool-type/en/EXERCISE.md new file mode 100644 index 00000000..4fbbfc95 --- /dev/null +++ b/modules/45-logic/10-bool-type/en/EXERCISE.md @@ -0,0 +1,9 @@ + +Implement the method `isPensioner()`, which accepts one parameter — a person's age — and checks whether it is the retirement age. A person who has reached the age of 60 and above is considered a pensioner. + +Examples of calls: + +```java +App.isPensioner(75); // true +App.isPensioner(18); // false +``` diff --git a/modules/45-logic/10-bool-type/en/README.md b/modules/45-logic/10-bool-type/en/README.md new file mode 100644 index 00000000..dc1968e0 --- /dev/null +++ b/modules/45-logic/10-bool-type/en/README.md @@ -0,0 +1,78 @@ +Besides arithmetic operations, from school mathematics we also know comparison operations, for example `5 > 4` or `3 < 1`. They exist in programming as well. Comparisons are often used in real tasks related to numbers. When we place an order in an online store, the system checks whether the user has enough money in their account. If the amount in the account is greater than or equal to the price of the product, the order is confirmed. If there are not enough funds, an error message appears. + +## Comparison in programming + +Let's start with an example in which two numbers are compared. We print the result of the comparison to the screen: + +```java +System.out.println(5 > 4); // => true +System.out.println(4 > 4); // => false +``` + +The result of a comparison is a value of type `boolean`. This type has only two possible variants, `true` and `false`. These are special values of the language, and they can be printed directly: + +```java +System.out.println(true); // => true +System.out.println(false); // => false +``` + +In practice they are rarely used this directly, but the logic of the program's behavior is built on top of them. We come across this every day, when we enter PIN codes and passwords, when we perform actions with different possible outcomes. All these variants are written inside the program in the form of conditional expressions. The program reasons roughly like this: *if it is this way, do one thing; if it is otherwise, do another*. + +The following comparison operations are available in Java: + +* `<` — less than +* `<=` — less than or equal to +* `>` — greater than +* `>=` — greater than or equal to +* `==` — equal to +* `!=` — not equal to + +Programming languages adapted all the mathematical comparison operations unchanged, except for the equality and inequality operators. In mathematics the usual `=` is used for equality, but in programming the `=` symbol assigns values to variables. That is why in Java comparison is done with the help of `==`. A few examples with the result printed to the screen: + +```java +System.out.println(5 >= 3); // => true +System.out.println(7 < 0); // => false +System.out.println(5 > 5); // => false +System.out.println(5 >= 5); // => true +System.out.println(2 == 5); // => false +System.out.println(2 != 5); // => true +``` + +Any comparison operation can be saved in a variable of type `boolean` and then printed: + +```java +boolean result = 5 > 4; +System.out.println(result); // => true +``` + +When the comparison contains hard-coded numbers, the operation seems meaningless. We already know its result, and it is always the same, because three is greater than two under any circumstances. The picture changes when the values come from outside. Let's write a method that accepts the age of a child and determines whether they are an infant. Children under one year old are considered infants: + +```java +public static boolean isInfant(int age) { + return age < 1; +} +``` + +With the single line of the method we write "return the value that results from the comparison `age < 1`". Depending on the argument that arrives, the comparison will be either true (`true`) or false (`false`). The method will return this result: + +```java +System.out.println(App.isInfant(3)); // => false +System.out.println(App.isInfant(1)); // => false +System.out.println(App.isInfant(0)); // => true +``` + +## Predicates + +When methods return the result of a comparison, they answer the question "yes" or "no". Such methods are called **predicates**. They are recognized by the fact that they return a logical value `true` or `false`. Often the name of a predicate contains a question or a statement that can be checked (`is`, `has`, `can`, `was`). Here is a method that checks whether a number is negative: + +```java +public static boolean isNegative(int number) { + // We check whether the number is less than zero + return number < 0; +} + +System.out.println(App.isNegative(-5)); // => true +System.out.println(App.isNegative(7)); // => false +``` + +The `isNegative` method gathers the condition inside and gives a short answer to the outside. This way the calculation is hidden behind an understandable name. diff --git a/modules/45-logic/10-bool-type/en/data.yml b/modules/45-logic/10-bool-type/en/data.yml new file mode 100644 index 00000000..b8fa805a --- /dev/null +++ b/modules/45-logic/10-bool-type/en/data.yml @@ -0,0 +1,9 @@ +--- +name: The logical type +tips: + - > + [The boolean type in + Java](https://docs.oracle.com/javase/specs/jls/se21/html/jls-4.html#jls-4.2.5) +definitions: + - name: The logical type (boolean) + description: 'a data type with two possible values: true and false.' diff --git a/modules/45-logic/10-bool-type/es/README.md b/modules/45-logic/10-bool-type/es/README.md index 717892d5..d38dfad6 100644 --- a/modules/45-logic/10-bool-type/es/README.md +++ b/modules/45-logic/10-bool-type/es/README.md @@ -1,64 +1,78 @@ -Además de las operaciones aritméticas, en matemáticas escolares también conocemos las operaciones de comparación, por ejemplo: +Además de las operaciones aritméticas, de las matemáticas escolares conocemos también las operaciones de comparación, por ejemplo `5 > 4` o `3 < 1`. También existen en programación. Las comparaciones se usan a menudo en tareas reales relacionadas con números. Cuando hacemos una compra en una tienda online, el sistema comprueba si al usuario le alcanza el dinero de su cuenta. Si el importe de la cuenta es mayor o igual que el precio del producto, el pedido se confirma. Si no hay fondos suficientes, aparece un mensaje de error. -```text -5 > 4 -``` +## La comparación en programación -Esto se lee como una pregunta: "¿Cinco es mayor que cuatro?". En este caso, la respuesta es "sí". En otros casos, la respuesta puede ser "no", por ejemplo, para la siguiente expresión: +Empecemos por un ejemplo en el que se comparan dos números. El resultado de la comparación lo mostramos en la pantalla: -```text -3 < 1 +```java +System.out.println(5 > 4); // => true +System.out.println(4 > 4); // => false ``` -Las operaciones de comparación no están limitadas a números. Se pueden comparar casi cualquier cosa, como cadenas de texto. Cuando ingresamos a un sitio web, se compara el nombre de usuario y la contraseña ingresados con los que están en la base de datos. Si hay una coincidencia, se realiza la autenticación. +El resultado de una comparación es un valor de tipo `boolean`. Este tipo tiene solo dos variantes posibles, `true` y `false`. Son valores especiales del lenguaje y se pueden mostrar directamente: -Los lenguajes de programación han adaptado todas las operaciones de comparación matemáticas prácticamente sin cambios. La única diferencia importante son los **operadores de igualdad y desigualdad**. +```java +System.out.println(true); // => true +System.out.println(false); // => false +``` -En matemáticas, se utiliza el signo de igual `=`, pero en programación esto no se encuentra con frecuencia. En muchos lenguajes, el símbolo `=` se utiliza para asignar valores a variables, por lo que se utiliza `==` para las comparaciones. +En la práctica se usan pocas veces así, de forma tan directa, pero sobre ellos se construye la lógica del comportamiento del programa. Nos topamos con esto cada día, cuando introducimos códigos PIN y contraseñas, cuando realizamos acciones con distintos desenlaces posibles. Todas esas variantes están escritas dentro del programa en forma de expresiones condicionales. El programa razona más o menos así: *si es así, haz una cosa; si es de otra manera, haz otra*. -Aquí tienes una lista de las operaciones de comparación en Java: +En Java están disponibles las siguientes operaciones de comparación: * `<` — menor que * `<=` — menor o igual que * `>` — mayor que * `>=` — mayor o igual que * `==` — igual que -* `!=` — no igual que +* `!=` — distinto de -Veamos algunos ejemplos de operaciones lógicas: +Los lenguajes de programación adaptaron todas las operaciones matemáticas de comparación sin cambios, excepto los operadores de igualdad y desigualdad. En matemáticas, para la igualdad se usa el habitual `=`, pero en programación el símbolo `=` asigna valores a las variables. Por eso en Java se compara con la ayuda de `==`. Unos cuantos ejemplos mostrando el resultado en la pantalla: -```text -5 > 4 -password == text +```java +System.out.println(5 >= 3); // => true +System.out.println(7 < 0); // => false +System.out.println(5 > 5); // => false +System.out.println(5 >= 5); // => true +System.out.println(2 == 5); // => false +System.out.println(2 != 5); // => true ``` -Ambos ejemplos son expresiones. El resultado de evaluar estas expresiones es uno de los dos valores especiales: +Cualquier operación de comparación se puede guardar en una variable de tipo `boolean` y después mostrarla: -* `true` — "verdadero" -* `false`— "falso" +```java +boolean result = 5 > 4; +System.out.println(result); // => true +``` -Este es un nuevo tipo de dato para nosotros, llamado **booleano**. Solo puede tener estos dos valores. Aquí tienes un ejemplo de código que lo utiliza: +Cuando en la comparación hay números fijos, la operación parece no tener sentido. Ya conocemos su resultado, y siempre es el mismo, porque tres es mayor que dos en cualquier circunstancia. El panorama cambia cuando los valores llegan de fuera. Escribamos un método que recibe la edad de un niño y determina si es un bebé. Se consideran bebés los niños de menos de un año: ```java -var resultado = 5 > 4; -System.out.println(resultado); // => true +public static boolean isInfant(int age) { + return age < 1; +} ``` -Intentemos escribir un método que tome la edad de un niño y determine si es un bebé. Se considera bebé a un niño menor de un año: +Con la única línea del método escribimos "devolver el valor que resulte de la comparación `age < 1`". Según el argumento que llegue, la comparación será verdadera (`true`) o falsa (`false`). El método devolverá ese resultado: ```java -// Un método que devuelve un booleano se llama predicado -// Por lo general, estos métodos tienen un prefijo como has, can, is, was, etc. -public static boolean esBebe(int edad) { - return edad < 1; -} +System.out.println(App.isInfant(3)); // => false +System.out.println(App.isInfant(1)); // => false +System.out.println(App.isInfant(0)); // => true ``` -Aprovechamos el hecho de que cualquier operación es una expresión. Por lo tanto, en una sola línea de la función, escribimos "devolver el resultado de la comparación `edad < 1`". +## Predicados -Dependiendo del parámetro recibido, la comparación será verdadera (`true`) o falsa (`false`). Finalmente, `return` devuelve ese resultado: +Cuando los métodos devuelven el resultado de una comparación, responden a la pregunta "sí" o "no". Esos métodos se llaman **predicados**. Se reconocen porque devuelven un valor lógico `true` o `false`. A menudo en el nombre de un predicado hay una pregunta o una afirmación que se puede comprobar (`is`, `has`, `can`, `was`). Aquí está un método que comprueba si un número es negativo: ```java -System.out.println(App.esBebe(3)); // => false -System.out.println(App.esBebe(0)); // => true +public static boolean isNegative(int number) { + // Comprobamos si el número es menor que cero + return number < 0; +} + +System.out.println(App.isNegative(-5)); // => true +System.out.println(App.isNegative(7)); // => false ``` + +El método `isNegative` reúne la condición dentro y hacia fuera entrega una respuesta corta. Así el cálculo se esconde detrás de un nombre comprensible. diff --git a/modules/45-logic/10-bool-type/es/data.yml b/modules/45-logic/10-bool-type/es/data.yml index a349d0db..74f7c254 100644 --- a/modules/45-logic/10-bool-type/es/data.yml +++ b/modules/45-logic/10-bool-type/es/data.yml @@ -1,5 +1,9 @@ --- name: Tipo lógico +tips: + - > + [El tipo boolean en + Java](https://docs.oracle.com/javase/specs/jls/se21/html/jls-4.html#jls-4.2.5) definitions: - name: Tipo lógico (boolean) description: >- diff --git a/modules/45-logic/12-string-comparasion/en/EXERCISE.md b/modules/45-logic/12-string-comparasion/en/EXERCISE.md new file mode 100644 index 00000000..607bec8e --- /dev/null +++ b/modules/45-logic/12-string-comparasion/en/EXERCISE.md @@ -0,0 +1,17 @@ + +Implement the method `isPalindrome()`, which determines whether a word is a palindrome or not. A palindrome is a word that reads the same in both directions. + +```java +App.isPalindrome("level"); // true +App.isPalindrome("wow"); // true +App.isPalindrome("hexlet"); // false + +// Words can be passed to the method in any case +App.isPalindrome("Wow"); // true +``` + +To determine a palindrome, you need to reverse the string and compare it with the original one. Use the `StringUtils.reverse()` method for this + +```java +StringUtils.reverse("mama"); // "amam" +``` diff --git a/modules/45-logic/12-string-comparasion/en/README.md b/modules/45-logic/12-string-comparasion/en/README.md new file mode 100644 index 00000000..ee0e98cf --- /dev/null +++ b/modules/45-logic/12-string-comparasion/en/README.md @@ -0,0 +1,122 @@ +Look at the code and try to answer what the values of these expressions are: + +```java +// What will the result be in these examples — `true` or `false`? + +"a" == "a"; +"a".toUpperCase() == "a".toUpperCase(); +``` + +The correct answer: in the first case `true`, in the second — `false`. Why? To answer this question you need to dive a little into how computers work. + + +In our programs we operate on data — numbers, strings, boolean values. We perform various operations — we store them in variables, multiply, divide, concatenate them. + +That is how a programmer sees their work. But inside the computer everything is a bit different. While running, the program gets access to and manipulates data through their addresses in memory: + +```java +// An area of memory is allocated for storing the variable +// The program remembers the address of this area and works with it internally +var name = "CodeBasics"; +// The program read the value of the variable at the address where the value is stored +System.out.println(name); +``` + +**Memory** is a large area for storing data, which is very similar to a warehouse. In memory, any value gets a number by which it can be retrieved and replaced. This number is the **address**. + +## Comparison by reference and by value + +Because of these technical peculiarities, the comparison of data with each other can be looked at in two ways: + +* *The same one* — the same piece of memory +* *The same kind* — identical values regardless of where the addresses point + +An example from real life: two identical glasses from one set. Despite being identical, they are still different glasses. + +Programming languages work with these notions differently. As in many other languages, in Java all data is divided into two large types: + +* Primitive data is compared by value, regardless of addresses +* Reference data is compared by addresses + +This is how primitive data works: + +```java +// The comparison goes by value, not by addresses +4 == 4; // true +true == true; // true +10.0 == 10.0; // true +``` + +Of the reference data we are so far familiar only with strings, but they work in a tricky way, so as an example let's look at arrays. Do not pay attention to the unfamiliar syntax. Just note that in this code seemingly identical things are not equal to each other: + +```java +// Creating arrays +int[] a = {1, 2} +int[] b = {1, 2} +// The values are identical, but the references are different +a == b; // false +``` + +## The peculiarities of strings + +Strings belong to reference data types, but they behave strangely: + +```java +// Comparison like with primitive data types +"hm" == "hm"; // true +// Comparison like with reference data types +"hexlet".toUpperCase() == "hexlet".toUpperCase(); // false +``` + +Programs constantly operate on strings, so the efficiency of working with them comes first. If a string always behaved like a reference type, then additional memory would be allocated for every value in the code: + +```java +// Without optimizations this expression would lead to a double allocation of memory +// One unit of memory for each "hm" +"hm" == "hm"; +``` + +But this does not happen. When Java meets an explicitly created string, a check is performed on whether such a string already exists in memory. + +If it does, it is reused; if not, it is created: + +```java +// Memory is allocated +var name1 = "Java"; +// Such a string already exists, so a reference to the already created string is substituted +// As a result, memory is saved +var name2 = "Java"; +// Comparison by reference +// Both variables point to one piece of memory +name1 == name2; // true +``` + +But if a string is returned from a method, then it is placed into its own area of memory with its own unique address: + +```java +// New memory is allocated in any case +var name1 = "java".toUpperCase(); // "JAVA" +// New memory is allocated in any case +var name2 = "java".toUpperCase(); // "JAVA" +name1 == name2; // false +``` + +It may seem that reference data brings nothing but problems. In fact, it is needed. This will become clear when we come across mutability in the future. + +In applied programming we compare strings by value more often than by reference. For this, the `equals()` method is built into strings: + +```java +var name1 = "java".toUpperCase(); // "JAVA" +var name2 = "java".toUpperCase(); // "JAVA" +name1.equals(name2); // true +``` + +Besides `equals()`, the `equalsIgnoreCase()` method is built into strings, which performs a check by value without taking the case into account: + +```java +var name1 = "java".toUpperCase(); // "JAVA" +var name2 = "java".toLowerCase(); // "java" +name1.equalsIgnoreCase(name2); // true +``` + +Sometimes comparing strings in Java behaves like comparing values, but never bet on that. When changing the code it is easy to forget to fix the check and get an error. Always use methods when you need to compare by value. diff --git a/modules/45-logic/12-string-comparasion/en/data.yml b/modules/45-logic/12-string-comparasion/en/data.yml new file mode 100644 index 00000000..7fea8d51 --- /dev/null +++ b/modules/45-logic/12-string-comparasion/en/data.yml @@ -0,0 +1,2 @@ +--- +name: Comparing strings diff --git a/modules/45-logic/12-string-comparasion/es/EXERCISE.md b/modules/45-logic/12-string-comparasion/es/EXERCISE.md index 96f5c2b9..37743115 100644 --- a/modules/45-logic/12-string-comparasion/es/EXERCISE.md +++ b/modules/45-logic/12-string-comparasion/es/EXERCISE.md @@ -1,17 +1,17 @@ -Implementa el método `isPalindrome()`, que determina si una palabra es un palíndromo o no. Un palíndromo es una palabra que se lee igual en ambos sentidos. +Implementa el método `isPalindrome()`, que determina si una palabra es un palíndromo o no. Un palíndromo es una palabra que se lee igual en los dos sentidos. ```java -App.isPalindrome("шалаш"); // true -App.isPalindrome("ага"); // true -App.isPalindrome("хекслет"); // false +App.isPalindrome("reconocer"); // true +App.isPalindrome("ana"); // true +App.isPalindrome("hexlet"); // false -// Las palabras pueden estar en cualquier caso -App.isPalindrome("Ага"); // true +// Las palabras se pueden pasar al método en cualquier combinación de mayúsculas y minúsculas +App.isPalindrome("Ana"); // true ``` -Para determinar si una palabra es un palíndromo, debes invertir la cadena y compararla con la original. Para esto, utiliza el método `StringUtils.reverse()` +Para determinar si una palabra es un palíndromo, hay que invertir la cadena y compararla con la original. Usa para eso el método `StringUtils.reverse()` ```java -StringUtils.reverse("мама"); // "амам" +StringUtils.reverse("mama"); // "amam" ``` diff --git a/modules/45-logic/20-combine-expressions/en/EXERCISE.md b/modules/45-logic/20-combine-expressions/en/EXERCISE.md new file mode 100644 index 00000000..e028e1b3 --- /dev/null +++ b/modules/45-logic/20-combine-expressions/en/EXERCISE.md @@ -0,0 +1,7 @@ + +Implement the method `isInternationalPhone()`, which checks the format of the given phone number. If the phone number starts with *+*, then it is the international format. + +```java +App.isInternationalPhone("89602223423"); // false +App.isInternationalPhone("+79602223423"); // true +``` diff --git a/modules/45-logic/20-combine-expressions/en/README.md b/modules/45-logic/20-combine-expressions/en/README.md new file mode 100644 index 00000000..fe4a81fc --- /dev/null +++ b/modules/45-logic/20-combine-expressions/en/README.md @@ -0,0 +1,60 @@ + +Logical operations are expressions. That means that **logical operations can be combined with other expressions**. Let's look at an example. Suppose we want to check whether a number is even. In programming, evenness is checked through the remainder of division by 2: + +* If the remainder is 0, then the number was even +* If the remainder is not 0, then the number was odd + +The remainder of division is a simple, but very important concept in arithmetic, algebra, number theory and cryptography. The idea is simple: you need to divide a number into several equal groups. If something is left at the end, that is the remainder of the division. + +We divide candies equally between people: + +* 7 candies, 2 people: 2 x 3 + **remainder 1**. That means 7 is not a multiple of 2 +* 21 candies, 3 people: 3 x 7 + **remainder 0**. That means 21 is a multiple of 3 +* 19 candies, 5 people: 5 x 3 + **remainder 4**. That means 19 is not a multiple of 5 + +In code the remainder is calculated with the help of the `%` operator: + +* `7 % 2` → `1` +* `21 % 3` → `0` +* `19 % 5` → `4` + +With its help let's write a method for checking evenness: + +```java +// Defined in the App class +public static boolean isEven(int number) { + return number % 2 == 0; +} + +App.isEven(10); // true +App.isEven(3); // false +``` + +In one expression we combined two operators: + +* `==` — the equality check +* `%` — the arithmetic operator of the remainder of division + +**The priority of arithmetic operations is higher than that of logical ones.** That means that first the arithmetic expression `number % 2` is calculated, and then the result takes part in the logical comparison. + +In plain words it can be read like this: "You need to calculate the remainder of the division of the number `number` by 2 and compare it with zero; then return the result of the comparison". + +Let's look at one more example. Let's write a method that accepts a string and checks whether the first letter is uppercase. The algorithm of actions will be as follows: + +1. We get the first character of the argument string and store it in a variable +2. We compare whether the character is equal to its uppercase version +3. We return the result + +And this is what the implementation in code will look like: + +```java +public static boolean isFirstLetterInUpperCase(String string) { + var firstLetter = string.charAt(0); + // The Character class contains various methods for working with a character + // The isUpperCase() method checks that the passed character is in upper case + return Character.isUpperCase(firstLetter); +} + +App.isFirstLetterInUpperCase("marmont"); // false +App.isFirstLetterInUpperCase("Robb"); // true +``` diff --git a/modules/45-logic/20-combine-expressions/en/data.yml b/modules/45-logic/20-combine-expressions/en/data.yml new file mode 100644 index 00000000..2c493291 --- /dev/null +++ b/modules/45-logic/20-combine-expressions/en/data.yml @@ -0,0 +1,2 @@ +--- +name: Combining operations and methods diff --git a/modules/45-logic/20-combine-expressions/es/README.md b/modules/45-logic/20-combine-expressions/es/README.md index 8f5d2fc5..86e9adca 100644 --- a/modules/45-logic/20-combine-expressions/es/README.md +++ b/modules/45-logic/20-combine-expressions/es/README.md @@ -1,60 +1,60 @@ -Las operaciones lógicas son expresiones. Esto significa que **las operaciones lógicas se pueden combinar con otras expresiones**. Veamos un ejemplo. Supongamos que queremos verificar si un número es par. En programación, la paridad se verifica mediante el residuo de la división por 2: +Las operaciones lógicas son expresiones. Eso significa que **las operaciones lógicas se pueden combinar con otras expresiones**. Veámoslo con un ejemplo. Supongamos que queremos comprobar la paridad de un número. En programación, la paridad se comprueba con el resto de la división por 2: -* Si el residuo es 0, entonces el número es par -* Si el residuo no es 0, entonces el número es impar +* Si el resto es 0, el número era par +* Si el resto no es 0, el número era impar -El residuo de la división es un concepto simple pero muy importante en aritmética, álgebra, teoría de números y criptografía. La idea es simple: se debe dividir un número en grupos iguales. Si queda algo al final, eso es el residuo de la división. +El resto de la división es un concepto simple, pero muy importante en aritmética, álgebra, teoría de números y criptografía. La idea es sencilla: hay que dividir un número en varios grupos iguales. Si al final queda algo, eso es el resto de la división. -Dividimos caramelos entre personas: +Repartimos caramelos a partes iguales entre personas: -* 7 caramelos, 2 personas: 2 x 3 + **residuo 1**. Esto significa que 7 no es divisible por 2 -* 21 caramelos, 3 personas: 3 x 7 + **residuo 0**. Esto significa que 21 es divisible por 3 -* 19 caramelos, 5 personas: 5 x 3 + **residuo 4**. Esto significa que 19 no es divisible por 5 +* 7 caramelos, 2 personas: 2 x 3 + **resto 1**. Significa que 7 no es múltiplo de 2 +* 21 caramelos, 3 personas: 3 x 7 + **resto 0**. Significa que 21 es múltiplo de 3 +* 19 caramelos, 5 personas: 5 x 3 + **resto 4**. Significa que 19 no es múltiplo de 5 -En el código, el residuo se calcula utilizando el operador `%`: +En el código, el resto se calcula con la ayuda del operador `%`: * `7 % 2` → `1` * `21 % 3` → `0` * `19 % 5` → `4` -Usando esto, escribiremos un método para verificar la paridad: +Con su ayuda escribiremos un método para comprobar la paridad: ```java // Definido en la clase App -public static boolean esPar(int numero) { - return numero % 2 == 0; +public static boolean isEven(int number) { + return number % 2 == 0; } -App.esPar(10); // true -App.esPar(3); // false +App.isEven(10); // true +App.isEven(3); // false ``` -En una sola expresión hemos combinado dos operadores lógicos: +En una sola expresión hemos combinado dos operadores: -* `==` - verificación de igualdad -* `%` - operador aritmético de residuo de la división +* `==` — comprobación de igualdad +* `%` — operador aritmético del resto de la división -**La prioridad de las operaciones aritméticas es mayor que la de las operaciones lógicas**. Esto significa que primero se calcula la expresión aritmética `numero % 2`, y luego el resultado se utiliza en la comparación lógica. +**La prioridad de las operaciones aritméticas es mayor que la de las lógicas.** Eso significa que primero se calcula la expresión aritmética `number % 2`, y después el resultado participa en la comparación lógica. -En ruso, esto se puede descifrar de la siguiente manera: "Es necesario calcular el residuo de la división del número `numero` por 2 y compararlo con cero; luego devolver el resultado de la comparación". +En palabras, se puede leer así: «Hay que calcular el resto de la división del número `number` por 2 y compararlo con cero; después devolver el resultado de la comparación». -Veamos otro ejemplo. Escribiremos un método que toma una cadena y verifica si la primera letra es mayúscula. El algoritmo será el siguiente: +Veamos otro ejemplo. Escribamos un método que recibe una cadena y comprueba si la primera letra es mayúscula. El algoritmo de acciones será el siguiente: -1. Obtener y guardar en una variable el primer carácter de la cadena argumento -2. Comparar si el carácter es igual a su versión en mayúscula -3. Devolver el resultado +1. Obtenemos y guardamos en una variable el primer carácter de la cadena que llega como argumento +2. Comparamos si el carácter es igual a su versión en mayúscula +3. Devolvemos el resultado -La implementación en código se verá así: +Y así se verá la implementación en el código: ```java -public static boolean esPrimeraLetraMayuscula(String cadena) { - var primeraLetra = cadena.charAt(0); - // La clase Character contiene varios métodos para trabajar con caracteres - // El método isUpperCase() verifica si el carácter pasado está en mayúscula - return Character.isUpperCase(primeraLetra); +public static boolean isFirstLetterInUpperCase(String string) { + var firstLetter = string.charAt(0); + // La clase Character contiene distintos métodos para trabajar con caracteres + // El método isUpperCase() comprueba que el carácter que se le pasa está en mayúscula + return Character.isUpperCase(firstLetter); } -App.esPrimeraLetraMayuscula("marmont"); // false -App.esPrimeraLetraMayuscula("Robb"); // true +App.isFirstLetterInUpperCase("marmont"); // false +App.isFirstLetterInUpperCase("Robb"); // true ``` diff --git a/modules/45-logic/25-logical-operators/en/EXERCISE.md b/modules/45-logic/25-logical-operators/en/EXERCISE.md new file mode 100644 index 00000000..6b2964ca --- /dev/null +++ b/modules/45-logic/25-logical-operators/en/EXERCISE.md @@ -0,0 +1,19 @@ + +Implement the method `isLeapYear()`, which determines whether a year is a leap year or not. A year is a leap year if it is a multiple of (that is, divides without a remainder by) 400, or if it is at the same time a multiple of 4 and not a multiple of 100. As you can see, all the necessary logic is already contained in the definition; all that is left is to put it into code: + +```java +App.isLeapYear(2018); // false +App.isLeapYear(2017); // false +App.isLeapYear(2016); // true +``` + +Being a multiple can be checked like this: + +```java +// % - returns the remainder of dividing the left operand by the right one +// We check that number is a multiple of 10 +number % 10 == 0 + +// We check that number is not a multiple of 10 +number % 10 != 0 +``` diff --git a/modules/45-logic/25-logical-operators/en/README.md b/modules/45-logic/25-logical-operators/en/README.md new file mode 100644 index 00000000..fb5898ad --- /dev/null +++ b/modules/45-logic/25-logical-operators/en/README.md @@ -0,0 +1,86 @@ +We already know how to write methods that check single conditions. In this lesson we will continue working with methods and learn how to build compound conditions. + +Such skills come in handy in widespread tasks, for example when checking a password. Some sites, during registration, ask you to think up a password from 8 to 20 characters in length. + +In mathematics we would write this as `8 <= x <= 20`, but in Java that trick will not work. We will have to make two separate logical expressions and join them with the special "AND" operator. + +Let's write a method that accepts a password and says whether it matches the conditions. We print the result to the screen: + +```java +// The password is longer than 8 characters AND the password is shorter than 20 characters +public static boolean isCorrectPassword(String password) { + var length = password.length(); + return length > 8 && length < 20; +} + +System.out.println(App.isCorrectPassword("qwerty")); // => false +System.out.println(App.isCorrectPassword("qwerty1234")); // => true +``` + +The `&&` operator means "AND". In mathematical logic this is called conjunction. The whole expression is considered true only when every **operand** is true, that is, every one of the expressions it is made of. In other words, `&&` means "both this and that". The priority of this operator is lower than the priority of the comparison operators, so the expression `length > 8 && length < 20` works correctly without parentheses. + +Besides `&&`, the `||` operator is often used, which means "OR" (disjunction). It reads as "either this, or that, or both". The expression `a || b` is considered true when at least one of the operands is true or when both are true. In the remaining cases the expression is false. + +Let's look at a method that determines whether a person is entitled to a discount. The discount is given to those who are younger than 18 or are students: + +```java +public static boolean hasDiscount(int age, boolean isStudent) { + return age < 18 || isStudent; +} + +System.out.println(App.hasDiscount(15, false)); // => true (younger than 18) +System.out.println(App.hasDiscount(25, true)); // => true (a student) +System.out.println(App.hasDiscount(15, true)); // => true (both conditions) +System.out.println(App.hasDiscount(25, false)); // => false +``` + +Operators can be combined in any quantity and in any sequence. When `&&` and `||` occur in the code at the same time, it is better to set the priority with parentheses: + +```java +a && b || c; // Without parentheses it is hard to understand the priority +a && (b || c); // With parentheses it is clear what belongs to what +``` + +Let's look at one more example. Imagine that we want to buy an apartment that satisfies these conditions: + +> More than 100 m² on any street **OR** more than 80 m² on the central street *Main Street* + +Let's write a method that checks the apartment. It accepts two parameters, the area and the name of the street. We compare strings through `.equals(...)`, because for strings this is the correct way of checking equality: + +```java +public static boolean isGoodApartment(int area, String street) { + return area >= 100 || (area >= 80 && "Main Street".equals(street)); +} + +System.out.println(App.isGoodApartment(91, "Queens Street")); // => false +System.out.println(App.isGoodApartment(78, "Queens Street")); // => false +System.out.println(App.isGoodApartment(70, "Main Street")); // => false + +System.out.println(App.isGoodApartment(120, "Queens Street")); // => true +System.out.println(App.isGoodApartment(120, "Main Street")); // => true +System.out.println(App.isGoodApartment(80, "Main Street")); // => true +``` + +The parentheses around `area >= 80 && "Main Street".equals(street)` set the priority. Without them the rule would read differently, and this way you can see at once which part belongs to which operator. + +## Truth tables + +The area of mathematics in which logical operators are studied is called boolean algebra. **Truth tables** show what the result will be when each operator is applied. + +### The AND operator `&&` + +| A | B | A && B | +| ----- | ----- | ------- | +| true | true | **true** | +| true | false | false | +| false | true | false | +| false | false | false | + +### The OR operator `||` + +| A | B | A ❘❘ B | +| ----- | ----- | -------- | +| true | true | **true** | +| true | false | **true** | +| false | true | **true** | +| false | false | false | diff --git a/modules/45-logic/25-logical-operators/en/data.yml b/modules/45-logic/25-logical-operators/en/data.yml new file mode 100644 index 00000000..71bc1a4b --- /dev/null +++ b/modules/45-logic/25-logical-operators/en/data.yml @@ -0,0 +1,14 @@ +--- +name: Logical operators +tips: + - | + [Boolean algebra](https://en.wikipedia.org/wiki/Boolean_algebra) + - | + [Logical conjunction](https://en.wikipedia.org/wiki/Logical_conjunction) + - | + [Logical disjunction](https://en.wikipedia.org/wiki/Logical_disjunction) +definitions: + - name: Logical operators + description: >- + the "AND" (`&&`) and "OR" (`||`) operators, which make it possible to + create compound logical conditions. diff --git a/modules/45-logic/25-logical-operators/es/README.md b/modules/45-logic/25-logical-operators/es/README.md index 4133fd03..12d0c16f 100644 --- a/modules/45-logic/25-logical-operators/es/README.md +++ b/modules/45-logic/25-logical-operators/es/README.md @@ -1,70 +1,86 @@ +Ya sabemos escribir métodos que comprueban condiciones sueltas. En esta lección seguiremos trabajando con métodos y aprenderemos a construir condiciones compuestas. -Ya sabemos cómo escribir métodos que verifican condiciones individuales. En esta lección continuaremos trabajando con métodos y aprenderemos a construir condiciones compuestas. +Estas habilidades sirven en tareas muy extendidas, por ejemplo al comprobar una contraseña. Algunos sitios, durante el registro, piden pensar una contraseña de entre 8 y 20 caracteres de longitud. -Estas habilidades son útiles en tareas bastante comunes, como verificar contraseñas. Como sabes, algunos sitios web solicitan contraseñas de 8 a 20 caracteres de longitud durante el registro. +En matemáticas lo escribiríamos como `8 <= x <= 20`, pero en Java ese truco no funciona. Tendremos que hacer dos expresiones lógicas separadas y unirlas con el operador especial "Y". -En matemáticas, escribiríamos `8 <= x <= 20`, pero en Java esto no funciona. Tendremos que hacer dos expresiones lógicas separadas y combinarlas con el operador especial "Y". - -Escribamos un método que tome una contraseña y determine si cumple con las condiciones: +Escribamos un método que recibe una contraseña y dice si cumple las condiciones. El resultado lo mostramos en la pantalla: ```java -// La contraseña tiene más de 8 caracteres **Y** menos de 20 caracteres -public static boolean esContraseñaCorrecta(String contraseña) { - var longitud = contraseña.length(); - return longitud > 8 && longitud < 20; +// La contraseña tiene más de 8 caracteres Y la contraseña tiene menos de 20 caracteres +public static boolean isCorrectPassword(String password) { + var length = password.length(); + return length > 8 && length < 20; } -esContraseñaCorrecta("qwerty"); // false -esContraseñaCorrecta("qwerty1234"); // true +System.out.println(App.isCorrectPassword("qwerty")); // => false +System.out.println(App.isCorrectPassword("qwerty1234")); // => true ``` -El operador `&&` significa "Y". En este caso, la expresión se considera verdadera solo si cada *operando* es verdadero, es decir, cada una de las expresiones compuestas. En otras palabras, `&&` significa "y esto, y aquello". +El operador `&&` significa "Y". En lógica matemática esto se llama conjunción. Toda la expresión se considera verdadera solo cuando es verdadero cada **operando**, es decir, cada una de las expresiones que la componen. En otras palabras, `&&` significa "tanto lo uno como lo otro". La prioridad de este operador es menor que la de los operadores de comparación, por eso la expresión `length > 8 && length < 20` funciona correctamente sin paréntesis. -La prioridad de este operador es menor que la de los operadores de comparación, por lo que la expresión funciona correctamente sin paréntesis. +Además de `&&` se usa a menudo el operador `||`, que significa "O" (disyunción). Se lee como "o lo uno, o lo otro, o ambos". La expresión `a || b` se considera verdadera cuando es verdadero al menos uno de los operandos o cuando son verdaderos los dos. En los demás casos la expresión es falsa. -Además de `&&`, se utiliza con frecuencia el operador `||`, que significa "O". Significa "o esto, o aquello, o ambos". Los operadores se pueden combinar en cualquier cantidad y en cualquier secuencia. La única excepción es cuando se encuentran `&&` y `||` al mismo tiempo, en ese caso es mejor establecer la prioridad con paréntesis: +Veamos un método que determina si a una persona le corresponde un descuento. El descuento se da a quienes tienen menos de 18 años o son estudiantes: ```java -a && b || c; // Sin paréntesis es difícil entender la prioridad -a && (b || c) // La prioridad es obvia +public static boolean hasDiscount(int age, boolean isStudent) { + return age < 18 || isStudent; +} + +System.out.println(App.hasDiscount(15, false)); // => true (menor de 18) +System.out.println(App.hasDiscount(25, true)); // => true (estudiante) +System.out.println(App.hasDiscount(15, true)); // => true (las dos condiciones) +System.out.println(App.hasDiscount(25, false)); // => false ``` -Veamos otro ejemplo. Supongamos que queremos comprar un apartamento que cumpla con las siguientes condiciones: +Los operadores se pueden combinar en cualquier cantidad y en cualquier orden. Cuando en el código coinciden a la vez `&&` y `||`, es mejor indicar la prioridad con paréntesis: -> Más de 100 m^2 en cualquier calle **O** más de 80 m^2 en la calle central *Main Street* +```java +a && b || c; // Sin paréntesis es difícil entender la prioridad +a && (b || c); // Con paréntesis queda claro qué se refiere a qué +``` -Escribamos un método que verifique el apartamento. Toma dos parámetros: el área y el nombre de la calle: +Veamos otro ejemplo. Imaginemos que queremos comprar un apartamento que cumpla estas condiciones: -```java -esBuenApartamento(91, "Queens Street"); // false -esBuenApartamento(78, "Queens Street"); // false -esBuenApartamento(70, "Main Street"); // false +> Más de 100 m² en cualquier calle **O** más de 80 m² en la calle central *Main Street* -esBuenApartamento(120, "Queens Street"); // true -esBuenApartamento(120, "Main Street"); // true -esBuenApartamento(80, "Main Street"); // true +Escribamos un método que comprueba el apartamento. Recibe dos parámetros, el área y el nombre de la calle. Las cadenas las comparamos con `.equals(...)`, porque para las cadenas esa es la forma correcta de comprobar la igualdad: -public static boolean esBuenApartamento(int area, String calle) { - return area >= 100 || (area >= 80 && "Main Street".equals(calle)); +```java +public static boolean isGoodApartment(int area, String street) { + return area >= 100 || (area >= 80 && "Main Street".equals(street)); } + +System.out.println(App.isGoodApartment(91, "Queens Street")); // => false +System.out.println(App.isGoodApartment(78, "Queens Street")); // => false +System.out.println(App.isGoodApartment(70, "Main Street")); // => false + +System.out.println(App.isGoodApartment(120, "Queens Street")); // => true +System.out.println(App.isGoodApartment(120, "Main Street")); // => true +System.out.println(App.isGoodApartment(80, "Main Street")); // => true ``` -El área de las matemáticas que estudia los operadores lógicos se llama **álgebra booleana**. A continuación se muestran las "tablas de verdad" que nos permiten determinar el resultado de la aplicación de un operador: +Los paréntesis alrededor de `area >= 80 && "Main Street".equals(street)` marcan la prioridad. Sin ellos la regla se leería de otra manera, y así se ve enseguida qué parte pertenece a qué operador. + +## Tablas de verdad + +El área de las matemáticas en la que se estudian los operadores lógicos se llama álgebra booleana. Las **tablas de verdad** muestran cuál será el resultado al aplicar cada operador. -### Operador Y `&&` +### El operador Y `&&` | A | B | A && B | | ----- | ----- | ------- | -| TRUE | TRUE | **TRUE** | -| TRUE | FALSE | FALSE | -| FALSE | TRUE | FALSE | -| FALSE | FALSE | FALSE | +| true | true | **true** | +| true | false | false | +| false | true | false | +| false | false | false | -### Operador O `||` +### El operador O `||` | A | B | A ❘❘ B | | ----- | ----- | -------- | -| TRUE | TRUE | **TRUE** | -| TRUE | FALSE | **TRUE** | -| FALSE | TRUE | **TRUE** | -| FALSE | FALSE | FALSE | +| true | true | **true** | +| true | false | **true** | +| false | true | **true** | +| false | false | false | diff --git a/modules/45-logic/25-logical-operators/es/data.yml b/modules/45-logic/25-logical-operators/es/data.yml index b5dbbbac..cf27e49b 100644 --- a/modules/45-logic/25-logical-operators/es/data.yml +++ b/modules/45-logic/25-logical-operators/es/data.yml @@ -7,3 +7,8 @@ tips: [Conjunción](https://es.wikipedia.org/wiki/Conjunci%C3%B3n) - | [Disyunción](https://es.wikipedia.org/wiki/Disyunci%C3%B3n) +definitions: + - name: Operadores lógicos + description: >- + los operadores "Y" (`&&`) y "O" (`||`), que permiten crear condiciones + lógicas compuestas. diff --git a/modules/45-logic/28-logical-negation/en/EXERCISE.md b/modules/45-logic/28-logical-negation/en/EXERCISE.md new file mode 100644 index 00000000..afa68690 --- /dev/null +++ b/modules/45-logic/28-logical-negation/en/EXERCISE.md @@ -0,0 +1,11 @@ + +Implement the method `notToday()`, which checks that the passed date is not today's date: + +```java +// suppose today is 2012-11-25 +notToday("2012-11-25"); // false +notToday("2013-11-25"); // true +notToday("2013-09-01"); // true +``` + +To get the current date as a string: `LocalDate.now().toString()`. diff --git a/modules/45-logic/28-logical-negation/en/README.md b/modules/45-logic/28-logical-negation/en/README.md new file mode 100644 index 00000000..77aaca37 --- /dev/null +++ b/modules/45-logic/28-logical-negation/en/README.md @@ -0,0 +1,102 @@ +Along with the logical operators **AND** and **OR**, the "**negation**" operation is often used. It changes a logical value to the opposite one. In Java the unary operator `!` corresponds to negation: + +```java +System.out.println(!true); // => false +System.out.println(!false); // => true +``` + +If there is a method that checks whether a number is even, then with the help of negation you can perform a check for oddness: + +```java +public static boolean isEven(int number) { + return number % 2 == 0; +} + +System.out.println(App.isEven(10)); // => true +System.out.println(!App.isEven(10)); // => false +``` + +In the example above we added `!` to the left of the method call and got the opposite action. Negation makes it possible to express the intended rules in code without writing new methods. + +Negation is applied not only to a method call, but also to a whole expression. The parentheses set what exactly the operator refers to: + +```java +System.out.println(!(5 == 5 || 5 == 3)); // => false + +// This same expression can be written differently, +// "the number is not equal to 5 and not equal to 3" +System.out.println(5 != 5 && 5 != 3); // => false +``` + +## Double negation + +Negation is allowed to be put several times in a row. In logic, double negation is equivalent to the absence of negation: + +```java +System.out.println(!!true); // => true +System.out.println(!!false); // => false + +System.out.println(!!App.isEven(10)); // => true +System.out.println(!!App.isEven(11)); // => false +``` + +## Priority + +The `!` operator can be combined with `&&` and `||`. Among the logical operators, negation has the highest priority, so it is applied first: + +```java +// (!true) || true => false || true => true +System.out.println(!true || true); // => true + +// (!true) && false => false && false => false +System.out.println(!true && false); // => false +``` + +Parentheses change the order of evaluation, and `!` is applied to the result inside them: + +```java +// !(true || true) => !true => false +System.out.println(!(true || true)); // => false + +// !(true && false) => !false => true +System.out.println(!(true && false)); // => true +``` + +A practical example. The method checks whether a driver can get behind the wheel. A license and sobriety are needed, so the second condition goes through negation: + +```java +public static boolean canDrive(boolean hasLicense, boolean isDrunk) { + return hasLicense && !isDrunk; +} + +System.out.println(App.canDrive(true, false)); // => true (has a license, sober) +System.out.println(App.canDrive(true, true)); // => false (has a license, but drunk) +System.out.println(App.canDrive(false, false)); // => false (no license) +``` + +Now you know what the operators **AND**, **OR** and `!` mean. With their help you will be able to set compound conditions made of two and more logical expressions. + +## De Morgan's laws + +When working with complex logical expressions, sometimes you need to invert them or rewrite them into an equivalent form that is more convenient to read. For this there are **De Morgan's laws**, two rules that describe how negation is distributed over a compound expression: + +```java +!(A && B) == !A || !B +!(A || B) == !A && !B +``` + +The first law says that the negation of a conjunction is equal to the disjunction of the negations. Let's check both parts on specific values: + +```java +System.out.println(!(true && false)); // => true +System.out.println(!true || !false); // => true +``` + +The second law says that the negation of a disjunction is equal to the conjunction of the negations: + +```java +System.out.println(!(true || false)); // => false +System.out.println(!true && !false); // => false +``` + +In practice, De Morgan's laws help to simplify conditions. Instead of `!(isAdmin || isModerator)` you can write `!isAdmin && !isModerator`. The second variant reads as "not an administrator and not a moderator", and it is easier to follow. diff --git a/modules/45-logic/28-logical-negation/en/data.yml b/modules/45-logic/28-logical-negation/en/data.yml new file mode 100644 index 00000000..6668250a --- /dev/null +++ b/modules/45-logic/28-logical-negation/en/data.yml @@ -0,0 +1,9 @@ +--- +name: Negation +tips: + - | + [De Morgan's laws](https://en.wikipedia.org/wiki/De_Morgan%27s_laws) +definitions: + - name: Negation + description: > + a logical operation that changes a logical value to the opposite one. diff --git a/modules/45-logic/28-logical-negation/es/README.md b/modules/45-logic/28-logical-negation/es/README.md index 50fd0a61..ff7617cb 100644 --- a/modules/45-logic/28-logical-negation/es/README.md +++ b/modules/45-logic/28-logical-negation/es/README.md @@ -1,24 +1,102 @@ -Junto con la conjunción (**Y**) y la disyunción (**O**), a menudo se utiliza la operación de "**negación**". +Junto con los operadores lógicos **Y** y **O** se usa a menudo la operación de "**negación**". Cambia el valor lógico por el contrario. En Java a la negación le corresponde el operador unario `!`: -La negación cambia el valor lógico a su opuesto. En programación, se corresponde con el operador `!`. Si hay un método que verifica si un número es par, se puede realizar una verificación de impar utilizando la negación: +```java +System.out.println(!true); // => false +System.out.println(!false); // => true +``` + +Si hay un método que comprueba la paridad de un número, con la ayuda de la negación se puede hacer la comprobación de la imparidad: ```java -public static boolean esPar(int numero) { - return numero % 2 == 0; +public static boolean isEven(int number) { + return number % 2 == 0; } -esPar(10); // true -!esPar(10); // false +System.out.println(App.isEven(10)); // => true +System.out.println(!App.isEven(10)); // => false +``` + +En el ejemplo de arriba añadimos `!` a la izquierda de la llamada al método y obtuvimos la acción contraria. La negación permite expresar en el código las reglas que tenemos en mente sin escribir métodos nuevos. + +La negación se aplica no solo a la llamada de un método, sino también a una expresión completa. Los paréntesis marcan a qué se refiere exactamente el operador: + +```java +System.out.println(!(5 == 5 || 5 == 3)); // => false + +// Esta misma expresión se puede escribir de otra manera, +// "el número no es igual a 5 y no es igual a 3" +System.out.println(5 != 5 && 5 != 3); // => false +``` + +## La doble negación + +La negación se puede poner varias veces seguidas. En lógica, la doble negación equivale a la ausencia de negación: + +```java +System.out.println(!!true); // => true +System.out.println(!!false); // => false + +System.out.println(!!App.isEven(10)); // => true +System.out.println(!!App.isEven(11)); // => false +``` + +## La prioridad + +El operador `!` se puede combinar con `&&` y `||`. Entre los operadores lógicos, la negación tiene la prioridad más alta, por eso se aplica primero: + +```java +// (!true) || true => false || true => true +System.out.println(!true || true); // => true + +// (!true) && false => false && false => false +System.out.println(!true && false); // => false ``` -Es decir, simplemente agregamos `!` antes de llamar al método y obtenemos la acción opuesta. La negación se puede aplicar no solo a la llamada de un método, sino también a una expresión completa: +Los paréntesis cambian el orden de cálculo, y `!` se aplica ya al resultado que está dentro de ellos: ```java -!(x == 5 || x == 3) +// !(true || true) => !true => false +System.out.println(!(true || true)); // => false -// Esta misma expresión se puede escribir de otra manera: -// x no es igual a 5 y no es igual a 3 -x != 5 && x != 3 +// !(true && false) => !false => true +System.out.println(!(true && false)); // => true +``` + +Un ejemplo práctico. El método comprueba si un conductor puede ponerse al volante. Se necesita el carné y estar sobrio, por eso la segunda condición pasa por la negación: + +```java +public static boolean canDrive(boolean hasLicense, boolean isDrunk) { + return hasLicense && !isDrunk; +} + +System.out.println(App.canDrive(true, false)); // => true (tiene carné, está sobrio) +System.out.println(App.canDrive(true, true)); // => false (tiene carné, pero está borracho) +System.out.println(App.canDrive(false, false)); // => false (no tiene carné) +``` + +Ahora ya sabes qué significan los operadores **Y**, **O** y `!`. Con su ayuda podrás plantear condiciones compuestas a partir de dos o más expresiones lógicas. + +## Las leyes de De Morgan + +Al trabajar con expresiones lógicas complejas, a veces hace falta invertirlas o reescribirlas en una forma equivalente que resulte más cómoda de leer. Para eso existen las **leyes de De Morgan**, dos reglas que describen cómo se reparte la negación por una expresión compuesta: + +```java +!(A && B) == !A || !B +!(A || B) == !A && !B +``` + +La primera ley dice que la negación de la conjunción es igual a la disyunción de las negaciones. Comprobemos las dos partes con valores concretos: + +```java +System.out.println(!(true && false)); // => true +System.out.println(!true || !false); // => true +``` + +La segunda ley dice que la negación de la disyunción es igual a la conjunción de las negaciones: + +```java +System.out.println(!(true || false)); // => false +System.out.println(!true && !false); // => false ``` -La negación es una herramienta poderosa que permite expresar reglas de manera concisa en el código sin necesidad de escribir nuevos métodos. +En la práctica, las leyes de De Morgan ayudan a simplificar las condiciones. En lugar de `!(isAdmin || isModerator)` se puede escribir `!isAdmin && !isModerator`. La segunda variante se lee como "no es administrador y no es moderador", y es más fácil de seguir. diff --git a/modules/45-logic/28-logical-negation/es/data.yml b/modules/45-logic/28-logical-negation/es/data.yml index 5a382d2f..a4c80a60 100644 --- a/modules/45-logic/28-logical-negation/es/data.yml +++ b/modules/45-logic/28-logical-negation/es/data.yml @@ -3,3 +3,7 @@ name: Negación tips: - | [Leyes de De Morgan](https://es.wikipedia.org/wiki/Leyes_de_De_Morgan) +definitions: + - name: Negación + description: > + operación lógica que cambia el valor lógico por el contrario. diff --git a/modules/45-logic/90-logical-expressions/en/EXERCISE.md b/modules/45-logic/90-logical-expressions/en/EXERCISE.md new file mode 100644 index 00000000..88ff1fa1 --- /dev/null +++ b/modules/45-logic/90-logical-expressions/en/EXERCISE.md @@ -0,0 +1,10 @@ + +A registration form accepts the user's data. Before sending it, the program checks two conditions at once: the user is 18 years old and they have marked their consent to the processing of the data. + +The variable `age` holds the age `20`, and the variable `hasConsent` holds the value `true`. Join the check of being of age `age >= 18` and the consent `hasConsent` with the `&&` operator and print the result of the compound expression to the screen. + +Expected output: + +```text +true +``` diff --git a/modules/45-logic/90-logical-expressions/en/README.md b/modules/45-logic/90-logical-expressions/en/README.md new file mode 100644 index 00000000..41fdf80a --- /dev/null +++ b/modules/45-logic/90-logical-expressions/en/README.md @@ -0,0 +1,84 @@ +A single comparison like `age >= 18` answers one question. Real checks gather several such questions into one compound expression. A form on a site accepts data and checks at once that the user is of age and that they consented to the processing of their data. All of this fits into one line: + +```java +var age = 20; +var hasConsent = true; +System.out.println(age >= 18 && hasConsent); // => true +``` + +A compound logical expression in Java always evaluates to `true` or `false`. The intermediate parts also turn into logical values, and the final result is assembled from them step by step. + +## The order of evaluation + +Arithmetic, comparisons and logical operators occur in one expression. Java evaluates them in a strict order of priorities: + +```text +Priority (from high to low): + + () parentheses + ↓ + * / % + - arithmetic + ↓ + < <= > >= == != comparisons + ↓ + ! negation + ↓ + && logical AND + ↓ + || logical OR +``` + +First the arithmetic is calculated, then the comparisons give `true` or `false`, and only after that the logical operators join these values. The `&&` operator is true when both operands are true. The `||` operator is true when at least one is true. The `!` operator changes a value to the opposite one. + +## Evaluation step by step + +Let's take apart the expression from the example above. We have `age` equal to `20`, and `hasConsent` equal to `true`: + +```java +age >= 18 && hasConsent +20 >= 18 && true // we substituted the values of the variables +true && true // the comparison 20 >= 18 gave true +true // both operands of && are true +``` + +Let's take a more complex expression, where all three operators take part. We check that the number is in the range from 1 to 10 and at the same time is even: + +```java +var number = 4; +System.out.println(number >= 1 && number <= 10 && number % 2 == 0); // => true +``` + +Java unfolds it like this: + +```java +4 >= 1 && 4 <= 10 && 4 % 2 == 0 +true && true && 4 % 2 == 0 // two comparisons gave true +true && true && 0 == 0 // we calculated the remainder 4 % 2 +true && true && true // the last comparison gave true +true // all the operands of && are true +``` + +## Parentheses and priority + +When `&&` and `||` are mixed in an expression, the order is set by parentheses. Without them `&&` is applied earlier than `||`, and the meaning may turn out not to be the intended one: + +```java +// && binds b and c, so this is a || (b && c) +System.out.println(true || false && false); // => true + +// the parentheses change the order, first what is inside is calculated +System.out.println((true || false) && false); // => false +``` + +The first expression first evaluates `false && false`, gets `false`, and then `true || false` gives `true`. In the second case the parentheses force `true || false` to be evaluated first, this gives `true && false`, and the result is `false`. + +## Short-circuit evaluation + +Java stops as soon as the result is already clear. If the left operand of `||` is true, the right one is not evaluated, because the whole expression is already true. If the left operand of `&&` is false, the right one is skipped as well: + +```java +var hasAccess = true; +System.out.println(hasAccess || age < 0); // => true, Java does not evaluate the right part +``` + +Such behavior is called short-circuit evaluation. It speeds up checks and protects from unnecessary work in the right part of the expression. diff --git a/modules/45-logic/90-logical-expressions/en/data.yml b/modules/45-logic/90-logical-expressions/en/data.yml new file mode 100644 index 00000000..3a8ce462 --- /dev/null +++ b/modules/45-logic/90-logical-expressions/en/data.yml @@ -0,0 +1,15 @@ +--- +name: Logical expressions +tips: + - > + [Operators and priority in + Java](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/operators.html) +definitions: + - name: A compound logical expression + description: > + an expression made of several comparisons and logical operators, which + evaluates to one value, true or false. + - name: Short-circuit evaluation + description: > + skipping the right operand of an operator when the result of the + expression is already determined by the left operand. diff --git a/modules/45-logic/90-logical-expressions/es/EXERCISE.md b/modules/45-logic/90-logical-expressions/es/EXERCISE.md new file mode 100644 index 00000000..ef51e9a5 --- /dev/null +++ b/modules/45-logic/90-logical-expressions/es/EXERCISE.md @@ -0,0 +1,10 @@ + +Un formulario de registro recibe los datos del usuario. Antes de enviarlos, el programa comprueba a la vez dos condiciones: que el usuario tiene 18 años y que ha marcado el consentimiento para el tratamiento de los datos. + +En la variable `age` está la edad `20`, y en la variable `hasConsent` está el valor `true`. Une la comprobación de la mayoría de edad `age >= 18` y el consentimiento `hasConsent` con el operador `&&` y muestra en la pantalla el resultado de la expresión compuesta. + +Salida esperada: + +```text +true +``` diff --git a/modules/45-logic/90-logical-expressions/es/README.md b/modules/45-logic/90-logical-expressions/es/README.md new file mode 100644 index 00000000..d25e861e --- /dev/null +++ b/modules/45-logic/90-logical-expressions/es/README.md @@ -0,0 +1,84 @@ +Una comparación suelta como `age >= 18` responde a una sola pregunta. Las comprobaciones reales reúnen varias preguntas de ese tipo en una expresión compuesta. Un formulario de un sitio recibe los datos y comprueba a la vez que el usuario es mayor de edad y que ha dado su consentimiento para el tratamiento de los datos. Todo eso cabe en una línea: + +```java +var age = 20; +var hasConsent = true; +System.out.println(age >= 18 && hasConsent); // => true +``` + +Una expresión lógica compuesta en Java siempre se evalúa a `true` o a `false`. Las partes intermedias también se convierten en valores lógicos, y el resultado final se construye a partir de ellas paso a paso. + +## El orden de evaluación + +En una misma expresión coinciden aritmética, comparaciones y operadores lógicos. Java los evalúa en un orden estricto de prioridades: + +```text +Prioridad (de la más alta a la más baja): + + () paréntesis + ↓ + * / % + - aritmética + ↓ + < <= > >= == != comparaciones + ↓ + ! negación + ↓ + && Y lógico + ↓ + || O lógico +``` + +Primero se calcula la aritmética, después las comparaciones dan `true` o `false`, y solo entonces los operadores lógicos unen esos valores. El operador `&&` es verdadero cuando son verdaderos los dos operandos. El operador `||` es verdadero cuando es verdadero al menos uno. El operador `!` cambia el valor por el contrario. + +## La evaluación paso a paso + +Analicemos la expresión del ejemplo de arriba. Tenemos `age` igual a `20` y `hasConsent` igual a `true`: + +```java +age >= 18 && hasConsent +20 >= 18 && true // sustituimos los valores de las variables +true && true // la comparación 20 >= 18 dio true +true // los dos operandos de && son verdaderos +``` + +Tomemos una expresión más complicada, en la que participan los tres operadores. Comprobamos que el número está en el rango de 1 a 10 y además es par: + +```java +var number = 4; +System.out.println(number >= 1 && number <= 10 && number % 2 == 0); // => true +``` + +Java la despliega así: + +```java +4 >= 1 && 4 <= 10 && 4 % 2 == 0 +true && true && 4 % 2 == 0 // dos comparaciones dieron true +true && true && 0 == 0 // calculamos el resto 4 % 2 +true && true && true // la última comparación dio true +true // todos los operandos de && son verdaderos +``` + +## Los paréntesis y la prioridad + +Cuando en una expresión se mezclan `&&` y `||`, el orden lo marcan los paréntesis. Sin ellos, `&&` se aplica antes que `||`, y el sentido puede no ser el que se pretendía: + +```java +// && une b y c, por eso esto es a || (b && c) +System.out.println(true || false && false); // => true + +// los paréntesis cambian el orden, primero se calcula lo que está dentro +System.out.println((true || false) && false); // => false +``` + +La primera expresión calcula primero `false && false`, obtiene `false`, y después `true || false` da `true`. En el segundo caso los paréntesis obligan a calcular `true || false` primero, sale `true && false`, y el resultado es `false`. + +## La evaluación abreviada + +Java se detiene en cuanto el resultado ya está claro. Si el operando izquierdo de `||` es verdadero, el derecho no se evalúa, porque toda la expresión ya es verdadera. Si el operando izquierdo de `&&` es falso, el derecho también se salta: + +```java +var hasAccess = true; +System.out.println(hasAccess || age < 0); // => true, Java no calcula la parte derecha +``` + +A este comportamiento se le llama evaluación abreviada. Acelera las comprobaciones y protege del trabajo innecesario en la parte derecha de la expresión. diff --git a/modules/45-logic/90-logical-expressions/es/data.yml b/modules/45-logic/90-logical-expressions/es/data.yml new file mode 100644 index 00000000..fc99f0e0 --- /dev/null +++ b/modules/45-logic/90-logical-expressions/es/data.yml @@ -0,0 +1,15 @@ +--- +name: Expresiones lógicas +tips: + - > + [Operadores y prioridad en + Java](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/operators.html) +definitions: + - name: Expresión lógica compuesta + description: > + expresión formada por varias comparaciones y operadores lógicos, que se + evalúa a un único valor true o false. + - name: Evaluación abreviada + description: > + omisión del operando derecho de un operador cuando el resultado de la + expresión ya está determinado por el operando izquierdo. diff --git a/modules/45-logic/description.en.yml b/modules/45-logic/description.en.yml new file mode 100644 index 00000000..09ffa755 --- /dev/null +++ b/modules/45-logic/description.en.yml @@ -0,0 +1,5 @@ +--- + +name: Logic +description: | + Logical expressions make it possible to answer the questions that arise while a program is running. Is the user authenticated? Is the subscription paid? Is the year a leap year? In this module we study predicate functions — the ones that ask a question and answer it: is this true or false. We will practice writing such functions and move on to more complex logical expressions.