diff --git a/modules/35-methods-using/100-methods/ru/README.md b/modules/35-methods-using/100-methods/ru/README.md index 28d48d2..6bb5d15 100644 --- a/modules/35-methods-using/100-methods/ru/README.md +++ b/modules/35-methods-using/100-methods/ru/README.md @@ -1,45 +1,116 @@ -Сложение, конкатенация, нахождение остатка от деления и остальные ранее рассмотренные операции – все это довольно базовые возможности языков программирования. +Программирование нужно для того, чтобы выполнять самые разные операции. Иногда это простые действия, например сложение чисел или объединение строк. Но чаще это сложные процессы вроде перевода денег со счета на счет, оформления заказа в интернет-магазине, расчета налогов или подготовки отчета. -Математика не ограничена арифметикой, кроме нее есть и множество других разделов со своими операциями — например, геометрия. То же самое касается и строк: их можно переворачивать, менять регистр букв, удалять лишние символы — и это только самое простое. На более высоком уровне есть прикладная логика конкретного приложения. +Такие операции невозможно выразить одной командой. За действием вроде "перевести деньги" скрываются десятки, сотни и даже тысячи строк кода. Это проверка баланса, списание суммы, учет комиссии, обновление базы данных, отправка уведомления пользователю. -Программы списывают деньги, считают налоги, формируют отчеты. Количество подобных операций бесконечно и индивидуально для каждой программы. И все они должны быть как-то выражены в коде. +Чтобы управлять этим кодом и не теряться в деталях, в программировании придумали специальный механизм. Он объединяет блок кода в единое целое, прячет реализацию и позволяет сосредоточиться на смысле. Для программиста достаточно вызвать его и доверить ему всю внутреннюю работу. ## Как выражаются операции -Для выражения любой произвольной операции в программировании существует понятие **функция**. Функции бывают как встроенные в язык, так и добавленные программистом. С одной встроенной функцией мы уже знакомы — это `println()`. +Для выражения произвольной операции в большинстве языков программирования используют **функции**. Функция объединяет блок кода под одним именем. Снаружи все выглядит как одна команда, а сотни строк внутри остаются скрытыми. -Функции — одна из ключевых конструкций в программировании, без них невозможно сделать практически ничего. Сначала мы научимся пользоваться уже созданными функциями, а уже потом научимся создавать свои собственные. +Здесь нужно сделать оговорку. В Java невозможно создать обычную функцию, как это позволяет большинство других языков. Все функции в Java создаются только внутри классов, которые мы пока не разбирали. Функции, определенные внутри классов, принято называть **методами**. Дальше мы будем придерживаться этой терминологии. -Здесь нужно сделать небольшую оговорку. В Java невозможно создать обычную функцию, как это позволяет делать большинство других языков. Все функции Java создаются только внутри классов, которые мы пока не разбирали. А функции, которые определены внутри классов принято называть **методами**. Поэтому в дальнейшем мы будем придерживаться этой терминологии. +С одним методом мы уже знакомы — это `println()`. Он выводит данные на экран. Методы — одна из ключевых конструкций в программировании, без них не сделать почти ничего. Сначала мы научимся пользоваться готовыми методами, а уже потом создавать свои собственные. -Начнем с простых методов для работы над строками. Ниже пример вызова метода `length()`, который считает количество символов в строке: +Начнем с методов для работы со строками. Ниже вызов метода `length()`, который считает количество символов в строке: ```java "Hexlet".length(); // 6 -"ABBA".length(); // 4 +"ABBA".length(); // 4 ``` -**Методы** — это действия, которые нужно выполнить над данными, к которым они применяются. В программировании **объектами** называют данные, у которых есть методы. В реальности все чуть сложнее, но пока нам достаточно и такого определения. В Java все не примитивные (ссылочные) типы данных — это объекты. Рассмотрим еще несколько примеров с добавлением переменных: +В первой строке шесть символов, поэтому `"Hexlet".length()` вернет `6`. Во второй строке четыре символа, и результат равен `4`. + +**Методы** — это действия, которые выполняются над данными. В программировании **объектами** называют данные, у которых есть методы. В реальности все чуть сложнее, но пока такого определения достаточно. В Java все не примитивные (ссылочные) типы данных — это объекты, и строки относятся к ним. + +Посмотрим на структуру вызова. Сначала пишется сам объект, затем точка, потом **имя** метода и круглые **скобки**. Точка показывает, что метод вызывается у конкретного объекта. Скобки показывают, что это именно вызов, а не обращение к данным. + +Внутри скобок указываются **аргументы**, то есть данные, которые метод получает для работы. Их может быть несколько, один или вовсе ни одного. У метода `length()` аргументов нет, поэтому скобки пустые. + +## Откуда берутся методы + +Одни методы встроены в язык, другие создают сами программисты. + +**Встроенные методы** идут вместе с Java. Их можно использовать сразу, без дополнительных действий. Пример такого метода — `length()` у строки. Большое количество методов доступно у строк, чисел и других типов данных прямо из коробки. + +**Методы, которые создают программисты**, появляются тогда, когда нужно оформить свою логику в отдельный блок. Такой метод можно назвать любым именем и вызывать так же, как встроенный. Мы научимся этому позже. + +Кроме того, существуют методы из внешних библиотек. Чтобы ими пользоваться, библиотеку подключают с помощью механизма импорта. Подробно импорт мы пока не разбираем. Достаточно знать, что он добавляет в программу набор готовых методов. + +## Метод без аргументов + +Одним из часто используемых строковых методов является `length()`. Для строки он возвращает количество символов. + +```java +var message = "Hello!"; +var count = message.length(); +System.out.println(count); // => 6 +``` + +Здесь в строке `"Hello!"` шесть символов, поэтому вызов `message.length()` вернет число `6`. + +```text +Объект Метод Результат +┌──────────┐ ┌──────────┐ ┌──────────┐ +│ "Hello!" │ ──→ │ length() │ ──→ │ 6 │ +└──────────┘ └──────────┘ └──────────┘ +``` + +## Возврат значения + +Возврат значения — один из ключевых принципов работы методов. Благодаря ему мы соединяем результаты разных действий и строим более сложную логику. Если метод возвращает значение, то его можно сохранить в переменную, передать в другой метод или использовать в вычислениях. Именно так работает `length()`. Он считает количество символов и отдает результат наружу. + +```java +var message1 = "Hello!"; +var length1 = message1.length(); // сохраняем результат + +var message2 = "World!"; +var length2 = message2.length(); + +// используем результат в выражении +var combinedLength = length1 + length2; +System.out.println(combinedLength); // 12 +``` + +Методы почти никогда не выводят данные на экран, они их возвращают. Если бы `length()` сразу печатал результат, как делает `println()`, то мы бы увидели число, но не смогли бы его использовать дальше. Вот почему возврат значения настолько важен. Он связывает методы между собой. Одни возвращают данные, другие используют их в своей работе. Именно так из маленьких шагов строятся большие и сложные программы. + +В примерах выше результат каждого вызова записывается в переменную. Но это не обязательно, метод можно использовать напрямую: ```java -var company = "Hexlet"; +var message = "Hexlet"; +System.out.println(message.length()); // => 6 +``` + +## Метод с аргументами + +Некоторые методы принимают сразу несколько данных для работы. Пример — статический метод `Math.max()`, который возвращает большее из двух чисел. Первый аргумент задает одно число, второй задает другое. -var companyLength = company.length(); -System.out.println(companyLength); // => 6 +```java +// Выбираем большее из 2 и 3 +var result = Math.max(2, 3); +System.out.println(result); // => 3 -// Приводим к верхнему регистру -company.toUpperCase(); // "HEXLET" +// Выбираем большее из 10 и 7 +System.out.println(Math.max(10, 7)); // => 10 ``` -Основное в работе с методами – понять принцип возврата значения. Методы почти никогда не выводят данные на экран, они их возвращают. Благодаря этому свойству, мы можем разбить нашу программу на кусочки, из которых потом составляется что-то сложное. +Здесь метод вызывается не у строки, а у класса `Math` через точку. Такие методы называют **статическими**, они принадлежат самому классу. По структуре вызов с несколькими аргументами не отличается от вызова без них. Те же имя метода, скобки и аргументы через запятую внутри. + +## Параметры и аргументы -В примерах выше результат вызова каждого метода записывается в переменные. Но это не обязательно, мы можем использовать методы напрямую: +В разговорах о методах то и дело встречаются слова **параметры** и **аргументы**. Они связаны между собой, но обозначают разное. + +О **параметрах** говорят при создании метода. Параметром называют переменную внутри метода, в которую попадает переданное значение. Об **аргументах** говорят при вызове. Аргументом называют то, что мы передаем в метод. Это число, переменная или любое выражение. ```java -var company = "Hexlet"; -System.out.println(company.length()); // => 6 +// числа в качестве аргументов +System.out.println(Math.max(2, 3)); // => 3 + +var x = 2; +// аргумент может быть выражением, оно вычислится до передачи в метод +System.out.println(Math.max(x + 1, 3)); // => 3 ``` -Постепенно мы начнем знакомиться со все большим количеством встроенных методов в язык. Этих методов настолько много, что их невозможно запомнить. Хорошая новость в том, что это и не требуется. Никто не помнит названий методов наизусть. +Запоминать это не обязательно, но пригодится при чтении англоязычной литературы. -Главное — примерно представлять себе, что требуется, а дальше можно использовать подсказки редактора, документацию и Google. Программисты постоянно сидят в документации разбираясь с тем, как что работает. +Постепенно мы будем знакомиться со все большим количеством встроенных методов. Этих методов настолько много, что запомнить их невозможно. Хорошая новость в том, что это и не требуется. Никто не помнит названий методов наизусть. Главное — примерно представлять, что нужно, а дальше помогут подсказки редактора, документация и поиск. Программисты постоянно сидят в документации, разбираясь с тем, как что работает. diff --git a/modules/35-methods-using/100-methods/ru/data.yml b/modules/35-methods-using/100-methods/ru/data.yml index 03d90c7..d0f39e3 100644 --- a/modules/35-methods-using/100-methods/ru/data.yml +++ b/modules/35-methods-using/100-methods/ru/data.yml @@ -1,2 +1,13 @@ --- name: Методы и их вызов +tips: [] +definitions: + - name: Метод + description: >- + операция, которая принадлежит объекту или классу, способна принимать + данные и возвращать результат. Метод вызывается через точку, например + `"Hexlet".length()`. + - name: Аргумент + description: >- + информация, которую метод получает при вызове. Например, `Math.max(2, 3)` + передает методу `max()` аргументы `2` и `3`. diff --git a/modules/35-methods-using/110-methods-as-expressions/ru/README.md b/modules/35-methods-using/110-methods-as-expressions/ru/README.md index 1cdc016..219abf8 100644 --- a/modules/35-methods-using/110-methods-as-expressions/ru/README.md +++ b/modules/35-methods-using/110-methods-as-expressions/ru/README.md @@ -1,15 +1,15 @@ -В программировании выражение — нечто возвращающее результат, который можно использовать. - -Мы уже знаем достаточно много о выражениях и о принципах их построения. Сложение, вычитание, конкатенация, а также другие математические и строковые операции — все это выражения: +Когда мы пишем программы, нам нужно соединять действия друг с другом. Сложение чисел, объединение строк и работа с переменными — примеры того, как простые шаги комбинируются в более сложное поведение. ```java -1 + 5 * 3; -"He" + "Let"; -// Переменные могут быть частью выражения -rate * 5; +var rate = 10; +var hours = 5; +var salary = rate * hours + 100; +System.out.println(salary); // => 150 ``` -Особенность выражений в том, что они возвращают результат, который можно использовать — например, присвоить переменной или вывести на экран: +В программировании для этого используют понятие **выражение**. Так называют конструкцию, которая вычисляется и дает результат. В примере выше `rate * hours + 100` — это выражение. Оно составлено из переменных (`rate`, `hours`), числового литерала (`100`) и арифметических операций. Все вместе оно возвращает результат, который можно сохранить в переменную или использовать дальше. + +Особенность выражений в том, что их результат всегда можно применить — присвоить переменной, передать в метод или вывести на экран: ```java // Тут выражение это 1 + 5 @@ -17,37 +17,108 @@ var sum = 1 + 5; System.out.println(1 + 5); ``` -Но не все в программировании является выражением. Определение переменной — это инструкция, она не может быть частью выражения. То есть такой код выдаст ошибку: +Но не все в программировании является выражением. Определение переменной — это инструкция, она не может быть частью выражения. Поэтому такой код выдаст ошибку: ```java // Бессмысленный код, который не сработает 10 + var sum = 1 + 5; ``` -Как вы увидите дальше, выражения можно комбинировать, получая все более сложное поведение в самых неожиданных местах и неожиданным образом. Вы будете лучше понимать, как можно соединять части кода, чтобы получить нужный результат. +Выражения можно комбинировать бесконечно, постепенно усложняя логику. Каждое новое выражение становится частью большего: + +```java +var rate = 10; +var hours = 5; +var bonus = 50; +// Выражение из множества операций +var salary = (rate * hours + bonus) * 12 - 500; +System.out.println(salary); +``` + +Здесь несколько выражений объединены в одно, и результат стал еще сложнее. Именно так и строятся программы. Маленькие шаги складываются в большие конструкции. Поэтому в программировании невозможно заранее заучить все комбинации. Гораздо важнее понять, как выражения соединяются между собой в нужный результат. -Поговорим о методах. Вызов метода — это выражение или нет? Мы знаем, что методы возвращают результат, то есть да, они выражения. Из этого автоматически следует много интересного. +## Вызов метода как выражение -Например, мы можем использовать вызов метода прямо в математических операциях. Вот как можно получить индекс последнего символа в слове: +Поговорим о методах. Вызов метода — это выражение или нет? Мы знаем, что методы возвращают результат, значит да, вызов метода является выражением. Из этого автоматически следует много интересного. + +Например, мы можем использовать вызов метода прямо в математических операциях. Вот как получить индекс последнего символа в слове: ```java // Индексы начинаются с нуля var name = "Java"; -// Вызов метода и вычитание вместе! +// Вызов метода и вычитание вместе var lastIndex = name.length() - 1; System.out.println(lastIndex); // => 3 ``` -В этом коде нет нового синтаксиса. Мы всего лишь соединили уже известные части, опираясь на их природу. Можно пойти еще дальше: +В этом коде нет нового синтаксиса. Мы всего лишь соединили уже известные части, опираясь на их природу. Метод `length()` возвращает число `4`, из него вычитаем единицу и получаем `3`. Можно пойти еще дальше и встроить вызов прямо в вывод: ```java System.out.println(name.length() - 1); // => 3 ``` -Все это справедливо для любых методов, в том числе строковых: +## Выражения как аргументы методов + +Аргументом метода всегда является какое-то значение. Но значение можно не только записывать напрямую, его можно вычислять. А значит, в аргументы можно подставлять любые выражения. + +```java +// Здесь аргумент println — это число 150 +System.out.println(150); + +// А здесь аргумент — выражение, которое сначала вычисляется +System.out.println(10 * 15); // => 150 + +// Можно комбинировать еще сложнее +var rate = 10; +var hours = 15; +var bonus = 50; +System.out.println(rate * hours + bonus); // => 200 +``` + +Метод `println()` получает готовое значение и выводит его на экран. Способ получения этого значения методу безразличен. Поэтому вызовы методов отлично сочетаются с любыми выражениями. + +## Вызов метода внутри метода + +Раз вызов метода сам по себе является выражением, его результат можно сразу передать другому методу. Это позволяет строить еще более сложные конструкции: + +```java +var name = "Java"; + +// Вызов name.length() возвращает 4 +// Этот результат сразу используется как аргумент println() +System.out.println(name.length()); // => 4 +``` + +Здесь `name.length()` вычисляется первым и возвращает число `4`. Затем это значение подставляется в вызов `println()`. Чтобы правильно читать такие конструкции, нужно помнить про порядок вычисления. + +1. Сначала выполняется метод, который находится "внутри", в нашем случае `name.length()`. +2. Затем его результат подставляется на место вызова. +3. После этого выполняется внешний метод, в нашем случае `println()`. + +Код `System.out.println(name.length())` можно мысленно разложить так: + +```text +System.out.println("Java".length()) + +Шаг 1: "Java".length() → 4 +Шаг 2: println(4) → выводит 4 +``` + +Этот принцип работает всегда. Сначала вычисляются вложенные вызовы, потом внешний. + +## Методы в составе выражений + +Методы возвращают значения, поэтому их вызовы можно использовать как часть любых других выражений. Это справедливо для всех методов, в том числе строковых: ```java var name = "Java"; -// toUpperCase() – переводит слово в верхний регистр +// toUpperCase() переводит слово в верхний регистр System.out.println("Привет " + name.toUpperCase()); // => Привет JAVA + +// Можно использовать результат метода в арифметике +var text = "hexlet"; +var doubled = text.length() * 2; +System.out.println(doubled); // => 12 ``` + +Здесь вызовы `name.toUpperCase()` и `text.length()` — это полноценные выражения. Они возвращают значения, которые комбинируются со строками, числами, переменными и другими операциями. diff --git a/modules/35-methods-using/110-methods-as-expressions/ru/data.yml b/modules/35-methods-using/110-methods-as-expressions/ru/data.yml index e1ff3aa..2806f9b 100644 --- a/modules/35-methods-using/110-methods-as-expressions/ru/data.yml +++ b/modules/35-methods-using/110-methods-as-expressions/ru/data.yml @@ -1,2 +1,7 @@ --- name: Вызов метода — выражение +definitions: + - name: Выражение + description: >- + последовательность действий над данными, которая приводит к результату, + и его можно использовать дальше. diff --git a/modules/35-methods-using/115-string-immutability/ru/README.md b/modules/35-methods-using/115-string-immutability/ru/README.md index 2bb2ff9..257c341 100644 --- a/modules/35-methods-using/115-string-immutability/ru/README.md +++ b/modules/35-methods-using/115-string-immutability/ru/README.md @@ -6,15 +6,47 @@ company.toUpperCase(); // в верхний регистр System.out.println(company); // => ? ``` -Кажется, что ответом будет `"HEXLET"`, но это не так. Эта программа выведет `"hexlet"` (проверьте на [tryjshell](https://onecompiler.com/jshell)). Почему? +Кажется, что ответом будет `"HEXLET"`, но это не так. Эта программа выведет `"hexlet"`. Почему? -Дело в том, что строки в Java неизменяемы. Не существует способа и методов, способных изменить саму строку. Любой метод строки может только вернуть новую строку. +Дело в том, что строки в Java **неизменяемы** (или **иммутабельны**). После создания их содержимое нельзя изменить. Не существует методов, способных поменять саму строку. Любой метод строки только возвращает новую строку, а оригинал остается прежним. -Основная причина, почему так сделано – производительность. Строки, и другие примитивные типы данных нельзя менять практически ни в одном современном языке. +## Методы строк не меняют оригинал -Вторая причина связана с простотой кода. Когда мы не изменяем данные, а создаем новые данные на основе старых, то код проще анализировать и модифицировать. Особенно если с данными происходит много манипуляций, с этим вам еще предстоит столкнуться. +Когда мы вызываем метод у строки, кажется, что мы меняем ее. Например, переводим в верхний регистр. На самом деле метод `toUpperCase()` возвращает новую строку в верхнем регистре, а исходная строка не меняется. -Но как же поступать, если данные нужно поменять? Для этого достаточно заменить значение переменной: +```text +company = "hexlet" + +company.toUpperCase() → "HEXLET" (новая строка) +company → "hexlet" (не изменилась) +``` + +Чтобы не потерять результат, сохраним его в переменную: + +```java +var company = "hexlet"; +var upper = company.toUpperCase(); +System.out.println(upper); // => HEXLET +``` + +Если не сохранить результат метода, он просто потеряется. Другие методы работают так же: + +```java +var text = " hi "; +var cleaned = text.trim(); +System.out.println(cleaned); // => "hi", результат без пробелов +System.out.println(text); // => " hi ", строка не изменилась +``` + +Метод `trim()` вернул новую строку без пробелов по краям, но сам `text` остался прежним. + +Основная причина такого поведения — производительность. Строки и другие примитивные типы данных нельзя менять почти ни в одном современном языке. Неизменяемость позволяет Java переиспользовать одинаковые строки в памяти и экономить ресурсы. Она же упрощает многопоточный код, где одни и те же данные читают сразу несколько потоков. + +Вторая причина связана с понятностью кода. Когда мы не изменяем данные, а создаем новые на основе старых, код проще анализировать и модифицировать. Особенно если с данными происходит много преобразований, с этим вам еще предстоит столкнуться. Случайно изменить значение строки невозможно, и это снимает целый класс ошибок. + +## Как менять данные + +Но как же поступать, если данные нужно поменять? Для этого достаточно записать результат метода обратно в ту же переменную: ```java var language = "JAVA"; @@ -22,7 +54,9 @@ language = language.toLowerCase(); System.out.println(language); // => java ``` -С другой стороны, именно в такой ситуации можно создать новую переменную с другим именем: +Это уместно, когда суть данных не меняется. После `toLowerCase()` это тот же язык, просто в нижнем регистре. + +С другой стороны, в такой ситуации можно создать новую переменную с другим именем: ```java var language = "JAVA"; @@ -30,4 +64,4 @@ var processedLanguage = language.toLowerCase(); System.out.println(processedLanguage); // => java ``` -Такой подход нередко предпочтительнее по соображениям читаемости. Переменные, которые постоянно меняются, сложнее анализировать. В итоге все зависит от задачи. С опытом придет понимание, какой подход лучше. +Такой подход нередко предпочтительнее по соображениям читаемости. Переменные, которые постоянно меняются, сложнее анализировать. Если результат метода представляет другую сущность, стоит дать ей отдельное имя. В итоге все зависит от задачи. С опытом придет понимание, какой подход лучше. diff --git a/modules/35-methods-using/115-string-immutability/ru/data.yml b/modules/35-methods-using/115-string-immutability/ru/data.yml index 5557336..0ddc549 100644 --- a/modules/35-methods-using/115-string-immutability/ru/data.yml +++ b/modules/35-methods-using/115-string-immutability/ru/data.yml @@ -1,2 +1,7 @@ --- name: Неизменяемость строк +definitions: + - name: Неизменяемость + description: >- + свойство данных, при котором их содержимое нельзя изменить после + создания. Методы возвращают новые данные, а исходные остаются прежними. diff --git a/modules/35-methods-using/120-methods-chain/ru/README.md b/modules/35-methods-using/120-methods-chain/ru/README.md index 437fc9b..ccf6f8c 100644 --- a/modules/35-methods-using/120-methods-chain/ru/README.md +++ b/modules/35-methods-using/120-methods-chain/ru/README.md @@ -1,4 +1,4 @@ -Обработка данных может состоять из достаточно большого количества шагов, которые нужно выполнить. +Обработка данных может состоять из большого количества шагов, которые нужно выполнить один за другим. Возьмем для примера такую задачу: сформировать адрес страницы в интернете на основе введенного пользователем названия статьи. Такая задача часто возникает при публикации статей в блогах. Подобные адреса выглядят так: @@ -6,17 +6,17 @@ https://ru.hexlet.io/blog/posts/iz-vahtovika-v-programmirovanie ``` -Последняя часть здесь *iz-vahtovika-v-programmirovanie* создана автоматически кодом, который мы написали на Хекслете. Кстати, у нее есть специальное название – это [**слаг**](https://en.wikipedia.org/wiki/Clean_URL#Slug). +Последняя часть здесь *iz-vahtovika-v-programmirovanie* создана автоматически кодом, который мы написали на Хекслете. У нее есть специальное название — это [**слаг**](https://en.wikipedia.org/wiki/Clean_URL#Slug). Какие шаги нужно выполнить, чтобы получить подобную строку? Вот лишь некоторые из них: * Перевести все в нижний регистр, чтобы случайно не создавались дубли одинаковых страниц в поисковых системах -* Очистить название от пробельных символов на концах. Там они могут случайно появиться при вводе названия -* Выполнить транслитерацию. Лучше, когда в адресах только символы латинского алфавита -* Вырезать все специальные символы, такие как вопросы, восклицательные знаки и тому подобное -* Заменить все пробелы на дефисы +* Очистить название от пробельных символов по краям. Они могут случайно появиться при вводе +* Выполнить транслитерацию, потому что в адресах лучше использовать символы латинского алфавита +* Вырезать специальные символы вроде вопросительных и восклицательных знаков +* Заменить пробелы на дефисы -Часть шагов тут требует новых для нас знаний, поэтому мы их опустим. Остальные шаги будут выглядеть примерно так: +Часть шагов требует новых для нас знаний, поэтому мы их опустим. Остальные шаги будут выглядеть примерно так: ```java // Название, введенное пользователем. Для простоты на английском @@ -32,9 +32,11 @@ name = name.toLowerCase(); System.out.println(name); // => how-much-is-the-fish ``` -Если внимательно посмотреть на этот код, то можно заметить общий шаблон. Метод возвращает данные, которые мы присваиваем переменной, и дальше по цепочке обрабатывает их. +Если внимательно посмотреть на этот код, то можно заметить общий шаблон. Метод возвращает данные, которые мы присваиваем переменной, и дальше по цепочке обрабатываем их. -Этот шаблон можно упростить, убрав промежуточное перезаписывание переменной: +## Цепочка методов + +Этот шаблон можно упростить, убрав промежуточное перезаписывание переменной. Метод возвращает новую строку, и к этой строке сразу применяется следующий метод. Такой прием называют **цепочкой методов (method chaining)**. ```java var name = " How much is the fish? "; @@ -42,7 +44,7 @@ name = name.trim().replace("?", "").replace(" ", "-").toLowerCase(); System.out.println(name); // => how-much-is-the-fish ``` -Благодаря тому, что каждый метод возвращает новую строку, мы можем продолжать обрабатывать ее, вызывая методы подряд. Если цепочка методов становится слишком длинной, то ее можно разбить на несколько строк: +Методы вызываются один за другим, как звенья в цепочке. Это позволяет писать компактный и читаемый код. Если цепочка становится слишком длинной, ее можно разбить на несколько строк: ```java name = name.trim() @@ -51,4 +53,55 @@ name = name.trim() .toLowerCase(); ``` -Несмотря на удобство этого механизма, им не стоит злоупотреблять. Промежуточные переменные могут упростить понимание кода. +Несмотря на удобство этого механизма, им не стоит злоупотреблять. Промежуточные переменные иногда упрощают понимание кода. + +## Порядок вычисления + +В цепочке методов порядок выполнения идет слева направо. Каждый следующий метод вызывается на результате предыдущего: + +```java +var text = " hExLeT "; +System.out.println(text.trim().toLowerCase().replace("h", "x")); // => xexlet +``` + +1. `" hExLeT "` является исходной строкой. +2. `trim()` удаляет пробелы по краям и возвращает `"hExLeT"`. +3. `toLowerCase()` приводит к нижнему регистру и возвращает `"hexlet"`. +4. `replace("h", "x")` заменяет `"h"` на `"x"` и возвращает `"xexlet"`. + +Тот же результат получится без цепочки, через промежуточные переменные: + +```java +var text = " hExLeT "; +var step1 = text.trim(); // "hExLeT" +var step2 = step1.toLowerCase(); // "hexlet" +var step3 = step2.replace("h", "x"); // "xexlet" +System.out.println(step3); +``` + +Каждый метод возвращает новую строку, и следующий метод применяется уже к ней. + +```text +" hExLeT ".trim().toLowerCase().replace("h", "x") + │ │ │ + ↓ ↓ ↓ + "hExLeT" │ │ + "hexlet" │ + "xexlet" +``` + +В цепочке вы просто двигаетесь слева направо, читая ее как обычное предложение. Если перепутать порядок, результат может отличаться. Например, замена пробелов сработает иначе, если выполнить ее до удаления концевых пробелов. В одних ситуациях итог совпадет случайно, в других порядок действительно повлияет на результат. + +## Где цепочка заканчивается + +Цепочку можно продолжать, пока результат остается строкой или другим типом, у которого есть методы. Если метод возвращает число или другой примитивный тип, дальнейшие методы вызывать уже нельзя: + +```java +var text = "hexlet"; +var index = text.toUpperCase().indexOf("E"); +System.out.println(index); // => 1 +``` + +Метод `indexOf()` возвращает число `1`, то есть позицию первого символа `"E"` в строке `"HEXLET"`. У числа нет строковых методов, поэтому цепочка на этом заканчивается. + +Цепочки методов служат удобным способом объединять несколько операций над значением без промежуточных переменных. diff --git a/modules/35-methods-using/120-methods-chain/ru/data.yml b/modules/35-methods-using/120-methods-chain/ru/data.yml index bb6bf53..f2a3c91 100644 --- a/modules/35-methods-using/120-methods-chain/ru/data.yml +++ b/modules/35-methods-using/120-methods-chain/ru/data.yml @@ -1,2 +1,8 @@ --- name: Цепочки вызовов методов +definitions: + - name: Цепочка методов + description: >- + прием, при котором методы вызывают один за другим, и каждый следующий + применяется к результату предыдущего. Например, + `" Hexlet ".trim().toLowerCase()`. diff --git a/modules/35-methods-using/130-string-symbols/App.java b/modules/35-methods-using/130-string-symbols/App.java new file mode 100644 index 0000000..c98eb00 --- /dev/null +++ b/modules/35-methods-using/130-string-symbols/App.java @@ -0,0 +1,7 @@ +public class App { + public static void main(String[] args) { + // BEGIN + System.out.println("Hexlet".charAt(1)); + // END + } +} diff --git a/modules/35-methods-using/130-string-symbols/Makefile b/modules/35-methods-using/130-string-symbols/Makefile new file mode 100644 index 0000000..d0d0a48 --- /dev/null +++ b/modules/35-methods-using/130-string-symbols/Makefile @@ -0,0 +1,2 @@ +test: + @ test.sh diff --git a/modules/35-methods-using/130-string-symbols/Test.java b/modules/35-methods-using/130-string-symbols/Test.java new file mode 100644 index 0000000..25b62fb --- /dev/null +++ b/modules/35-methods-using/130-string-symbols/Test.java @@ -0,0 +1,24 @@ +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.PrintStream; + +class Test { + public static void main(String[] args) { + final var expected = "e"; + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + System.setOut(new PrintStream(out)); + + App.main(null); + + final var actual = out.toString().trim(); + + System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out))); + System.out.println(actual); + + assertThat(actual).isEqualTo(expected); + } +} diff --git a/modules/35-methods-using/130-string-symbols/ru/EXERCISE.md b/modules/35-methods-using/130-string-symbols/ru/EXERCISE.md new file mode 100644 index 0000000..80ac47a --- /dev/null +++ b/modules/35-methods-using/130-string-symbols/ru/EXERCISE.md @@ -0,0 +1,7 @@ +Выведите на экран второй символ строки `"Hexlet"`. Используйте метод `charAt()` и помните, что индексы начинаются с нуля. + +Ожидаемый вывод: + +```text +e +``` diff --git a/modules/35-methods-using/130-string-symbols/ru/README.md b/modules/35-methods-using/130-string-symbols/ru/README.md new file mode 100644 index 0000000..6ca422d --- /dev/null +++ b/modules/35-methods-using/130-string-symbols/ru/README.md @@ -0,0 +1,65 @@ +Иногда из строки нужно достать один символ. Сайт знает имя и фамилию пользователя, а вывести их хочет в сокращенном виде A. Ivanov. Для этого берут первую букву имени и ставят рядом точку. + +В Java каждый символ строки имеет свой номер, который называют индексом. Отсчет начинается с нуля. У первого символа индекс `0`, у второго `1` и дальше по порядку. Чтобы достать символ по индексу, у строки вызывают метод `charAt()`. Внутри скобок указывают нужный индекс. + +```java +var firstName = "Alexander"; +System.out.println(firstName.charAt(0)); // => A +``` + +Метод `charAt()` вызывается у строки через точку. В скобках стоит аргумент `0`, поэтому метод вернет самый первый символ. Структура такая же, как у других строковых методов. Сначала объект, потом точка, потом имя метода и скобки с аргументом. + +Связь между символами и их индексами видно на схеме: + +```text +Символ A l e x a n d e r +Индекс 0 1 2 3 4 5 6 7 8 +``` + +Длина строки `Alexander` равна `9`, поэтому индекс последнего символа равен `8`, то есть `9 - 1`. Чтобы достать последний символ, передают именно этот индекс: + +```java +var firstName = "Alexander"; +System.out.println(firstName.charAt(8)); // => r +``` + +## Что возвращает метод + +Метод `charAt()` возвращает один символ. В Java для одиночного символа есть отдельный тип данных `char`. Значение типа `char` записывают в одинарных кавычках, например `'A'`. Строка из двойных кавычек и одиночный символ из одинарных кавычек относятся к разным типам. + +```java +var firstName = "Alexander"; +var letter = firstName.charAt(0); +System.out.println(letter); // => A +``` + +Здесь результат вызова сохраняется в переменную `letter`. Это удобно, когда символ нужен дальше в коде. Но переменная не обязательна, метод можно вывести сразу: + +```java +System.out.println("Hexlet".charAt(0)); // => H +``` + +Метод вызывается прямо у строкового литерала `"Hexlet"`. Сначала вычисляется `charAt(0)`, затем результат уходит в `println()`. + +## Выход за границы строки + +Индекс должен попадать внутрь строки. Если передать индекс больше последнего, программа завершится с ошибкой: + +```java +var firstName = "Alexander"; +System.out.println(firstName.charAt(9)); +// StringIndexOutOfBoundsException +``` + +В строке `Alexander` девять символов с индексами от `0` до `8`. Индекса `9` нет, поэтому метод выбрасывает ошибку `StringIndexOutOfBoundsException`. Поэтому при работе с символами сначала проверяют длину строки и обращаются к символу только тогда, когда индекс точно внутри границ. Этот прием разбирается дальше в курсе. + +## Спецсимволы + +Метод `charAt()` считает все символы подряд. Не только буквы и знаки, но и спецсимволы. Каждый из них занимает свою позицию и имеет индекс, даже если на экране его не видно. + +Например, в строке `"\nyou"` под индексом `0` стоит `\n` (перенос строки), а под индексом `1` уже идет буква `y`. Поэтому вызов `magic.charAt(1)` вернет именно `y`. + +```java +var magic = "\nyou"; +System.out.println(magic.charAt(1)); // => y +``` diff --git a/modules/35-methods-using/130-string-symbols/ru/data.yml b/modules/35-methods-using/130-string-symbols/ru/data.yml new file mode 100644 index 0000000..8e026c1 --- /dev/null +++ b/modules/35-methods-using/130-string-symbols/ru/data.yml @@ -0,0 +1,10 @@ +--- +name: Символы строки +tips: [] +definitions: + - name: Индекс + description: позиция символа внутри строки, отсчет начинается с нуля. + - name: charAt + description: >- + метод строки, который возвращает символ по указанному индексу, например + `"Hexlet".charAt(0)` вернет `H`. diff --git a/modules/35-methods-using/140-string-substring/App.java b/modules/35-methods-using/140-string-substring/App.java new file mode 100644 index 0000000..b2c04d4 --- /dev/null +++ b/modules/35-methods-using/140-string-substring/App.java @@ -0,0 +1,7 @@ +public class App { + public static void main(String[] args) { + // BEGIN + System.out.println("Hexlet".substring(0, 3)); + // END + } +} diff --git a/modules/35-methods-using/140-string-substring/Makefile b/modules/35-methods-using/140-string-substring/Makefile new file mode 100644 index 0000000..d0d0a48 --- /dev/null +++ b/modules/35-methods-using/140-string-substring/Makefile @@ -0,0 +1,2 @@ +test: + @ test.sh diff --git a/modules/35-methods-using/140-string-substring/Test.java b/modules/35-methods-using/140-string-substring/Test.java new file mode 100644 index 0000000..c5117ea --- /dev/null +++ b/modules/35-methods-using/140-string-substring/Test.java @@ -0,0 +1,24 @@ +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.PrintStream; + +class Test { + public static void main(String[] args) { + final var expected = "Hex"; + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + System.setOut(new PrintStream(out)); + + App.main(null); + + final var actual = out.toString().trim(); + + System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out))); + System.out.println(actual); + + assertThat(actual).isEqualTo(expected); + } +} diff --git a/modules/35-methods-using/140-string-substring/ru/EXERCISE.md b/modules/35-methods-using/140-string-substring/ru/EXERCISE.md new file mode 100644 index 0000000..87ffdf6 --- /dev/null +++ b/modules/35-methods-using/140-string-substring/ru/EXERCISE.md @@ -0,0 +1,7 @@ +В переменной хранится строка `"Hexlet"`. Достаньте из нее первые три символа с помощью метода `substring()` и выведите подстроку на экран. + +Ожидаемый вывод: + +```text +Hex +``` diff --git a/modules/35-methods-using/140-string-substring/ru/README.md b/modules/35-methods-using/140-string-substring/ru/README.md new file mode 100644 index 0000000..04bde58 --- /dev/null +++ b/modules/35-methods-using/140-string-substring/ru/README.md @@ -0,0 +1,77 @@ +Работая со строками, мы часто решаем одну и ту же задачу — достать часть строки. Получить год из даты, имя из полного ФИО или первые символы из адреса электронной почты. В Java для этого у строки есть метод `substring()`. + +## Что такое подстрока + +Подстрока — это часть строки, которая входит внутрь другой строки. В строке `"12-08-2034"` подстрокой может быть `"2034"`, `"12"` или даже `"-"`. Все зависит от того, какую информацию надо достать. + +Допустим, нужен только год `"2034"`. Каждый символ строки имеет свой индекс (позицию), отсчет идет с нуля: + +```text +'1' '2' '-' '0' '8' '-' '2' '0' '3' '4' + 0 1 2 3 4 5 6 7 8 9 +``` + +Год начинается с индекса `6` и заканчивается на `9`. Чтобы достать его, у строки вызывают метод `substring()` с двумя аргументами: + +```java +var value = "12-08-2034"; +var year = value.substring(6, 10); +System.out.println(year); // => 2034 +``` + +Метод вызывается у строки через точку. Первый аргумент задает индекс начала, второй задает индекс конца. Формат вызова такой: + +```java +строка.substring(начало, конец) +``` + +Символ с индексом начала входит в результат, а символ с индексом конца не входит. Конец удобно воспринимать как порядковый номер символа, который надо взять последним. + +```java +var value = "code-basics"; + +System.out.println(value.substring(5, 11)); // => basics (с 5 по 10 индекс) +System.out.println(value.substring(0, 7)); // => code-ba (с 0 по 6 индекс) +System.out.println(value.substring(2, 6)); // => de-b +``` + +Как все это посчитать? Когда работаем с конкретной строкой, почти всегда считаем на глаз. + +## Подстрока тоже строка + +Метод `substring()` возвращает строку, даже если внутри только цифры. Значит, результат используют как обычную строку — печатают, соединяют, передают в другие методы. Результат одного вызова можно сразу передать в следующий: + +```java +var value = "01-12-9873"; + +var part = value.substring(3, 7); // => 12-9 +System.out.println(part.substring(0, 2)); // => 12 +``` + +Сначала получили подстроку `"12-9"`, а потом достали из нее новую подстроку `"12"`. + +## Подстрока до конца строки + +Иногда часть строки нужна от какого-то символа и до самого конца. Для этого у `substring()` есть вариант с одним аргументом. Он задает только индекс начала, а конец метод сам берет равным концу строки. + +```java +var value = "Hexlet"; + +System.out.println(value.substring(3)); // => let (с 3 символа до конца) +``` + +Вызов `value.substring(3)` берет символы с индекса `3` и дальше до конца строки. Длина строки `"Hexlet"` равна `6`, поэтому `value.substring(3)` и `value.substring(3, 6)` дают одинаковый результат `let`. Когда конец совпадает с длиной строки, второй аргумент можно опустить. + +## Выход за границы строки + +Индексы должны попадать внутрь строки. Если передать индекс больше длины строки, метод выбросит ошибку: + +```java +var value = "Hexlet"; +System.out.println(value.substring(0, 10)); +// StringIndexOutOfBoundsException +``` + +В строке `"Hexlet"` шесть символов, поэтому индекс конца не может быть больше `6`. Передали `10`, и метод завершился ошибкой `StringIndexOutOfBoundsException`. Перед вызовом стоит убедиться, что индексы внутри границ строки. + +Главное понять базовую структуру `строка.substring(начало, конец)`, а на практике эти вызовы быстро войдут в привычку. diff --git a/modules/35-methods-using/140-string-substring/ru/data.yml b/modules/35-methods-using/140-string-substring/ru/data.yml new file mode 100644 index 0000000..827c655 --- /dev/null +++ b/modules/35-methods-using/140-string-substring/ru/data.yml @@ -0,0 +1,11 @@ +--- +name: Подстрока +tips: [] +definitions: + - name: Подстрока + description: часть строки, которая входит внутрь другой строки. + - name: substring + description: >- + метод строки, который возвращает подстроку по индексам начала и конца. + Символ с индексом начала входит в результат, символ с индексом конца не + входит, например `"Hexlet".substring(0, 3)` вернет `Hex`. diff --git a/modules/35-methods-using/150-string-format/App.java b/modules/35-methods-using/150-string-format/App.java new file mode 100644 index 0000000..e97e9fe --- /dev/null +++ b/modules/35-methods-using/150-string-format/App.java @@ -0,0 +1,7 @@ +public class App { + public static void main(String[] args) { + // BEGIN + System.out.println(String.format("Привет, %s!", "Мир")); + // END + } +} diff --git a/modules/35-methods-using/150-string-format/Makefile b/modules/35-methods-using/150-string-format/Makefile new file mode 100644 index 0000000..d0d0a48 --- /dev/null +++ b/modules/35-methods-using/150-string-format/Makefile @@ -0,0 +1,2 @@ +test: + @ test.sh diff --git a/modules/35-methods-using/150-string-format/Test.java b/modules/35-methods-using/150-string-format/Test.java new file mode 100644 index 0000000..6a19d46 --- /dev/null +++ b/modules/35-methods-using/150-string-format/Test.java @@ -0,0 +1,24 @@ +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.PrintStream; + +class Test { + public static void main(String[] args) { + final var expected = "Привет, Мир!"; + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + System.setOut(new PrintStream(out)); + + App.main(null); + + final var actual = out.toString().trim(); + + System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out))); + System.out.println(actual); + + assertThat(actual).isEqualTo(expected); + } +} diff --git a/modules/35-methods-using/150-string-format/ru/EXERCISE.md b/modules/35-methods-using/150-string-format/ru/EXERCISE.md new file mode 100644 index 0000000..f2146cb --- /dev/null +++ b/modules/35-methods-using/150-string-format/ru/EXERCISE.md @@ -0,0 +1,7 @@ +Соберите приветствие по шаблону. Подставьте в шаблон `"Привет, %s!"` слово `"Мир"` с помощью метода `String.format()` и выведите результат на экран. + +Ожидаемый вывод: + +```text +Привет, Мир! +``` diff --git a/modules/35-methods-using/150-string-format/ru/README.md b/modules/35-methods-using/150-string-format/ru/README.md new file mode 100644 index 0000000..3bae8cf --- /dev/null +++ b/modules/35-methods-using/150-string-format/ru/README.md @@ -0,0 +1,63 @@ +Вспомним, как работает конкатенация. Нужные строки и переменные со строками внутри соединяют знаком `+`. + +```java +var firstName = "Joffrey"; +var greeting = "Hello"; + +System.out.println(greeting + ", " + firstName + "!"); +// => Hello, Joffrey! +``` + +В сложных выражениях по такому коду трудно сразу понять, какой текст получится на выходе. Пробелы, запятые и кавычки начинают мешать восприятию. Даже этот пример требует небольшого усилия, чтобы прочитать итоговую строку. + +Поэтому во многих языках есть отдельный способ собрать строку из шаблона и значений. Шаблон задает форму будущей строки, а на места меток подставляются нужные данные. В Java для этого вызывают метод `String.format()`. + +```java +var firstName = "Joffrey"; +var greeting = "Hello"; + +System.out.println(String.format("%s, %s!", greeting, firstName)); +// => Hello, Joffrey! +``` + +Первый аргумент `String.format()` — это шаблон. Внутри шаблона стоят метки `%s`, на их места по порядку подставляются остальные аргументы. Первый `%s` заменяется на `greeting`, второй на `firstName`. Метка `%s` означает, что значение подставляется как строка. + +```text +String.format("%s, %s!", greeting, firstName) + └┬┘ └┬┘ + "Hello" "Joffrey" → "Hello, Joffrey!" +``` + +В варианте с шаблоном текст читается целиком. Пробелы, запятые и восклицательный знак видны сразу, а в цепочке из `+` они тонут среди кавычек. + +## Метод formatted + +У того же действия есть второй вид записи. Метод `formatted()` вызывают прямо у строки-шаблона через точку, а значения передают в скобках. + +```java +var firstName = "Joffrey"; +var greeting = "Hello"; + +System.out.println("%s, %s!".formatted(greeting, firstName)); +// => Hello, Joffrey! +``` + +Здесь шаблон `"%s, %s!"` стоит слева от точки, а `formatted()` подставляет в него аргументы. Результат такой же, как у `String.format()`. Эти два способа делают одно и то же, выбирают любой по вкусу. + +## Пример с числом + +Метка `%s` подставляет не только строки. На ее место можно поставить число, и оно само превратится в строку. + +```java +var school = "Hexlet"; +var year = 2012; + +var about = String.format("%s работает с %s года", school, year); +System.out.println(about); // => Hexlet работает с 2012 года +``` + +Метод собрал из шаблона и аргументов готовую строку и вернул ее. Результат сохранили в переменную `about` и вывели на экран. + +## Почему это удобно + +Шаблон выглядит почти так же, как итоговая строка. Видно, где встанут пробелы и знаки препинания, видно, куда подставятся значения. По такому коду проще понять, что получится на выходе. Поэтому в большинстве задач сборку строки по шаблону предпочитают длинной цепочке конкатенаций. diff --git a/modules/35-methods-using/150-string-format/ru/data.yml b/modules/35-methods-using/150-string-format/ru/data.yml new file mode 100644 index 0000000..c9ca455 --- /dev/null +++ b/modules/35-methods-using/150-string-format/ru/data.yml @@ -0,0 +1,11 @@ +--- +name: Форматирование строк +tips: [] +definitions: + - name: Шаблон + description: >- + строка с метками `%s`, на места которых подставляются значения. + - name: String.format + description: >- + метод, который собирает строку из шаблона и значений, например + `String.format("Привет, %s!", "Мир")` вернет `Привет, Мир!`. diff --git a/modules/35-methods-using/200-methods-deterministic/ru/README.md b/modules/35-methods-using/200-methods-deterministic/ru/README.md index ccd57be..6b40247 100644 --- a/modules/35-methods-using/200-methods-deterministic/ru/README.md +++ b/modules/35-methods-using/200-methods-deterministic/ru/README.md @@ -1,11 +1,20 @@ -Независимо от того, какой язык программирования используется, методы внутри него обладают некоторыми фундаментальными свойствами. Зная эти свойства, легче прогнозировать поведение методов, способы их тестирования и место их использования. К таким свойствам относится детерминированность. Метод называется детерминированным тогда, когда для одних и тех же входных параметров он возвращает один и тот же результат. Например, метод, извлекающий символ из строки — детерминированный. +У методов в любом языке программирования есть фундаментальные свойства. Эти свойства помогают понять, как метод поведет себя в разных ситуациях, как его тестировать и где применять. Одним из таких свойств является **детерминированность**. + +**Детерминированный метод** всегда возвращает один и тот же результат при одинаковых входных данных. Например, детерминированным можно назвать метод, который извлекает символ из строки по его позиции: ```java "wow".charAt(1); // 'o' "wow".charAt(1); // 'o' + +"hexlet".charAt(0); // 'h' +"hexlet".charAt(0); // 'h' ``` -Сколько бы раз мы не вызывали этот метод, передавая туда значение `1`, он всегда вернет `'o'`. В свою очередь метод, возвращающий случайное число, не является детерминированным, так как у одного и того же входа (даже если он пустой, то есть параметры не принимаются) мы получим всегда разный результат. Насколько он разный - не важно, даже если хотя бы один из миллиона вызовов вернет что-то другое, этот метод автоматически считается недетерминированным. +Сколько бы раз мы ни вызывали `charAt()` с аргументом `1` для строки `"wow"`, он всегда вернет `'o'`. Результат зависит только от входных данных и не меняется от вызова к вызову. + +## Недетерминированные методы + +К противоположному типу относятся **недетерминированные методы**. Они возвращают разные результаты при одинаковых входных данных или при их отсутствии (методы без аргументов). Хорошим примером служит метод, который возвращает случайное число: ```java // Метод, возвращающий случайное число @@ -13,4 +22,21 @@ Math.random(); // 0.09856613113197676 Math.random(); // 0.8839904367241888 ``` -Зачем это нужно знать? Детерминированность серьезно влияет на многие аспекты. Детерминированные функции удобны в работе, их легко оптимизировать, легко тестировать. Если есть возможность сделать функцию детерминированной, то лучше ее такой и сделать. +У этого метода нет аргументов, но его результат каждый раз разный. Насколько он разный, не важно. Даже если хотя бы один вызов среди миллиона даст другой результат, метод считается недетерминированным. + +```text +Детерминированный: Недетерминированный: +"wow".charAt(1) → всегда 'o' Math.random() → 0.42 +"wow".charAt(1) → всегда 'o' Math.random() → 0.91 +"wow".charAt(1) → всегда 'o' Math.random() → 0.07 +``` + +## Почему это важно + +Детерминированность влияет на то, как мы работаем с методами. + +- детерминированные методы легко тестировать и предсказывать; +- их проще оптимизировать и использовать повторно; +- недетерминированные методы сложнее проверять, потому что результат меняется. + +Поэтому там, где это возможно, лучше стремиться к тому, чтобы метод оставался детерминированным. diff --git a/modules/35-methods-using/200-methods-deterministic/ru/data.yml b/modules/35-methods-using/200-methods-deterministic/ru/data.yml index 3f4b91c..1e05afa 100644 --- a/modules/35-methods-using/200-methods-deterministic/ru/data.yml +++ b/modules/35-methods-using/200-methods-deterministic/ru/data.yml @@ -4,3 +4,8 @@ tips: - > [Детерминированные функции](https://ru.wikipedia.org/wiki/Чистота_функции#Детерминированность_функции) +definitions: + - name: Побочный эффект + description: >- + действие, которое изменяет внешнее окружение (среду выполнения). Например, + вывод на экран или отправка письма.