- •Лабораторная работа 1 Язык Go. Введение. Объявление констант и переменных. Логические значения и числовые типы данных
- •Задание 1.1 Выведем на экран строку «Моя first программа на Go».
- •Задание 1.2 Написать программу, которая выводит сообщение вида «Иван изучает язык Go» (имя и название языка это аргументы командной строки, переданные программе при выполнении).
- •Задание 1.3 Нужно вывести сообщения вида «ИмяN изучает языкN». Пусть в командной строке задается 2n атрибутов в формате «имя файла имя1 язык1 имя2 язык2 ...»
- •Задание 1.4 Реализуйте вышеописанный пример, используйте различные варианты присваивания значений переменной X (латинские и русские символы).
- •Задание 1.8 Выполните проверку этого задания.
Задание 1.4 Реализуйте вышеописанный пример, используйте различные варианты присваивания значений переменной X (латинские и русские символы).
Рассмотрим объявления констант в Go. Константы представляют собой выражения, значения которых известны компилятору и вычисление которых гарантированно происходит во время компиляции, а не во время выполнения. Базовым типом каждой константы является фундаментальный тип: логическое значение, строка или число. Результаты всех арифметических и логических операций, а также операций сравнения с операндами-константами также являются константами.
Константы объявляются с помощью ключевого слова const. Приведем несколько примеров объявлений:
const limit = 512 // константа; совместима с любыми числовыми типами
const top uint16 = 1421 // константа; тип: uint16
Обычной практикой считается не указывать тип явно, если только не требуется использовать какой-то конкретный тип, который не определяется компилятором. Типизированные числовые константы (такие как top в примере выше) могут использоваться только в выражениях с числовыми значениями того же типа (если явно не преобразовывать их тип). Нетипизированные числовые константы могут использоваться в выражениях с числовыми значениями любых встроенных типов (например, константу limit можно использовать в выражениях с целыми или вещественными числами).
Когда требуется определить несколько констант, их можно сгруппировать в одно объявление с единственным ключевым словом const. В случаях, когда требуется лишь объявить константы с отличающимися значениями, и при этом сами значения не играют никакой роли, можно воспользоваться поддержкой перечислений .
Например, приведенные ниже фрагменты объявляют одни и те же константы:
const Zero = 0 const One = 1 const Two = 2
|
const ( Zero = 0 One = 1 Two = 2 ) |
const ( Zero = iota // 0 One // 1 Two // 2 ) |
Генератор констант iota – это предопределенный идентификатор, представляющий последовательность нетипизированных целочисленных значений. Значение этого идентификатора сбрасывается в нуль при встрече каждого нового ключевого слова const и увеличивается для каждой последующей константы в группе. Поэтому в правом фрагменте выше все константы получат значение iota (One и Two – неявно). Объявление константы Zero следует за ключевым словом const, значение iota будет сброшено в нуль и присвоено константе Zero. Константа One также получит значение iota, но на этот раз оно будет увеличено на 1. Аналогично константа Two получит значение iota, которое к этому моменту станет равно 2.
Если в правом фрагменте убрать явное присваивание значения iota, константа Zero получит значение 0, константа One получит значение константы Zero, и константа Two получит значение константы One – все константы получат значение 0. Аналогично, если константе Zero присвоить значение 2, все константы в этой группе получат значение 2. Eсли константе One присвоить значение 4, константа Zero получит значение 0 (так как она является первой в группе и ей явно не назначено какое-либо другое значение или значение iota), константа One получит значение 4 (указанное явно), и константа Two значение предыдущей константы, т.е. 4.
Рассмотрим объявление переменных в Go. Объявление var создает переменную определенного типа, назначает ей имя и присваивает начальное значение. Каждое объявление имеет общий вид var имя тип = выражение (тип или = выражение может отсутствовать). Если опущен тип, то он определяется из инициализирующего выражения. Если же опущено выражение, то начальным значением является нулевое значение для данного типа:
для чисел 0;
для булевых переменных false;
для строк "" (пустая строка);
для интерфейсов и ссылочных типов (срезов, указателей, отображений, каналов, функций) nil;
для составного типа (массивы или структура) нулевые значения всех его элементов или полей.
Рассмотрим примеры объявления переменных:
var i int // переменная; значение 0; тип: int
var debug = false // переменная; определяемый компилятором тип: bool
Несколько переменных можно объявить и (необязательно) инициализировать в одном объявлении var, используя соответствующий список выражений. Пропущенный тип позволяет объявлять несколько переменных разных типов, например:
var i, j, k int // int, int, int
var s, b, f = "строка”, true, 2.3 // string, bool, float64
Инициализаторы могут быть литеральными значениями или произвольными выражениями. Переменные уровня пакета инициализируются до начала выполнения функции main, локальные переменные инициализируются тогда, когда встречаются их объявления в процессе выполнения функции.
Множество переменных может также быть инициализировано с помощью вызова функции, возвращающей несколько значений, например:
var f, err = os.Open(name) // os.Open возвращает имя файла и ошибку
В функции для объявления и инициализации локальных переменных может использоваться альтернативная форма объявления, именуемая кратким объявлением переменной. Она имеет вид имя := выражение, и тип переменной определяется как тип выражения, например
first := -19 // переменная; определяемый компилятором тип: int
last := int64(9876543210) // переменная; тип: int64
result := true // переменная; определяемый компилятором тип: bool
step := 1.5 // переменная; определяемый компилятором тип: float64
str := "строка" // переменная; определяемый компилятором тип: string
В одном кратком объявлении можно объявить и инициализировать несколько переменных, например i, j := 0, 1 // i = 0, j = 1
Краткие объявления переменных могут использоваться для вызовов функций наподобие os.Open, которые возвращают два или больше значений, например:
f, err := os.Open(name)
if err != nil {
return err
}
// ... использование f
f.Close()
Если некоторые из переменных уже были объявлены в том же лексическом блоке, то для этих переменных краткие объявления действуют как присваивания, например:
in, err := os.Open(infile) //объявляем in и err
out, err := os.Create(outfile) //объявляем out и присваиваем значение err
Краткое объявление переменной должно объявлять по крайней мере одну новую переменную, например, приведенный ниже код не компилируется:
in, err := os.Open(infile)
in, err:= os.Create(outfile) // Ошибка: нет новой переменной
//(для устранения ошибки следует
//использовать присваивание «=»)
Время жизни переменной это интервал времени выполнения программы, в течение которого она существует. Переменные уровня пакета существуют все время работы программы. Локальные переменные имеют динамическое время жизни: новый экземпляр создается всякий раз, когда выполняется оператор объявления, и переменная живет до тех пор, пока она становится недоступной, после чего выделенная для нее память может быть использована повторно. Параметры и результаты функций являются локальными переменными они создаются всякий раз, когда вызывается их функция.
Рассмотрим логические значения и выражения в Go. В языке Go имеются два встроенных логических значения, true и false, оба относятся к типу bool. Кроме того, в Go поддерживаются стандартные логические операторы и операторы сравнения, возвращающие результат типа bool (таблица 1.2).
Таблица 1.2 Логические операторы и операторы сравнения
Оператор |
Описание/результат |
!b |
Оператор логического отрицания. Вернет false, если b имеет значение true |
a || b |
Оператор «логическое ИЛИ» с сокращенным порядком вычисления. Вернет true, если одно из подвыражений, a или b, вернет true |
a && b |
Оператор «логическое И» с сокращенным порядком вычисления. Вернет true, если оба подвыражения, a и b, вернут true |
x < y |
Вернет true, если значение выражения x меньше значения выражения y |
x <= y |
Вернет true, если значение выражения x меньше или равно значению выражения y |
x == y |
Вернет true, если значение выражения x равно значению выражения y |
x != y |
Вернет true, если значение выражения x не равно значению выражения y |
x >= y |
Вернет true, если значение выражения x больше или равно значению выражения y |
x > y |
Вернет true, если значение выражения x больше значения выражения y |
Операторы сравнения в языке Go накладывают определенные ограничения на сравниваемые значения. Два значения должны иметь один и тот же тип или, если они являются интерфейсами, должны содержать реализацию одного и того же интерфейса. Если одно из значений является константой, его тип должен быть совместим с типом другого значения. То есть нетипизированные числовые константы можно сравнивать с числовыми значениями любого типа, но числовые значения разных типов, ни одно из которых не является константой, сравнивать нельзя, если явно не преобразовать тип одного операнда в тип другого.
Операторы == и != могут применяться к операндам любых совместимых типов, включая массивы и структуры, элементы которых могут сравниваться между собой с помощью == и !=. Эти операторы не могут использоваться для сравнения срезов. Операторы сравнения (<, <=, >=, >) могут применяться только к числам и строкам.
Рассмотрим числовые типы данных в Go. Все числовые типы считаются отличными друг от друга, то есть к числовым значениям разных типов (например, к значениям типов int32 и int) нельзя применять двухместные арифметические операторы или операторы сравнения.
Чтобы выполнить арифметическую операцию или сравнить два числовых значения разных типов, необходимо выполнить преобразование типов, обычно к большему типу, чтобы избежать потери точности. Синтаксис преобразования типа имеет вид: тип(значение). Например:
const factor = 3 // Константа factor совместима со всеми числовыми типами
i := 20000 // i автоматически получит тип int
i *= factor
j := int16(20) // j получит тип int16; то же, что и: var j int16 = 20
i += int(j) // Типы должны совпадать, поэтому преобразование обязательно
k := uint8(0) // То же, что и: var k uint8
k = uint8(i) // Успех, но k получит значение i, усеченное до 8 бит
fmt.Println(i, j, k) // Выведет: 60020 20 116
Задание 1.5 Выполните проверку рассмотренного примера, измените приложение так, чтобы переменные и константы получали свои значения при запуске программы (аргументы командной строки).
Если необходимо обеспечить преобразование к меньшему типу без потери данных, всегда можно реализовать подходящую функцию. Например:
func Uint8FromInt(x int) (uint8, error) {
if 0 <= x && x <= math.MaxUint8 {
return uint8(x), nil
}
return 0, fmt.Errorf("%d за пределами диапазона uint8", x)
}
Эта функция принимает аргумент типа int и возвращает значение типа uint8 и nil, если целое число находится в заданном диапазоне, или 0 и значение ошибки в противном случае. Константа math.MaxUint8 определена в пакете math, где также имеются похожие константы для остальных встроенных числовых типов. Функция fmt.Errorf() возвращает значение ошибки, основанное на строке формата и указанном значении.
Задание 1.6 Выполните проверку функции.
К значениям одного типа могут применяться арифметические операторы – в таблице 1.3 перечислены операторы, которые могут применяться к значениям любых числовых (встроенных) типов.
Таблица 1.3 Арифметические операторы, применимые ко всем встроенным числовым типам
Оператор |
Описание/результат |
+x |
x |
-x |
Изменение знака x |
x++ |
Увеличивает x на значение нетипизированной константы 1 |
x-- |
Уменьшает x на значение нетипизированной константы 1 |
x += y |
Увеличивает значение x на значение y |
x -= y |
Уменьшает значение x на значение y |
x *= y |
Присваивает переменной x результат умножения x на y |
x /= y |
Присваивает переменной x результат деления x на y. Если x и y – целые числа, остаток от деления теряется. Деление на нуль вызывает аварию (исключение) |
Выражения, определяющие значения констант, вычисляются на этапе компиляции – в них могут использоваться любые арифметические и логические операторы, а также операторы сравнения. Например:
const (
большоеЧисло int64 = 10000000000 // тип: int64
вещественноеЧисло = 16.0 / 9.0 // тип: float64
комплексное Число = complex(-2, 3.5) * вещественноеЧисло
// тип: complex128
логическоеЗначение = 0.0 <= вещественноеЧисло &&
вещественноеЧисло < 2.0 // тип: bool
)
В примере были использованы идентификаторы на русском языке, т.к. Go полностью поддерживает идентификаторы на национальных языках.
Задание 1.7 Выведите значения констант на экран.
Рассмотрим целочисленные типы данных в Go. Язык обеспечивает как знаковую, так и беззнаковую целочисленную арифметику. Имеются знаковые целые числа четырех размеров 8, 16, 32 и 64 бита, представленные типами int8, int16, int32 и int64, а также соответствующие беззнаковые версии uint8 , uint16, uint32 и uint64. Для большинства ситуаций достаточно использовать единственный целочисленный тип int. Переменные этого типа подходят для использования в качестве счетчиков циклов, индексов массивов и срезов и арифметических вычислений общего назначения.
Тип rune является синонимом для типа int32 и по соглашению указывает, что данное значение является символом Unicode. Эти два имени могут использоваться взаимозаменяемо. Тип byte является синонимом для типа uint8 и подчеркивает, что это значение является фрагментом неформатированных данных, а не малым числом.
Имена и диапазоны значений представлены в таблице 1.4.
Таблица 1.4 Целочисленные типы и диапазоны представляемых значений
Тип |
Диапазон представляемых значений |
byte |
Синоним типа uint8 |
int |
Диапазон int32 или int64, в зависимости от реализации |
int8 |
[-128, 127] |
int16 |
[-32768, 32767] |
int32 |
[-2147483648, 2147483647] |
int64 |
[-9223372036854775808, 9223372036854775807] |
rune |
Синоним типа int32 |
uint |
Диапазон uint32 или uint64, в зависимости от реализации |
uint8 |
[0, 255] |
uint16 |
[0, 65535] |
uint32 |
[0, 4294967295] |
uint64 |
[0, 18446744073709551615] |
uintptr |
Беззнаковое целое, пригодное для хранения значения указателя |
В таблице 1.5 представлены арифметические операторы, применяющиеся только к встроенным целочисленным типам.
Таблица 1.5 Арифметические операторы, применимые только к встроенным целочисленным типам
Оператор |
Описание/результат |
^x |
Поразрядное дополнение значения x |
x %= y |
Присваивает переменной x остаток от деления x на y; деление на нуль вызывает аварию |
x &= y |
Присваивает переменной x результат поразрядной операции «И» над значениями x и y |
x |= y |
Присваивает переменной x результат поразрядной операции «ИЛИ» над значениями x и y |
x ^= y |
Присваивает переменной x результат поразрядной операции «исключающее ИЛИ» над значениями x и y |
x &^= y |
Присваивает переменной x результат поразрядной операции «И-НЕ» над значениями x и y |
x >>= u |
Присваивает переменной x результат поразрядного сдвига вправо значения x на беззнаковое целое число u бит |
x <<= u |
Присваивает переменной x результат поразрядного сдвига влево значения x на беззнаковое целое число u бит |
x % y |
Остаток от деления x на y; деление на нуль вызывает аварию |
x & y |
Операция «поразрядное И» |
x | y |
Операция «поразрядное ИЛИ» |
x ^ y |
Операция «поразрядное исключающее ИЛИ» |
x &^ y |
Операция «поразрядное И-НЕ» |
x << u |
Поразрядный сдвиг влево значения x на беззнаковое целое число u бит |
x >> u |
Поразрядный сдвиг вправо значения x на беззнаковое целое число u бит |
Если результат арифметической операции, как знаковой, так и беззнаковой, имеет больше битов, чем может быть представлено типом результата, мы говорим о переполнении (overflow). При этом старшие биты, которые не помещаются в результате, отбрасываются. Если исходное число имеет знаковый тип, результат может быть отрицательным, если левый бит равен 1, как показано в следующем примере с int8 :
var u uint8 = 255
fmt.Println(u, u+1, u*u) // "255 0 1"
var i int8 = 127
fmt.Println(i, i+1, i*i) // "127 -128 1"
