Выполнение математических операций в go с помощью операторов

Операторы присвоения

Самый распространенный оператор присвоения — это тот, который вы уже использовали: знак равенства . Оператор присвоения присваивает значение справа переменной слева. Например, присваивает значение переменной .

При программировании обычно используются составные операторы присвоения, выполняющие операцию со значением переменной, а затем присваивают новое полученное значение этой переменной. Эти составные операторы объединяют арифметические операторы с опертором . Поэтому для сложения мы соединим и для получения составного оператора . Давайте посмотрим, как это выглядит:

Во-первых, мы зададим переменную равной , а затем используем составной оператор для добавления нужного числа переменной слева, а затем присвоим результат переменной .

Составные операторы присвоения используются в циклах , которые вы используете, когда хотите повторить процесс несколько раз:

Используя цикл для прохождения по срезу , вы можете автоматизировать процесс для оператора , который умножает переменную на число , а затем присваивает результат переменной .

Go имеет составной оператор присвоения для каждого из арифметических операторов, описанных в этом обучающем руководстве.

Чтобы добавить, а затем присвоить значение:

Чтобы вычесть, а затем присвоить значение:

Чтобы умножить, а затем присвоить значение:

Чтобы разделить, а затем присвоить значение:

Чтобы вернуть остаток, а затем присвоить значение:

Составные операторы присвоения могут быть полезными, когда нужно инкрементировать или декрементировать значение, или при необходимости автоматизировать определенные процессы в вашей программе.

Объявление циклов ForClause и Condition

Чтобы учесть множество разных вариантов, существует три различных способа создания циклов for в Go, каждый со своими особенностями. В Go циклы for бывают условными (Condition), ForClause и RangeClause. В этом разделе мы посмотрим, как объявлять и использовать ForClause и Condition.

Давайте сначала посмотрим, как использовать цикл for типа ForClause.

Цикл ForClause определяется с помощью начальное значение, за ним идет условие, а затем изменение счетчика. Они складываются в следующий синтаксис:

Чтобы понять, что делают все эти компоненты, давайте рассмотрим цикл for, который увеличивает значение в указанном диапазоне с помощью синтаксиса ForClause:

Давайте разберем этот цикл по частям.

Первая часть цикла – это i := 0. Это начальное значение:

Он говорит, что мы объявляем переменную i и устанавливаем ее исходное значение 0.

Следующий компонент – условие:

В этом условии заявляется, что цикл должен продолжать работу, пока i меньше 5.

В конце идет изменение счетчика:

Согласно ему, переменная i при каждом прохождении цикла увеличивается на единицу (для этого используется ).

Вывод этой программы выглядит так:

Цикл был выполнен 5 раз. Изначально он присвоил переменной i значение 0, а затем проверил, меньше ли оно пяти. Поскольку значение i было меньше 5, цикл повторялся и выполнял действие fmt.Println(i). После завершения цикла был вызван оператор i ++, и значение переменной i увеличивалось на 1.

Примечание: В программировании, как правило, индексация начинается с 0, поэтому на экране 5 чисел – от 0 до 4.

Начинать с 0 или заканчивать определенным числом не обязательно. Мы можем присвоить любое начальное значение, а также остановиться на любом значении. Это позволяет задать в цикле любой желаемый диапазон:

Например, здесь итерация начинается с 20 (включительно) и идет до 25 (исключительно), поэтому вывод выглядит так:

Изменение счетчика также может быть любым – это называется шагом в других языках.

Для начала попробуйте использовать изменение счетчика с положительным значением:

В этом случае цикл for выведет числа от 0 до 15, но с шагом 3, то есть отображаться будет только каждое третье число, вот так:

Мы также можем использовать для этого аргумента отрицательное значение, чтобы выполнить итерацию в обратном направлении, но мы должны соответствующим образом скорректировать исходное значение и аргументы условия:

Здесь мы устанавливаем в переменной i начальное значение 100, используем условие i < 0, чтобы остановиться на 0. Шаг уменьшает значение на 10 с помощью оператора -=. Цикл начинается со значением 100 и заканчивается на 0, с каждой итерацией уменьшаясь на 10. Мы можем увидеть это в выходных данных:

Также можно исключить начальное значение и шаг из синтаксиса for и использовать только условие. Такой цикл называется условным:

На этот раз переменная i объявлена отдельно от цикла for в предыдущей строке кода. В цикле есть только условие, которое проверяет, не меньше ли значение переменной пяти. Пока условие оценивается как true, цикл продолжит итерацию.

В некоторых ситуациях нельзя точно знать, сколько итераций вам потребуется для выполнения определенной задачи. В этом случае вы можете опустить все операторы и использовать ключевое слово break для выхода из цикла:

Например, этот цикл будет полезен при чтении структуры неопределенного размера, такой как :

В предыдущем коде строка buf :=bytes.NewBufferString(“one\ntwo\nthree\nfour\n”) объявляет буфер с некоторыми данными. Поскольку мы не знаем, когда буфер завершится, мы создаем цикл for только с условием, без начального значения и шага. Внутри цикла мы используем строку line, err := buf.ReadString(‘\n’), чтобы цикл мог прочитать строку из буфера и проверить, не было ли при этом ошибки. Если ошибка есть, мы исправляем ее и используем ключевое слово break для выхода из цикла for. Благодаря break не нужно включать условие для остановки цикла.

В этом разделе вы узнали, как объявить цикл ForClause и использовать его для итерации по известному диапазону значений. Также вы научились использовать цикл Condition для итерации по неизвестному диапазону – такой цикл работает, пока не будет выполнено определенное условие. Далее мы посмотрим на RangeClause.

Прохождение циклом по последовательным типам данных с помощью RangeClause

В Go часто используются циклы для прохождения по элементам последовательных типов данных или коллекций, например, срезов, массивов и строк. Чтобы облегчить этот процесс, мы можем использовать цикл с синтаксисом RangeClause. Хотя вы можете пройтись по последовательным типам данных с помощью синтаксиса ForClause, RangeClause понятнее и его удобнее читать.

Прежде чем переходить к использованию RangeClause, давайте рассмотрим, как мы можем пройтись по элементам среза с помощью синтаксиса ForClause:

main.go

В результате вы получите следующий вывод, содержащий каждый элемент среза:

Теперь давайте воспользуемся RangeClause для выполнения того же набора действий:

main.go

В данном случае выводим каждый элемент из списка. Хотя мы использовали переменные и , мы могли вызвать любую переменной и получить тот же результат:

При использовании для среза всегда будут возвращаться два значения. Первое значение будет индексом, в котором находится текущая итерация цикла, а второе — это значение, скрывающееся в элементе с данным индексом. В данном случае для первой итерации индекс был , а значение — .

Иногда нам требуется только значение внутри элементов среза, а не индекс. Если мы изменим предыдущий код, чтобы выводить только значение, то получим ошибку компиляции:

main.go

Поскольку объявляется внутри цикла , но не используется ни разу, компилятор вернет ошибку . Такая же ошибка может быть получена в Go при любом объявлении переменной, которая не будет использована.

По этой причине Go имеет , являющийся символом нижнего подчеркивания (). В цикле вы можете использовать пустой идентификатор, чтобы игнорировать любое значение, возвращаемое из ключевого слова . В данном случае мы хотим игнорировать индекс, который является первым возвращаемым аргументом.

main.go

Этот вывод показывает, что цикл проходит по срезу строк и выводит каждую строку из среза без индекса.

Также вы можете использовать для добавления элементов в список:

main.go

Здесь мы добавили заполнитель в виде строки для каждого элемента длины среза .

Обратите внимание, что мы не должны использовать пустой идентификатор , чтобы игнорировать любое значение, возвращаемое оператором. Go позволяет оставить всю декларативную часть оператора , если мы не будем использовать ни одно из возвращаемых значений

Также мы можем использовать оператор для заполнения среза значениями:

main.go

В данном примере срез инициализируется с десятью пустыми значениями, но цикл задает все значения в списке, например:

Первый раз, когда мы выводили значение среза , то видели только нули. Затем мы проходим по каждому индексу и задаем значение для элемента с текущим индексом. Далее мы выводим значения второй раз, демонстрируя, что все они имеют значения от до .

Также мы можем использовать оператор для прохождения по каждому символу в строке:

main.go

При прохождении по map оператор будет возвращать ключ и значение:

main.go

Примечание

Важно отметить, что порядок, в котором map возвращает значения, является случайным. Каждый раз, когда вы запускаете эту программу, вы можете получить разный результат

Теперь, когда мы научились проходиться по последовательным данным с помощью для циклов , давайте посмотрим, как можно использовать циклы внутри циклов.

Приоритет операций

В Go, как и в математике, мы должны помнить, что операторы будут оцениваться по порядку приоритета, а не в порядке слева направо или справа налево.

Если мы рассмотрим следующее математическое выражение:

Мы можем прочитать его слева направо, но сначала будет выполняться умножение, так что если бы мы выводили , то получили бы следующее значение:

Это объясняется тем, что дает , а затем мы добавляем для получения в качестве результата.

Если же мы хотим добавить значение к , а затем умножить сумму на , нужно использовать скобки в Go, как это делается в математике:

Одним из способов, с помощью которого можно запомнить порядок операций, является акроним PEMDAS:

Порядок Символ Значение
1 P Parentheses (скобки)
2 E Exponent (степень)
3 M Multiplication (умножение)
4 D Division (деление)
5 A Addition (сложение)
6 S Subtraction (вычитание)

Вы можете быть знакомы с другим акронимом для порядка операций, например, BEDMAS или BODMAS. Какой бы акроним вам ни подошел, попробуйте держать его в уме, когда вы будете выполнять математические операции в Go, чтобы получить результаты, которые вы ожидаете.

Умножение и деление

Как и сложение с вычитанием, умножение и деление в Go очень похожи на обыкновенные математические операции. Знак, который мы будем использовать для умножения – это *, а для деления используется /.

Вот пример умножения двух значений с плавающей запятой:

В Go деление имеет разные характеристики в зависимости от числового типа, который мы делим.

При делении целых чисел оператор / выполняет деление floor, где для частного x возвращается наибольшее целое число, меньшее или равное x.

Если вы попробуете разделить 80/6, вы получите 13, а тип данных будет int:

Если в выводе вы хотите получить число с плавающей точкой, вы должны явно преобразовать значения перед делением.

Вы можете сделать это, заключив значения в float32() или float64():

Итерация последовательных типов данных с помощью RangeClause

В Go циклы for обычно используются для перебора элементов последовательных или коллекционных типов данных, таких как срезы, массивы и строки. Чтобы упростить эту задачу, можно использовать цикл for типа RangeClause. Конечно, можно использовать и синтаксис ForClause, однако RangeClause чище и удобнее читается.

Прежде чем перейти к RangeClause, давайте посмотрим, как можно итерировать следующий срез, используя синтаксис ForClause:

Этот цикл выдаст следующий вывод, отображая каждый элемент среза:

Теперь давайте используем цикл RangeClause для выполнения того же набора действий:

В этом случае выводится каждый элемент в списке. В цикле использованы переменные i и shark, но им можно было присвоить другие допустимые имена и получить такой же результат.

При использовании range в срезе всегда будут возвращаться два значения. Первое значение – индекс текущей итерации цикла, а второе – значение этого индекса. В этом случае для первой итерации индекс был равен 0, а значение было hammerhead.

В некоторых ситуациях нужно извлечь только элементы среза, без индекса. Но если изменить предыдущий код, чтобы выводить только значение, мы получим ошибку компиляции:

Поскольку переменная i объявлена в цикле for, но никогда не используется, компилятор выдаст ошибку: i declared and not used. Эта же ошибка возникает в Go каждый раз, когда вы объявляете переменную и не используете ее.

Для подобных случаев в Go есть , который выражается подчеркиванием (_). В цикле for можно использовать пустой идентификатор, чтобы игнорировать любое значение, возвращаемое ключевым словом range. В этом случае мы будем игнорировать индекс, который является первым возвращаемым аргументом.

Эти выходные данные показывают, что цикл for проитерировал срез строк и без индекса вывел на экран каждый элемент.

Вы также можете использовать ключевое слово range, чтобы добавить элементы в список:

Здесь мы добавили строку-заполнитель “shark” для каждого элемента в срезе sharks.

Обратите внимание: нам не нужно было использовать пустой идентификатор _, чтобы проигнорировать возвращаемые оператором range  значения. Go позволяет пропустить все объявление оператора range, если ни одно из возвращаемых значений не нужно

Также можно использовать оператор range для заполнения значений среза:

В этом примере integers среза инициализируется десятью пустыми значениями, но цикл for устанавливает все значения в списке:

В первом выводе значения integers есть только нули. Затем цикл перебирает все индексы и устанавливает значение для текущего индекса. После этого мы выводим значение integers во второй раз, и на этот раз тут находятся числа от 0 до 9.

Мы также можем использовать оператор range для перебора всех символов в строке:

При выполнении итерации по карте range будет возвращать и ключ, и значение:

Примечание: Важно отметить, что значения карты возвращаются в случайном порядке. Каждый запуск программы может выдать другой результат

Операторы

Оператор – это символ или функция, обозначающая операцию. Например, в математике знак плюс или + – это оператор, который выполняет сложение.

В Go вы встретите некоторые знакомые операторы из математики. Однако другие операторы, которые мы будем использовать, специфичны для компьютерного программирования.

Вот краткая справочная таблица математических операторов в Go. Мы рассмотрим в этом руководстве все следующие операции.

Операция Результат
x + y Сумма х и у
x – y Разница между х и у
-x Меняет знак х
+x Преобразование значения х в число
x * y Произведение х и у
x / y Отношение х к у
x % y Остаток х / у

Мы также рассмотрим составные операторы присваивания, включая += и *=, которые объединяют арифметический оператор с оператором =.

Конструкция break (continue) с указателем

Существует ещё одно решение, позволяющее прекратить работу нескольких вложенных циклов: операторы break и continue с указателем.

Синтаксис break с указателем:

Используем эту конструкцию для фрагмента кода из прошлого примера:

В результате с помощью указателя (его имя может быть любым) и слова break мы из внутреннего цикла перешли к внешнему и прекратили его работу. Эта конструкция применяется с различными типами циклов: for, while, do…while и foreach.

Вместо оператора break можно использовать continue с указателем. Отличие в том, что с помощью continue мы не прекращаем указанный цикл, а переходим к его следующей итерации. Вот, например, исправленный код из прошлого примера, но с использованием continue:

Примечания:

1. Заключать название цикла в фигурные скобки не обязательно, если его тело состоит всего из одной строки. Но на этом лучше не экономить, потому что при расширении тела цикла (когда вы будете модифицировать программу) про недостающие скобки очень легко забыть. Зато вовремя расставленные {} делают ваш код более читаемым.

2. Оператор break с указателем применим не только к циклам, но и к простым блокам кода. Но делать это не рекомендуется, так как даже блок-схемами очень сложно описать такое поведение программы. Код, где используются break с указателями, становится сложным для чтения и поддержки.

Операторы

Оператор — это символ или функция, указывающая операцию. Например, в математике значок плюс или — это оператор сложения.

В Go мы увидим несколько похожих операторов, которые взяты из математики. Однако другие операторы, которые мы будем использовать, принадлежат только к компьютерному программированию.

Здесь вы можете найти таблицу с математическими операторами в Go. Мы рассмотрим все эти операторы в рамках данного руководства.

Операция Что возвращает
Сумма и
Разница между и
Изменение знака
Тождественность
Произведение и
Результат деления на
Остаток деления на

Также мы рассмотрим составные операторы присваивания, включая и , которые совмещают арифметический оператор с оператором .

Цикл do…while (с постусловием)

Кроме цикла с предусловием while существует вариант, который выполняет хотя бы одну итерацию, а после этого проверяет условие. Это цикл do…while, который называется циклом с постусловием.

Синтаксис do…while:

Сначала отрабатывает действие в <теле цикла>, а потом проверяется <условие выполнения цикла>. Если оно возвращает true, то цикл выполнит действие повторно.

Предположим, что нам обязательно нужно вывести на консоль слово «Привет» хотя бы один раз. Для этого можно использовать следующую конструкцию:

Цикл do…while редко применяется на практике, но его всё же используют:

  • если нужно сделать что-то хотя бы единожды — например, вывести слово «Привет», как выше;
  • если значение, от которого зависит условие, инициализируется внутри тела цикла.

Пример кода:

С помощью конструкции new Random().nextInt() здесь берётся очередное случайное число. Итог работы таков: цикл будет находить и выводить на консоль случайные числа до тех пор, пока их значение не будет превышать 50.

Диаграмма работы цикла do…while:

Операторы присваивания

Наиболее распространённым оператором присваивания является знак равенства (=). Он присваивает переменной слева значение справа. К примеру, в выражении v = 23 переменной v было присвоено значение 23.

В программировании часто используются составные операторы присваивания, которые выполняют операцию со значением переменной, а затем присваивают этой переменной полученное новое значение. Составные операторы объединяют арифметический оператор с оператором =. Поэтому для сложения мы будем комбинировать + с =, чтобы получить составной оператор +=. Давайте посмотрим, как это выглядит:

Сначала мы присваиваем переменной w значение 5, затем мы используем составной оператор присваивания +=, чтобы добавить правильное число к значению переменной слева, а затем присваиваем переменной w полученный результат.

Составные операторы присваивания часто используются в циклах for, которые позволяют программе повторить процесс несколько раз:

Используя цикл for для итерации по срезу values, вы смогли автоматизировать процесс оператора *=, который умножил переменную w на 2, а затем снова присвоил результат переменной w. Это позволяет автоматизировать процесс умножения чисел в заданном диапазоне.

В Go есть составные операторы присваивания для каждой математической операции:

  • y += 1 (сложение и присваивание)
  • y -= 1 (вычитание и присваивание)
  • y *= 2 (умножение и присваивание)
  • y /= 3 (деление и присваивание)
  • y %= 3 (вывод остатка и присваивание)

Составные операторы присваивания могут быть полезны, когда значения нужно постепенно увеличивать или уменьшать или если вам нужно автоматизировать определенные процессы в программе.

Вложенные циклы

В Go циклы можно вкладывать друг в друга, как и в других языках программирования. Вложенность — это размещение одного конструкта внутри другого. В данном случае вложенный цикл — это цикл, который работает внутри другого цикла. Это может быть полезно, когда вы хотите выполнять цикличное действие для каждого элемента набора данных.

Вложенные циклы структурно похожи на . Они построены следующим образом:

Программа сначала начинает выполнять внешний цикл, запуская первую итерацию. Эта первая итерация запускает внутренний цикл, который выполняется до конца. После этого программа возвращается вверх внешнего цикла, завершая вторую итерацию и снова запуская внутренний цикл. Опять же, внутренний цикл выполняется до конца, а программа будет возвращаться вверх внешнего цикла, пока последовательность не будет завершена или оператор break или другой оператор прервет процесс.

Давайте реализуем вложенный цикл , чтобы мы более внимательно изучили этот вопрос. В данном примере внешний цикл будет проходить по срезу с целыми числами , а внутренний цикл будет проходить по срезу строк .

main.go

При запуске этой программы мы получим следующий вывод:

Вывод показывает, что программа завершает первую итерацию внешнего цикла, выводя , после чего запускается выполнение внутреннего цикла с выводом , и соответственно. Когда внутренний цикл завершен, программа возвращается вверх внешнего цикла, выводит , а затем снова в полном объеме выводит внутренний цикл (, , ) и т. д.

Вложенные циклы могут быть полезными для итерации по элементам внутри срезов, состоящих из срезов. В срезе, состоящем из срезов, если мы используем только один цикл , программа выведет каждый внутренний список как элемент:

main.go

Чтобы получить доступ к каждому элементу внутренних срезов, мы реализовали вложенный цикл :

main.go

Когда мы используем вложенный цикл , мы можем пройтись по отдельным позициям внутри этих срезов.

Операторы break

В Go оператор break прерывает выполнение текущего цикла. Он очень часто используется вместе с условным оператором if.

Давайте посмотрим на следующий пример применения оператора break в цикле for:

Эта небольшая программа создает цикл for, который будет повторяться, пока значение i меньше 10.

В цикле for есть оператор if. Оператор if проверяет переменную i, чтобы убедиться, что ее значение меньше 5. Если значение i не равно 5, цикл продолжает работать и выводит значение i. Если значение i равно 5, цикл выполнит оператор break, сообщит «Breaking out of loop» и не будет работать дальше. Когда программа прекращает работу, она выводит сообщение Exiting program.

Когда мы запустим этот код, мы получим такой вывод:

Как видите, когда как целочисленное значение i стало равняться 5, цикл прервался, поскольку оператор break сообщил программе, что так нужно сделать.

Вложенные циклы

Внимание: для освоения этого раздела необходимо понимание принципов работы с массивами. Часто используют циклы, один из которых выполняется в теле другого, — их называют вложенными

Это может потребоваться для обхода двумерных массивов, генерации данных и много чего ещё. Вкладывать друг в друга можно разные циклы неограниченное количество раз

Часто используют циклы, один из которых выполняется в теле другого, — их называют вложенными. Это может потребоваться для обхода двумерных массивов, генерации данных и много чего ещё. Вкладывать друг в друга можно разные циклы неограниченное количество раз.

Вот примеры кода:

В этом фрагменте был создан двумерный массив chars, по которому мы прошли с помощью одного цикла for, вложенного в другой — тоже for. Для каждой итерации внешнего цикла выполняются все итерации вложенного в него внутреннего. Таким образом, для массива размерности 5 на 5 будет совершено 25 итераций — внешний цикл идёт по строкам, внутренний — по столбцам.

Ещё пример, но теперь уже трёх вложенных циклов:

Тут мы прошлись по значениям из трёх массивов и сгенерировали шесть сообщений с разными приветствиями, именами и вопросами.

Бывают ситуации, когда нужно внутри вложенного цикла прекратить выполнение текущего и того, в который он вложен. Если использовать для этого просто break, мы выйдем только из текущего цикла, а внешний продолжит работать:

Как видно из примера, ожидаемого результата (прекратить работу обоих циклов, если найдено число 5) мы не получили. В таких ситуациях можно использовать, например, проверку с помощью boolean-значения:

Мы вводим во внешний цикл логическую переменную check и присваиваем ей значение false. Если внутри второго цикла работа прекращается оператором break, перед этим check присваивается значение true. После завершения работы вложенного цикла проверяем во внешнем, что находится в нашей переменной check. Если true, значит, вложенный цикл был прерван и требуется прервать текущий.

Бесконечный цикл

Бесконечный цикл может понадобиться, если мы не знаем условие выхода (оно определено внутри цикла) или условий несколько, либо если нужно, чтобы цикл не заканчивался вовсе.

Есть три варианта того, как создать такой цикл:

или

или

Пример использования бесконечного цикла:

С помощью конструкции new Random ().nextInt () мы на каждой итерации получаем случайное число и записываем его в переменную randomNumber. Если рандомное число равно 5, то выходим из цикла.

Цикл ниже не закончит работу никогда — строка «Привет!» будет выводиться в консоль бесконечно:

С точки зрения эффективности исполнения не имеет значения, какой из вариантов бесконечного цикла мы используем. Если взглянуть на байт-код, для JVM все эти фрагменты будут выглядеть одинаково:

L0

LINENUMBER <line number> L0

FRAME SAME

GOTO L0

Умножение и деление

Как и сложение и вычитание, умножение и деление будут выглядеть примерно так, как в математике. Знак, который мы используем в Go для умножения — это , а знак, который мы используем для деления — это .

Здесь приведен пример выполнения умножения в Go двух чисел с плавающей запятой:

В Go деление имеет разные характеристики в зависимости от типа числового значения, на которое мы делим.

Если мы делим целые числа, оператор Go выполняет обычное деление, где для целой части x возвращаемое число — это самое большое число, которое меньше или равно x.

Если вы запустите следующий пример с делением , вы получите в качестве результата, а типом данных будет :

Если желаемый результат вывода — float, вы должны явно преобразовывать значения, прежде чем выполнять деление.

Вы можете сделать это, обернув желаемый тип или вокруг ваших значений:

Вложенные циклы

Важно помнить, что оператор break остановит выполнение только самого внутреннего цикла, в котором он вызывается. Если у вас есть набор вложенных циклов, вам нужно внести оператор break в каждый цикл отдельно, если этот цикл должен прерываться

В этой программе у нас есть два цикла. Оба цикла повторяются 5 раз, в каждом есть условный оператор if с оператором break. Внешний цикл будет прерван, если значение outer равно 3. Внутренний цикл прервется, если значение inner будет равно 2.

Если вы запустите программу, вы увидите такой вывод:

Обратите внимание: каждый раз, когда внутренний цикл прерывается, внешний цикл не делает этого. Это потому, что break будет прерывать только самый внутренний цикл, из которого он вызывается

Итак, вы знаете, как оператор break останавливает выполнение цикла. Теперь давайте посмотрим, как можно продолжить итерацию цикла.

Рейтинг
( Пока оценок нет )
Понравилась статья? Поделиться с друзьями:
Мой редактор ОС
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: