Пользовательские функции Java: как создать функцию в Java?
Функция представляет собой небольшую программу, выполняющую определённые действия, когда её (функцию) вызывают по имени. В языке программирования Java есть много встроенных и готовых к работе функций, однако никто не мешает создавать пользователю свои функции.
Исходя из вышеизложенного, пользовательской функцией можно назвать функцию, которую создал сам разработчик для решения конкретной задачи. Непосредственный процесс написания функций считается процедурным подходом в программировании.
Что обычно помещают в функции? Как правило, речь идёт о повторяющемся программном коде. Соответственно, чтобы программисту не писать те же самые строчки кода снова и снова, он просто выносит эти строчки в отдельный блок, а потом вызывает этот блок в основном Java-коде программы тогда, когда это нужно.
Идём дальше, в Java выделяют 2 вида функций: 1. Функция, которая что-либо возвращает. 2. Функция, которая не возвращает ничего.
Вдобавок к этому, функция в Джаве может быть с параметрами и без них. Тут следует напомнить важный момент: переменная, которая создана в функции, после завершения этой функции «умирает», то есть больше не существует.
Рассмотрим формулу создания функции в Java:
Создание функций в Java
Для начала создадим пользовательскую функцию, которая что-нибудь возвращает. Этот тип функций используется чаще всего, ведь очень часто нужно что-либо посчитать, выполнить преобразование и т. п., то есть речь идёт о том, чтобы использовать полученный результат вне этой функции. А так как всё, что создано в функции, после её завершения «погибает», нам надо, чтобы в основной код программы вернулся результат работы этой функции. Для это используется оператор return.
Итак, создадим пользовательскую функцию, которая вернёт нам значение переменной, а также присвоит это значение переменной в основном коде.
public class Main < public static int function1()< //идентификатор доступа, функция является статичной, тип возвращаемого значения, имя функции без параметров int a = 5; //создаём переменную со значением return a; //возвращаем значение при вызове данной функции >public static void main(String[] args) < //блок основного кода нашей программы >>Таким образом, создаётся переменная, которая потом возвращается в нужном разработчику месте. Но вообще, в теле пользовательской функции мы можем прописать любой код, например, по созданию массивов, циклов и другой структуры.
Теперь давайте создадим функцию в Java, которая ничего возвращать не будет. Такой тип функции может пригодиться во время работы с глобальными переменными либо если надо что-либо напечатать и вывести на экран.
По большему счёту, особых отличий между написанием функций обоих видов нет. Главное — указать другой тип (void) и не применять return.
public class Main < public static void function2()< System.out.println("Записывайтесь на курсы OTUS!"); //выводим сообщение на экран >public static void main(String[] args) < //основной блок нашей программы >>Вызываем функции без параметров
Чтобы работать с функциями, получая от них какой-либо результат, надо вызвать функцию в нужном месте по имени.
Давайте воспользуемся написанными нами функциями и вызовем их в основном коде.
public class Main < public static int function1()< int a = 5; return a; >public static void function2() < System.out.println("Записывайтесь на курсы OTUS!"); >public static void main(String[] args) < //основной блок программы int b = function1(); //присваиваем переменной значение, которое возвратит первая функция System.out.println(b); //выводим на экран значение нашей переменной function2(); //вызываем по имени вторую функцию >>Вывод будет следующим:
5 Записывайтесь на курсы OTUS!Следует добавить, что функция, которая что-либо возвращает, обязательно должна вызываться так, как указано в примере, то есть возвращаемое значение должно быть чему-то присвоено.
Создаём функции с параметрами
Иногда надо произвести над значениями какие-нибудь действия. Для этого нужно передать нашей пользовательской функции эти самые значения. Когда значения передаются в нашу функцию, они становятся её аргументами функции.
Итак, давайте создадим функцию с параметрами, а потом вызовем её в основном коде с помощью аргументов. Возведём переменную в определённую степень, а потом вернём значение в переменную.
Необходимые параметры нужно указывать при создании функции (речь идёт о переменных в скобках после имени функции). При этом аргументы надо передать в обязательном порядке, иначе функция попросту не заработает, ведь у неё просто не будет значения, с которым надо взаимодействовать. Аргументы надо указывать при вызове функции (2 целочисленных значения).
public class Main < public static int function3(int var_1, int var_2)< //функция с параметрами int a = 1; //создаём переменную, в которой будет находиться расчётное значение for(int i = 0; i < var_2; i++)< //используем цикл для возведения в степень a = a * var_1; //перемножаем новое значение со старым, возводя тем самым в степень >return a; //возвращаем посчитанное значение > public static void main(String[] args) < //основной блок программы int result = function3(5,3); //вызываем функцию, передав 2 аргумента (возводим 5 в третью степень) System.out.println(result); //выводим значение переменной >>В консоли увидим следующее значение:
Таким образом, в функцию в Java мы можем помещать, что угодно. В нашем же случае, аргументы надо передать обязательно, иначе возникнет ошибка.
Вот и всё, надеемся, что теперь вы разобрались с темой по созданию пользовательских функций на языке Java. Если же интересуют более сложные задачи, добро пожаловать на наш курс для опытных Java-разработчиков!
Функции в Java -
разбиваем код на блокиЧасто какой-то код в программе нужно повторять много раз в разных местах. Тогда этот код можно поместить в функцию. Функция - это блок кода, у которого есть имя, параметры и возвращаемое значение. Функции можно вызывать по имени в любых местах, в main или в других функциях. Это гораздо удобнее, чем без конца копировать кучу кода, когда вам нужно выполнить его несколько раз.
Функцией называют блок кода, у которого есть имя, параметры и возвращаемое значение. По имени можно вызывать этот блок в любом месте программы.
Параметры - это входные данные, на которые опирается наша функция. Например, функция возводит число в квадрат - ей нужно знать, чему равно в данный момент это число. При одном вызове мы можем указать число и получить его в квадрате, а при другом вызове мы укажем уже другое число, и получим другой квадрат. Так что пока мы пишем код функции, то просто говорим, что нам нужно какое-то дробное число, а какое именно - будет указано в скобках при вызове функции, каждый раз новое.
Многие функции создаются, чтобы посчитать какой-то результат. Этот результат нужно вернуть, то есть передать из кода функции в место, где её вызвали. Например, функция, которая возводит число в квадрат, будет возвращать результат расчета, то есть квадрат числа, которое её дали на вход.
Посмотрим код функции для возведения числа в квадрат. Сначала пишется заголовок функции, где указывается слева направо тип возвращаемого значения, имя функции и в скобках её параметры. Функцию возведения в квадрат часто называют sqr от английского слова square - "квадрат". Поскольку её нужно число, возможно дробное, и возвращает она тоже число, то тип параметра будет double и тип возвращаемого значения тоже будет double .
// Наш заголовок читается так: // статическая, возвращает double, называется sqr, принимает параметром один double static double sqr ( double number) < // Фигурная скобка открывает тело функции - её блок кода. // Чтобы посчитать квадрат числа, нужно его умножить само на себя. // Результат перемножения нужно вернуть из функции словом return вот так: return number*number; >// фигурная скобка закрывает блок функции.
Слово static в заголовке функции пока что будем писать всегда, а разберём подробнее, когда будем делать свои классы.
Фигурные скобки . > обрамляют блок функции. Если закрывающую не поставить, то компилятор решит, что у функции нет конца и так и скажет "missing >", то есть "отсутствует >". А если наоборот закрывающих слишком много, то скажет "unbalanced brackets", то есть "скобки не сбалансированы" как про круглые (), так и фигурные <>
Теперь посмотрим на вызов нашей функции, например, в main. Чтобы вызвать функцию нужно написать её имя и в скобках указать значения параметров. Результат работы функции имеет смысл сохранить в какой-нибудь переменной, чтобы потом ей пользоваться, к примеру, чтобы распечатать:
public static void main (String[] args) < // Объявим x равный 10 double x = 10; // Вызвать sqr с параметром x: double result = sqr (x); // Возвращаемое значение sqr подставится прямо сюда // получится double result = 100 System.out.println ("10 в квадрате будет " + result); >
При вызове функции программа выполняет весь её код, вместо параметров подставляя значения, указанные в скобках. Сейчас мы сделали вызов sqr (x) . Значит в параметр подставится число x, а оно сейчас равно 10. В то самое место кода, где мы написали вызов sqr (x)
Тип переменной для хранения результата функции должен соответствовать типу самого результата, поэтому в нашем случае мы сделали double result, а не, например, int, ведь sqr возвращает double.
Теперь на основе функции sqr сделаем в main простой калькулятор - он будет спрашивать у пользователя числа и возводить их в квадрат. Заодно посмотрим, как ещё можно использовать функции.
public static void main (String[] args) < System.out.println ("Это программа для расчета квадратов чисел"); System.out.println ("Введите число:"); // Считаем в x дробное число с клавиатуры Scanner scan = new Scanner (System.in); // Создадим сканер клавиш клавиатуры // Кстати тут мы тоже вызываем функцию - nextDouble, только она уже есть в Java // Мы указываем scan, чтобы подчеркнуть, что вызов именно // для нашего конкретного сканера scan, созданного строчкой выше double x = scan.nextDouble (); // Вызвать sqr с параметром x: double result = sqr (x); // Возвращаемое значение sqr подставится прямо сюда System.out.println (x + " в квадрате будет " + result); System.out.println ("Введите новое число:"); // Мы используем уже введенную раньше переменную x, // поэтому указываем просто x, а не double x: x = scan.nextDouble (); // Снова вызвать sqr с параметром x: result = sqr (x); // Возвращаемое значение sqr подставится прямо сюда System.out.println (x + " в квадрате будет " + result); // Теперь посмотрим другой вариант для той же задачи: System.out.println ("Введите новое число:"); x = scan.nextDouble (); // sqr(x) в программе будет уже просто число, и его можно подставлять куда угодно: System.out.println (x + " в квадрате будет " + sqr (x) ); // И самый короткий вариант: System.out.println ("Введите новое число:"); System.out.println ("В квадрате это будет " + sqr ( scan.nextDouble () ) ); >
В последней строчке мы вызвали сразу nextDouble, sqr и println:
System.out.println ("В квадрате это будет " + sqr ( scan.nextDouble () ) );
Компилятор идёт слева направо и видит, что здесь стоит распечатка println. Он пробует её выполнить, но обнаруживает, что строчку для распечатки он получит, только выполнив функцию sqr, а значит, он выполняет сначала её. Но тут снова, чтобы получить число для возведения в квадрат, нужно выполнить scan.nextDouble .Поэтому прежде всего заработает scan.nextDouble и число будет введено с клавиатуры. Оно будет передано на вход функции sqr и будет возведено в квадрат. Тогда уже оно попадёт в строчку и будет распечатано на экране в функции println.
Получается, что возвращаемое значение одной функции можно сразу дать в качестве входного параметра для другой функции. Это нормально, ведь возвращаем мы число, и на вход нам тоже нужно число. Но при этом, поскольку мы сейчас не заводили никаких переменных, мы уже не сможем использовать результаты работы этой функции где бы то ни было ещё в коде программы. Итак, каскад вызовов делают для краткости кода, но если вы хотите использовать результат где-то ещё в программе, то нужно писать несколько строк и сохранять значения в переменных.
Сейчас в нашем небольшом калькуляторе мы 4 раза подряд совершили один и тот же набор действий - считать число, возвести в квадрат, вывести на экран. Имеет смысл для этих действий завести свою функцию, и таким образом сократить объём кода:
// Для полноты картины приводим нашу функцию sqr static double sqr (double num) < return num*num; >// Назовём функцию ask_and_calc, то есть спросить и посчитать квадрат. // Она ничего не возвращает, поэтому вместо типа возвращаемого значения // пишется void, по-английски пустота. // Данные функция сама возьмёт с клавиатуры и параметры ей не нужны. // Поэтому в круглых скобках ничего нет - это будет функция без параметров. static void ask_and_calc () < System.out.println ("Введите число:"); Scanner scan = new Scanner (System.in); double x = scan.nextDouble (); double result = sqr (x); System.out.println (x + " в квадрате будет " + result); >// Кстати main тоже ничего не возвращает, поэтому он void . // А параметром идёт String[] args, что означает "набор String". // Эти строки можно указывать при запуске приложения из командной строки, // но мы их пока не будем использовать. public static void main (String[] args) < System.out.println ("Это программа для расчета квадратов чисел"); // Больше не копируем один и тот же код 4 раза, а просто вызываем функцию ask_and_calc(); // название читается по-английски, и сразу понятен смысл ask_and_calc(); ask_and_calc(); ask_and_calc(); >
Итак, функция ask_and_calc ничего не возвращает, а значит вместо типа возвращаемого значения пишется void - "пустота". Параметров у этой функции тоже нет, так что пишутся пустые скобки () .
Как сократился main! Он раньше занимал целый экран, а теперь всего несколько строчек. И как понятен он теперь - практически можно читать на английском языке! В этом и состоит удобство использования функций - код разбит на блоки с понятными названиями и их можно вызывать много раз подряд.
О термине "процедура". Строго говоря, функцией называется блок кода, который что-то возвращает. Когда используется слово void, и ничего не возвращается, то говорят, что это процедура. Но разница так тонка, что в обиходе слова «функция» и «процедура» используются практически как синонимы.
О термине "метод". И функции, и процедуры ещё называют методами. Слово метод употребляют, когда хотят подчеркнуть, что функция или процедура принадлежат классу. Классы мы будем изучать позже и тогда вернёмся к вопросу терминологии. В простом понимании функция, процедура и метод - это всё синонимы.
О термине "аргумент". Также параметры ещё называют аргументами. Так что можно сказать "параметр функции", а можно "аргумент функции" и это будет одно и то же.
Наш main можно было бы сократить ещё больше, если бы мы вызывали ask_and_calc в цикле вот так:
public static void main (String[] args) < System.out.println ("Это программа для расчета квадратов чисел"); // спросить число и возвести его в квадрат 4 раза for (int i = 0; iКогда мы делаем функцию и затем вызываем её в коде много раз или пишем цикл, который вызывает один и тот же код много раз, мы экономим объём программы, и при этом делаем её понятной для чтения человеку. Это называется принцип повторного использования кода. Один из краеугольных камней современного программирования.
Заметьте, что в нашем примере мы запускаем функцию sqr внутри функции ask_and_calc, а её внутри main. Так что как видите, запуск одной функции внутри другой - это нормально и удобно.
При каждом запуске функции для всех её переменных заново выделяется память, а когда функция заканчивается - эта память освобождается под нужды других программ. Все числа нужно хранить где-то в памяти, а нужны они только на время выполнения функции.
Более точно можно сказать, что переменная объявленная внутри блока фигурных скобок в функции, в операторе if или в цикле "живёт" только до конца этого блока, то есть пока не будет достигнута закрывающая фигурная скобка. Блок из фигурных скобок задаёт "область жизни", а также "область видимости" переменной. Область жизни, потому что вне блока память, выделенная под переменную, уже освобождена. Область видимости, потому что к этой переменной больше не имеет смысла обращаться, и для компилятора эта переменная больше не видна.
Это правило даёт нам возможность в разных функциях называть переменные одинаковыми именами. Это бывает удобно, например, любой ответ пользователя на вопрос можно поместить в переменную answer, по-английски "ответ". В одной функции answer будет означать ответ на один вопрос, а в другой функции уже совсем на другой.
То же самое касается параметров функций. В разных функциях могут быть параметры с одинаковым названием, но это их никак не связывает, каждый выполняет свою задачу для своей функции.
Вы дочитали тему до конца - это успех :) Пришло время решать задачи. Ответьте на несколько вопросов, чтобы лучше усвоился новый материал:
Функции (методы)
В Java, чтобы вызвать функцию (или метод, что является более точным термином в объектно-ориентированных языках наподобие Java, поскольку функция является методом класса), Вам нужно написать имя функции, за которым будут следовать значения ее аргументов (в скобках). Например, ниже приведен вызов функции треугольного вероятностного распределения с тремя численными аргументами:
triangular( 2, 5, 14 )
А следующий вызов функции распечатывает в лог модели координаты агента вместе с временной меткой:
traceln( time() + ": X = " + getX() + " Y codeline">time() , getX() и getY() .
Даже если у функции нет аргументов, Вы все равно должны поставить скобки после имени функции: time() .
Функция может либо возвращать значение, либо нет. Например, вызов функции time() возвращает текущее модельное время — значение типа double , а вызов функции traceln() не возвращает никакого значения. Если функция возвращает значение, она может быть использована в выражении (например, функция time() была использована в выражении traceln() ). Если функция не возвращает значение, она может быть вызвана только в операторе — (точка с запятой после вызова traceln() показывает, что это оператор).
Стандартные и системные функции
Большая часть кода, который вы пишете в AnyLogic, является кодом подкласса Agent (основного класса исполняющего модуля AnyLogic). Для вашего удобства системные функции AnyLogic и наиболее часто используемые стандартные функции Java доступны там напрямую (вам не нужно думать, какому классу или пакету они принадлежат, и вы можете вызывать эти функции без каких бы то ни было префиксов). Ниже приведены примеры (это лишь некоторые функции из нескольких сотен, полный список вы можете найти в справочнике классов AnyLogic).
Имя типа, написанное перед именем функции, означает тип возвращаемого функцией значения. Если функция не возвращает значение, то вместо имени типа пишется void , но в целях простоты мы не будем писать void .
Математические функции (импортированы из класса java.lang.Math , всего около 45 функций):
Функция | Описание |
---|---|
double min( a, b ) | Возвращает минимум из a и b . |
double log( a ) | Возвращает натуральный логарифм аргумента a |
double pow( a, b ) | Возвращает значение a , возведенное в степень b . |
double sqrt( a ) | Возвращает квадратный корень из a . |
Функции, связанные с модельным временем, датой или элементами даты (около 20 функций):
Функция | Описание |
---|---|
double time() | Возвращает текущее значение модельного времени (в единицах модельного времени). |
Date date() | Возвращает текущую модельную дату ( Date является стандартным Java классом). |
int getMinute() | Возвращает текущую минуту в часе текущей модельной даты |
double minute() | Возвращает интервал времени, соответствующий одной минуте, в выбранных в данной модели единицах модельного времени. |
Функция | Описание |
---|---|
double uniform( min, max ) | Возвращает число, полученное согласно равномерному распределению |
double exponential( rate ) | Возвращает число, полученное согласно экспоненциальному распределению |
Вывод в лог модели и форматирование:
Функция | Описание |
---|---|
traceln( Object o ) | Распечатывает текстовое представление объекта с разделителем строки в консоль |
String format( value ) | Форматирует значение в имеющую правильное представление строку |
Управление выполнением модели:
Функция | Описание |
---|---|
boolean finishSimulation() | Завершает выполнение модели после выполнения текущего события |
boolean pauseSimulation() | Приостанавливает выполнение модели, переводит управляющий модуль в состояние «паузы». |
error( String msg ) | Выдает сообщение об ошибке. Прекращает выполнение модели, выведя заданное сообщение. |
Функция | Описание |
---|---|
Agent getOwner() | Возвращает агента верхнего уровня, включающего в себя данного агента; если такой есть |
int getIndex() | Возвращает индекс этого агента в списке, если это популяция агентов |
Experiment getExperiment() | Возвращает эксперимент, управляющий выполнением модели |
IExperimentHost getExperimentHost() | Возвращает графический интерфейс модели |
Engine getEngine() | Возвращает исполняющий модуль AnyLogic |
Если объект является агентом, то он поддерживает дополнительные функции:
Функция | Описание |
---|---|
inState( Truck.Moving ) | Проверяет, находится ли агент (типа Truck ) в данный момент в состоянии Moving его диаграммы состояний. |
Функции, связанные с сетями и взаимодействием агентов:
Функция | Описание |
---|---|
connectTo( agent ) | Устанавливает соединение с другим агентом |
send( msg, agent ) | Посылает сообщение заданному агенту |
Функции, связанные с пространством и движением:
Функция | Описание |
---|---|
double getX() | Возвращает X-координату агента в непрерывном пространстве |
moveTo( x, y, z ) | Начинает движение агента к точке (x, y, z) в трехмерном пространстве |
Вы можете получить список всех функций, доступных в текущем контексте, например, в поле свойства элемента, вызвав Мастер подстановки кода (нажав Ctrl + Пробел (macOS: Alt + Пробел )).
Функции элементов модели
Все элементы модели AnyLogic (события, диаграммы состояний, табличные функции, графики, графические фигуры, элементы управления, объекты библиотек и т.д.) отображаются в Java объекты и предоставляют в пользование свои поля и методы, с помощью которых Вы можете получить информацию об объектах и управлять ими.
Чтобы вызвать функцию определенного элемента модели, находящегося внутри агента, Вам нужно написать имя объекта, затем поставить точку, и затем — имя вызываемой функции: .
Вот некоторые примеры вызова функций элементов текущего агента:
Планирование и отмена запланированных событий:
- event.restart( 15*minute() ) — планирует событие event на момент времени, отстоящий от текущего на 15 минут
- event.reset() — отменяет (возможно, запланированное ранее) событие event
Посылка сообщений диаграмме состояний и проверка текущего состояния:
- statechart.receiveMessage( "Go!" ) — доставляет сообщение "Go!" в диаграмму состояний statechart
Отображение области просмотра:
- viewArea.navigateTo() – отображает часть холста окна презентации, попавшую в область просмотра viewArea
Изменение цвета фигуры:
- rectangle.setFillColor( red ) — меняет цвет заливки прямоугольника rectangle на красный
Получение текущего значения элемента управления — флажка:
- boolean checkbox.isSelected() — Возвращает текущее состояние флажка checkbox
Эта строка кода прячет или показывает фигуру, в зависимости от текущего состояния флажка checkbox:
- rectangle.setVisible( checkbox.isSelected() );
Изменение состояний и значений параметров вложенных агентов:
- source.set_rate( 100 ) – присваивает параметру rate объекта source значение 100
- hold.setBlocked( true ) – переводит объект hold в заблокированное состояние
Обратите внимание, что параметр rate отображается на панели свойств объекта как Интенсивность прибытия.
Вы можете получить список всех функций объекта, вызвав Мастер подстановки кода. Для этого напишите в коде имя объекта, поставьте точку и затем вызовите Мастер, нажав Ctrl + Пробел (macOS: Alt + Пробел ).
Задание Вашей собственной функции
Вы можете задавать свои собственные функции в агентах, экспериментах, Ваших собственных Java классах. Для типов агента и экспериментов функции могут задаваться графически, как элементы графического редактора.
Другой способ задания функции — написание ее в Дополнительном коде класса в секции свойств типа агента или эксперимента Java для экспертов, см. рисунок ниже.
Мы рекомендуем задавать функции графически — это более наглядный способ, к тому же предоставляющий более быстрый доступ к коду функции на стадии разработки модели.
#10 – Функции в языке Джава
Функции выполняют роль небольших подпрограмм, что могут содержать повторяющийся код из проекта. В языке Джава можно использовать встроенные функции и создавать свои собственные. За урок мы научимся разрабатывать свои функции (методы) в языке Джава.
Видеоурок
Функции можно назвать небольшими подпрограммами, куда можно вынести повторяющийся код и обращаться к нему, когда это будет нужно. Функции значительно облегчают построение программ, так как нам не надо копировать однотипный код множество раз, а можно просто воспользоваться одной общей функцией.
Многие путают функции и методы и не понимают отличий между ними. На самом деле отличий нет, так как что методы, что функции являются одним и тем же. Функции что записаны вне классов называют функциями, а функции что записаны внутри классов называются методами. Поскольку Java это объектно ориентированный язык, то лучше говорить методы, хотя это не имеет никакого значения.
Точно такая же ситуация обстоит с переменным. В классах переменные называются полями, а вне классов - переменными.
Для создания функций необходимо указать возвращаемый тип данных, указать название и параметры. В случае когда функция ничего не возвращает, то указывается тип данных void .
Перед типом данных всегда прописывается модификатор доступа. Сейчас мы их детально рассматривать не будем, так как это тема ООП. В уроке мы будем постоянно устанавливать модификатор public, тем самым будем говорить что функция доступна всем внутри класса.
Зачем static?
Поскольку мы хотим обращаться к функциям напрямую без создания объекта, то мы прописываем ключевое слово static . Оно говорит компилятору что функция принадлежит всему классу, а не конкретным объектам.
В будущем мы более детально ознакомимся с данным модифактором, но пока будем представлять его как удобное слово для обращения к функциям.
Создание функции
На основе всех данных наша функция будет выглядеть следующим образом:
public static void test()
Функция выше не принимает никаких параметров и ничего не возвращает. Она просто пишет слово в консоль. Давайте разнообразим функцию и добавим параметр:
public static void test(String word)
Теперь функция принимает параметр, который будет отображен в консоли.
Если функция должна что-либо вернуть, то прописываем тип данных который будет возвращен. Для возвращения данных используем ключевое слово return :
public static double test(double some_number)
Весь код будет доступен после подписки на проект!
Задание к уроку
Необходимо оформить подписку на проект, чтобы получить доступ ко всем домашним заданиям
Большое задание по курсу
Вам необходимо оформить подписку на сайте, чтобы иметь доступ ко всем большим заданиям. В задание входит методика решения, а также готовый проект с ответом к заданию.
PS: подобные задания доступны при подписке от 1 месяца