что такое переменная в программировании для детей
О переменных в программировании
Если заглянуть в википедию, то можно увидеть, что переменная в программировании — это поименованная, либо адресуемая иным способом область памяти, адрес которой можно использовать для осуществления доступа к данным. Слово, из этого определения, на которое я хотел бы обратить ваше внимание — это данные. Так ли на самом деле, что в языках программирования, на которых мы пишем, переменные используются только для доступа к данным. Я, например, пишу на PHP. И в нём, как и в других языках программирования, переменные, кроме доступа к данным, используются также для доступа к объектам(экземплярам классов) и массивам(ассоциативным и обычным) и ещё некоторым вещам. Данные(строки, целые числа, числа с плавающей точкой, булевы значения), объекты(экземпляры классов) и структуры( ассоциативные и обычные массивы, если брать PHP) для человека по-сути являются разными сущностями (абстракциями), и было бы разумным, в наших языках программирования, обращаться с ними, как с разными вещами, игнорируя факт того, что для машины они являются одним и тем же(именованными областями памяти). Для этого я предлагаю вместо переменных начать пользоваться такими сущностями как, например: объект, структура, данные.
Такая замена даёт возможность ментального разделения таких разных и не имеющих ничего общего сущностей(абстракций) как объекты, структуры и данные.
RFC для PHP по этой теме уже в процессе создания.
PS
Вообщем создать RFC для PHP не вышло, тк разработчики PHP сказали, что не хотят тратить символы которые будут нужны для реализации этой задумки. Добавлю от себя что в PHP это всё равно не получилось бы реализовать потому, что у него динамическая типизация. Но! В языке со статической типизацией, где типы не меняются, это вполне можно реализовать. Поэтому надеюсь разработчики таких языков обратят своё внимание на предложенную идею.
Что такое переменная в программировании для детей
Давайте теперь разберемся с переменными, а именно что они из себя представляют и для чего служат. Разбираться мы будем на практике: для этого мы напишем вторую программу, которая будет запрашивать у пользователя поочередно два целых числа, затем складывать их и результат выводить на экран. Затем уже на этом примере разберемся с сущностью переменных в программировании. Хотя, я думаю, что вы все и сами поймете, изучив пример, в котором используются переменные.
Этот же текст с использованием однострочного комментария выглядел бы так:
Вот мы и разобрались с комментариями. Запомните и используйте в своих программах для ясности!
Прежде, чем использовать какую-либо переменную в программе, ее нужно объявить.
Объявить переменную можно в любом месте программы, до ее первого использования. Хотя желательно в своих программах придерживаться какого-либо одного стиля: например, объявлять в начале функции main(), как в этой программе.
Переменные могут хранить не только числовые значения, но и символьные, логические. Пока что мы рассматриваем только числовые. В данном случае мы объявили три переменные целого типа.
Можно также объявить эти переменные и в одну строку, т.к. их типы совпадают. Получится так:
Сейчас хочу немного сказать о синтаксисе языка. Как и в любом языке, понятном человеку, в компьютерном языке программирования тоже есть свой синтаксис. С его помощью определяют правила построения инструкций компьютеру. Рассмотрим синтаксис объявления переменных.
Следующая строка кода выводит значение этой переменной на экран:
Теперь начинаем кодить. Набирайте программу в среде CodeBlocks, компилируйте, запускайте на выполнение и экспериментируйте с ней. Вот теперь хоть какая-то, а польза уже есть с нашей программы, она складывает два числа как-никак)))
Для закрепления материала на операторы ввода/вывода и работу с переменными давайте наберем еще одну программу. Предлагаю разобраться с ней вам самим. Нового в ней будет то, что операции сложения, вычитания, деления и умножения можно производить непосредственно в потоке вывода, т.е. не нужно будет объявлять еще дополнительно переменную для хранения результата произведенных операций. Это еще одно удобство языка С++.
Не забывайте делать отступы для лучшей читабельности и ясности кода. Это очень важное замечание!
Напишите программу, которая запрашивает у пользователя три числа, записывает их в ранее объявленные переменные, а затем с помощью первого оператора вывода выводит их сумму, а во втором операторе вывода выводит их произведение. Сравните ваш код с кодом на следующей странице.
Вы также можете сами себе выдумать подобное задание и реализовать. Больше работайте в редакторе с программой, экспериментируйте с кодом и у вас все получится!
Константы, переменные и арифметика
Рассмотрим пример кода «железнодорожного светофора», то есть программу, которая заставляет поочерёдно мигать два светодиода:
Как видно, предполагается, что светодиоды подключаются к пинам 5 и 6 на Arduino, а переключение происходит раз в 1000 миллисекунд, т.е. ежесекундно.
Представьте теперь, что в силу обстоятельств, стало необходимо перенести светодиоды с этих пинов на 12-й и 13-й пины. К тому же стало понятно, что устройство смотрится лучше при переключении не раз в секунду, а раз в 2 секунды. Что делать?
Теперь программа работает как нужно, но для того, чтобы проделать эти небольшие изменения, нам пришлось так или иначе изменить абсолютно все строки в наших функциях!
В данном случае мы имеем дело с довольно простой программой и проделать изменения не так сложно. Но что если устройство довольно сложное и скетч расписан на сотни строк?! В этом случае сделать все изменения правильно, не ошибиться и не пропустить ни одного изменения становится крайне сложно: мы люди и нам свойственно ошибаться из-за невнимательности.
Макроопределения
Мы можем единожды указать, что левый светодиод — это пин 13, правый — пин 12, а переключать состояния нужно каждые X миллисекунд. Для этого каждому значению назначается понятное имя, которое затем и используется для обращения:
Всё, теперь для изменения параметров устройства достаточно изменить нужные значения в начале программы и не думать об изменениях в самой логике. Кроме того выражение вроде digitalWrite(RIGHT_LED, LOW) гораздо более информативно нежели digitalWrite(12, LOW) и даёт чёткое понимание того, что имел в виду автор.
Конструкция #define называется макроопределением. Она говорит компилятору о том, что всякий раз, когда он видит указанное имя, стоит использовать на этом месте указанное значение.
Обратите внимание: макроопределения #define не завершаются точкой с запятой в конце строки. Дело в том, что #define — это не обычное выражение, а так называемая препроцессорная директива. Подстановка конкретных значений вместо имён происходит ещё до компиляции, на стадии предварительной обработки исходного файла. На этой стадии, по сути, компилятор проделывает операцию как в текстовом редакторе: «Найти все и заменить». Просто результат этой операции не сохраняется в файл, а тут же им используется для непосредственной компиляции.
Попытка скомпилировать такой скетч приведёт к ошибке.
Встроенные макроопределения
На самом деле код мигания светодиодом:
с точки зрения компилятора есть ни что иное как:
Использование понятных имён вместо магических чисел — это один из признаков профессионализма. Это делает код более понятным и простым для изменений, что всегда уважается.
Об именах макроопределений
По негласному соглашению все макроопределения должны иметь имена, написанные заглавными буквами с символом нижнего прочерка _ на месте пробелов.
Это настолько привычное правило, что вы можете ввести в заблуждение других, если не будете его придерживаться. Опять же, следование общепринятым канонам — признак профи.
Переменные
Макроопределения хороши для именования значений, которые не могут измениться по ходу выполнения программы. Мы вряд ли захотим на лету, без изменения кода и перезагрузки Arduino, перенести светодиод маячка с одного пина Arduino на другой. Но что делать, если какие-то параметры программы всё же должны изменяться с течением времени?
Для этого существуют переменные — именованные значения, которые могут изменяться при исполнении программы процессором. Как и когда они должны изменяться зависит от вас, от того какой алгоритм вы задумали и как написали программу для этого.
Например, давайте рассмотрим программу «помирающего маячка». Первый раз он мигает через 1000 мс, затем через 1100 мс, затем через 1200 мс и так далее до бесконечности:
Обратите внимание: объявление переменной, в отличие от макроопределения — это обычное выражение, поэтому оно должно завершаться точкой с запятой.
Пользоваться переменной можно в двух смыслах: получать её значение и изменять её значение. Получение значения выглядит ровно так же, как и использование макроопределений: мы просто используем её имя в коде, а в результате, при исполнении программы в действительности используется её значение:
В нашей программе это выражение означает «уснуть на столько миллисекунд, сколько сейчас записано в переменной с именем blinkDelay ». При первом исполнении этого выражения значение будет изначальным, таким образом мы уснём на 900 мс.
Далее в нашем скетче мы можем видеть:
Это так называемое арифметическое выражение (англ. expression). Символ += называется оператором и означает в C++ «увеличить на». Таким образом после исполнения этого выражения, значение переменной blinkDelay станет на сотню больше и сохранится в ней до следующего изменения.
В итоге мы получаем что и хотели: «помирающий» маячок.
Об именах переменных
Как и в случае с макроопределениями существует общепринятая конвенция о том как нужно называть переменные. Их принято именовать в так называемом «верблюжьем стиле» (camelCase). То есть, начинать строчными буквами, а каждое новое слово писать слитно, с заглавной буквы.
Также отличительным признаком профессионализма является использование понятных, лаконичных имён из которых чётко понятно зачем нужна конкретная переменная в программе.
Ничто так не выносит мозг, как использование одно- или двухбуквенных имён без смысла или применение транслита.
Составление арифметических выражений
В нашем примере мы использовали оператор += для увеличения целочисленного значения нашей переменной.
Конечно же, это не единственный оператор в C++. Кроме того += — это так называемый синтаксический сахар (syntax sugar) — удобная и короткая запись полного выражения. На самом деле выражение:
эквивалентно такой, полной записи:
Не стоит воспринимать символ = дословно, как «равно». В C++ этот символ называется оператором присваивания или просто присваиванием. Нужно читать это выражение так: присвоить переменной blinkDelay (то, что слева от = ) вычисленное значение blinkDelay + 100 (то, что справа от = ).
Вас не должно смущать, что blinkDelay упоминается и в левой и в правой части выражения. Опять же, это не означает «900 = 1000», это означает «записать в переменную blinkDelay новое значение: результат сложения текущего значения blinkDelay и числа 100.
Зацикленная змейка
В одном арифметическом выражении может быть сколько угодно операторов и участвовать сколько угодно переменных. Для демонстрации давайте напишем программу для устройства-гирлянды. Допустим, вы подключили к Arduino 10 светодиодов, к пинам с 4-го по 13-й и хотите, чтобы они включались поочерёдно, как бегущая змейка. Тогда скетч может выглядеть так:
Далее мы выжидаем 100 мс и выключаем его.
Итоговое значение вычисляется по тем же правилам, что и в обычной математике: сначала то, что в скобках; затем умножение и деление; и наконец, сложение и вычитание. В языке C++ символ % — это оператор «модуло» или оператор остатка от деления:
То есть, после четвёртого пина переменная ledPin примет значение 5-го.
Таким образом после 13-го пина снова последует 4-й, а это то, что нам нужно! Оператор остатка от деления часто используют как раз для зацикливания чего-либо.
Пульсирующий маячок
Давайте теперь напишем программу для пульсирующего маячка: светодиода, который поочерёдно то набирает яркость от нуля до максимума, то так же плавно гаснет до нуля. Скетч для этого может выглядеть так:
В C++ можно не указывать начальное значение. В этом случае переменная при появлении примет значение абстрактного мусора: случайного значения, оставшегося в памяти от прошлой программы. Если по нашему замыслу перед чтением переменной мы точно сначала установим её значение — это не проблема.
Функция analogWrite принимает 2 аргумента: пин, о котором идёт речь и значение скважности. Значение скважности — это целое число от 0 до 255, которое определяет отношение длительности ступеньки в 0 В к длительности ступеньки в 5 В. Например:
Как известно, не все пины Arduino поддерживают ШИМ. Нужно выбрать тот, который отмечен символом
Ещё раз взглянем на наш loop :
В C++ символ / означает оператор деления. Поскольку мы оперируем целыми числами оператор деления всегда отсекает дробную часть:
Далее идёт, вероятно, немного пугающая по началу конструкция:
То есть тернарный оператор образует выражение, в котором участвуют 3 подвыражения: условие, «что если не ноль» и «что если ноль». Если рассматривать наш пример, то:
В итоге, brightness с начала программы, с каждым новым loop будет то расти от 0 до 255, то уменьшаться обратно от 255 до 0. А это то, что нам нужно!
Нам остаётся лишь чуть задержаться, чтобы дать светодиоду посветиться на текущем уровне яркости. В примере сделана задержка на 5 мс. Вы можете легко посчитать: при таком значении полное нарастание или затухание занимает 5 мс × 256 = 1280 мс.
О компактной записи и области видимости
При программировании потребность в таких временных переменных «на выброс» возникает очень часто. Для плюс-минус сложных устройств в скетче вам могут понадобиться десятки или сотни таких переменных. Представьте себе, что все они будут определены в одном месте большим рулоном.
Разобраться в том, какая переменная для чего предназначена, будет крайне сложно. Для решения этой и других проблем в C++ переменные могут объявляться непосредственно в том месте, где используются.
Если применить это к нашему примеру, получится такой код:
Как видите, переменная sign теперь определяется непосредственно в том месте, где начинает использоваться.
Переменные объявленные вне функций, такие как step и brightness называются глобальными переменными. Они инициализируются в самом начале исполнения программы и доступны в скетче отовсюду.
Встраивание выражений
Если снова посмотреть на наш пример можно увидеть, что мы рассчитываем sign с помощью довольно простого выражения, а затем используем всего в одном месте, при расчёте brightness на следующей строке.
В таких случаях можно встроить арифметическое выражение непосредственно в то место, где используется его результат и обойтись без отдельной промежуточной переменной вовсе:
По итогам компиляции мы получим абсолютно идентичный результат. Мы просто написали то же самое, но другими словами. Промежуточная переменная всё равно будет создана, но это уже закадровая работа компилятора и нам не стоит об этом задумываться.
Не стоит злоупотреблять подобным склеиванием чрезмерно: код может потерять стройность и понятность. Но для простых случаев, как наш, это вполне уместно.
Кроме такого встраивания, выражения вообще можно составлять при вызове функции, на месте соответствующего аргумента. Если воспользоваться этим для нашего кода, получится вот такая сверхкомпактная запись:
Да, это весь наш loop и он делает всё то же самое, что и раньше. Конструкция с analogWrite находится уже на грани читаемости, но для демонстрации вполне подходит. Разберёмся что же здесь написано.
То есть мы мало того, что встроили тернарное выражение прямо в вызов функции, мы ещё и операцию по увеличению step на единицу каждый loop разместили там же.
Обратите внимание, что если оператор ++ расположен после имени переменной, то в расчёте значения арифметического значения выражения используется старое значение этой переменной: то, что было до увеличения на единицу.
Если ++ стоял бы перед переменной, то она сначала увеличилась бы на единицу и только затем участвовала в вычислениях:
Вернёмся к нашему вызову:
Как уже говорилось, выражение brightness++ означает: «использовать текущее значение, но сразу после использования увеличить его на 1».
Функции с возвращаемыми значениями
Это интересно, но не даёт таких возможностей к написанию программ, как получение значений из-вне. Допустим, к Arduino подключён какой-то сенсор: датчик освещённости, датчик газа, простой потенциометр или что-то ещё. Как получить его показания и использовать их в программе для чего-то полезного?
Первое, что мы видим — это макроопределение пина с потенциометром:
Таким образом в качестве значения макроопределения мы использовали другое макроопределение. Так делать можно и это довольно распространённая практика.
Совершенно верно. Некоторые функции помимо того, что делают что-то полезное умеют так же возвращать значение обратно, в вызывающий код. Функции вроде pinMode или analogWrite не возвращают ничего, по задумке их автора, а вот analogRead возвращает некоторое целочисленное значение.
Чтобы понять какие аргументы функция принимает, возвращает ли она что-нибудь и если возвращает, то что, следует обращаться к документации на эту функцию.
Таким образом в первой строке loop мы просто считываем сигнал с потенциометра, получая угол поворота его ручки в виде целого числа в пределах от 0 до 1023.
Вспоминая о компактной записи, мы можем сделать наш loop чуть лаконичнее:
Итак, вы научились работать со сложными выражениями, макроопределениями и переменными. Использовать функции с возвращаемыми значениями и встраивать вычисления. Этих знаний уже достаточно для создания нехитрых устройств. Пробуйте, экспериментируйте, учитесь!
Компьютерная грамотность с Надеждой
Заполняем пробелы — расширяем горизонты!
Что такое переменная в программировании и чем она отличается от константы
Прежде чем переходить к вопросу о том, что такое переменная в программировании, попробуем разобраться, почему понадобились и константы, и переменные. Алгебра от арифметики существенно отличается тем, что в арифметике мы имеем дело только с числами, а в алгебре вводится понятие переменной величины.
Согласитесь, что выражение
2 + 3 = 5
достаточно серьезно отличается от выражения:
a + b = c
В чем отличие? Не только в том, что в алгебре вместо цифр применяются буквы латинского алфавита, но отличие и в уровне абстракции.
Численные выражения, хоть что с ними делай, дают в итоге только численный результат.
А вот абстрактные буквенные выражения превращаются в формулы, законы и следствия и, двигаясь дальше за пределы алгебры, в леммы, в теоремы, и, вообще, ведут к дифференциальному и интегральному исчислению, к математическому анализу и прочему. Правда, в матанализе (в математическом анализе) уже не хватает латинских букв, в ход идут греческие, всякие «дельты», «сигмы» и прочее. Но это уже не столько от нехватки букв, сколько от постоянного роста уровня абстракции, которая (абстракция) требует новых выразительных средств.
Почему так? Потому что определенный, пусть даже небольшой дополнительный уровень абстракции позволяет мыслить иначе, делать иначе, изучать иначе, и показывать иные результаты, чем при меньшем уровне абстракции.
Так же и в компьютерной грамотности. Можно говорить сначала о самом низком уровне абстракции, например, об арифметике в программировании.
Калькуляторы дружат с константами
Например, возьмем калькулятор. Что он может делать? Достаточно много: выполнять ряд арифметических и даже более сложных действий.
Что получим? Очевидно, значение «5». Арифметика. Но с использованием компьютерной техники — калькулятора.
Рис. 1. Суммирование констант 2+3 на калькуляторе (Windows 7)
Не будем дальше углубляться в возможности калькуляторов. Например, можно было бы рассмотреть более сложные калькуляторы: для инженеров, для программистов, для обработки статистических данных и пр. (см. рис. 2).
Рис. 2. Некоторые виды калькуляторов, имеющихся в Windows 7
Но даже сложные калькуляторы так и останутся калькуляторами, то есть, будут делать арифметические операции той или иной степени сложности. Потому что это один уровень абстракции, самый низкий, на уровне чисел. Ничем другим, кроме как обработкой числовых выражений, калькуляторы заниматься не могут.
Программы дружат с переменными величинами
И если бы следующий, новый уровень абстракции не вошел в обиход в компьютерной грамотности, то тогда не появилось бы программирование в том виде, как оно существует в наше время. Программирование, которое позволяет делать программное обеспечение, что называется, на все случаи жизни, и с дружественным интерфейсом, то есть, удобным для последующего пользования.
Как это работает? Поясним несколько упрощенно, чтобы не требовалось глубокое погружение в сложную область программирования.
Для начала отметим, что в программировании все выражения пишут как бы наоборот по сравнению с тем, как их пишут в алгебре. Если в алгебре сначала указывают операнды (переменные), над которыми следует произвести действия, а потом после знака равенства указывают результат, как в примере
то в программировании делают все наоборот: сначала указывают результат, а потом действие, то есть:
Здесь не случайно я пишу строчные (заглавные) буквы вместо прописных (маленьких) букв:
во-первых, чтобы отличить алгебру от программирования, а
во-вторых, потому что первоначально в нашей стране в программировании использовали в основном заглавные буквы латинского алфавита.
Так как вместо прописных букв латиницы у нас делали строчную кириллицу, иначе где еще взять коды для русских букв?! Это связано с тем, что многие трансляторы с языков программирования у нас в стране лишь адаптировали с западных аналогов, а не разрабатывали с нуля. А там, откуда все это копировалось, русского языка не было по понятным причинам. Хотя были и примеры наших «родных» языков программирования.
И пишу я компьютерные выражения не посредине строки, как это принято в алгебре, а пишу в начале строки так, как это принято в программировании. Это уже вопросы синтаксиса языков программирования, правил написания выражения этих языков. В алгебре одни правила, в программировании – другие, хотя буквы и там, и там могут быть одинаковые.
Почему стали в программировании писать наоборот, а именно стали писать C = A + B? Трудно сказать. Так сложилось, что сначала надо было указывать результат, и лишь потом действие.
Что же дает подобное «волшебное» выражение с буквами вместо цифр для программирования? Казалось бы, в чем разница между константами и переменными:
5 = 2 + 3 (напишем наоборот лишь для сравнения) и
Давайте разберемся. Что может быть результатом сложения 2+3? Большинство ответит, конечно, «5». И хоть это почти правильный ответ, пожалуй, мы с этим согласимся.
Почему почти? Да потому что это правильный ответ для десятичной системы исчисления. Для четверичной системы исчисления, в которой используются только цифры от 0 до 3, ответ был бы «11», да-да, именно одиннадцать, можете не сомневаться. А в пятеричной системе исчисления, где добавляется еще цифра 4, ответ был бы «10».
Но в любом случае, о какой бы системе исчисления мы не говорили, результатом 2+3 всегда будет одно и то же число (константа). В десятичной системе (вернемся к ней теперь надолго), это «5», и только «пять».
А сколько будет A + B? Ответ очевиден: все зависит от того, чему равны A и B. Значит, результатом 2+3 всегда будет 5, а результатом A+B будут разные значения в зависимости от величин A и B.
Достаточно очевидно. Ну и что, что 5 – константа, а тут переменная? А то, что переменные – это другой уровень абстракции. За счет A+B мы теперь можем получать множество разных значений.
Как могут использоваться выражения с переменными величинами
Допустим, A – это вес одного товара, а B – это вес другого товара. Значит, A+B – это суммарный вес обоих товаров. Значит, используя выражение C=A+B, программист может запрограммировать автоматическое суммирование двух весов.
Как он это сделает?
Что получается в итоге? Конечно, вы сразу догадались, что переменной C будет присвоено значение суммы весов, сохраненных в переменных A и B.
И далее программист напишет в своей программе (тоже прошу поверить, что это можно сделать средствами языка программирования): вывести на экране дисплея значение переменной C. Что мы увидим на экране? Конечно, сумму весов первого и второго товаров!
И теперь эту, один раз написанную программу, можно использовать снова, но уже для суммирования следующей пары весов.
Если еще убрать ручной ввод веса товара, и сразу автоматически ввести вес, скажем, с электронных весов (которые сейчас широко применяются в тех же супермаркетах), то на экран дисплея будет автоматически выводиться сумма весов 2-х товаров: положил одни товар, затем положил второй, и видишь сразу результат.
А если пойти дальше, и не выводить на экран сумму весов 2-х товаров, а записать это куда-то в базу данных?! А если не ограничиваться 2-я товарами, а, скажем, говорить о миллионе разных видов товаров, подлежащих взвешиванию? Почему бы и нет! Все это можно описать в виде выражений, подобных C = A + B.
И в итоге мы получим, можно без стеснения сказать, серьезную автоматизированную систему для супермаркета, где учитываются и веса всех товаров, и количество, и стоимость, а также все покупки, сделанные покупателями и прочее, и прочее и прочее. Но это стало возможным, когда появилось программирование с использованием переменных величин, тех самых A, B, C и тому подобное! Без этого уровня абстракции, без переменных не было бы программирования.
Переменные и константы – вместе навсегда
Справедливости ради, надо сказать, что цифры (простые и не очень простые числа) остались в программировании. Они тоже нужны. Их назвали иностранным словом «константы».
Константы – это величины, которые никогда и ни при каких обстоятельствах не меняют свои значения.
А что такое переменная в программировании?
Переменные, в отличие от констант, то и дело меняют свои значения, постоянно их меняют, оттого они и называются переменными величинами.
Так что наряду с выражением C = A + B, в программировании возможно как выражение C = A + 3, так и C = 2 + B.
Однако в левой части программного выражения (до знака равенства «=») константа не может употребляться. Там может быть только переменная, поскольку значение выражения, которое присваивается переменной в левой части выражения, может меняться в зависимости от значений переменных в правой части выражения. А значит, слева может быть только переменная величина.
Благодаря латинским буквам, которые используются вместо цифр, арифметика превратилась в алгебру. Так и программирование от калькуляторов перешло к вычислительным машинам благодаря переменным величинам.
Чего стоило разработчикам языков программирования реализовать возможности для применения переменных величин? Это уже отдельная тема. Одно могу сказать, стоило дорого, само собой, не только в деньгах. Дорого в расходовании (применении) интеллекта для изобретения подобных вещей.
Другой уровень абстракции требует принципиально иных решений, новой конфигурации «железа», новых команд для нового «железа». Но это уже, что называется, другая история…
Нашли ошибку? Выделите фрагмент текста и нажмите Ctrl+Enter.