Перечислим здесь важные особенности, про которые следует помнить при программированиии на Go.
Язык Go является регистрозависимым: большие и маленькие буквы различаются.
Арифметические операции обозначаются так: сложение (+
), вычитание (-
), умножение (*
), деление (/
), взятие остатка (%
).
Операции сравнения обозначаются так: равно (==
), не равно (!=
), меньше (<
), больше (>
), меньше или равно (<=
), больше или равно (>=
).
Логические операции: не (!
), и (&&
), или (||
).
Текстовые константы берутся в двойные кавычки. Одиночные символы – в одинарные. Внутри текстовых и символьных констант допустимы экранированные последовательности, такие как символ перехода на следующую строчку (\n
), символ табуляции (\t
), двойная кавычка (\"
), одинарная кавычка (\'
). Экранирование можно отключить, взяв текст в обратные кавычки. При этом внутри текста допускается явный переход на следующую строчку.
Комментарии в программе бывают однострочные (выделяются //
) и блочные (начинаются /*
и заканчиваются */
).
Каждая команда языка заканчивается точкой-с-запятой (;
), но от программиста не требуется их ставить: компилятор языка их ставит самостоятельно. При написании некоторых конструкций эта тонкость существенна: фигурные скобки нужно ставить так, как показано в примерах.
Команда изменения значения (=
) обозначается одинарным знаком «равно». Обозначение двоеточие-равно (:=
) используется для создания новых переменных.
Есть команды увеличения (+=
), уменьшения (-=
), домножения (*=
) и т.д. Также есть команды увеличения на 1 (постфиксный ++
) и уменьшения на 1 (постфиксный --
).
Программа состоит из нескольких пакетов. Каждый пакет содержит список используемых пакетов, а также – определения констант, глобальных переменных, типов данных и подпрограмм.
Точка входа в программу должна называться main
и содержаться в пакете main
. В соответствии с этим «скелет» программы выглядит так:
package main
func main() {
// программа начинает работу отсюда
}
Здесь приведён пример программы, складывающей два числа, запрошенных у пользователя.
package main
import "fmt" // используем пакет fmt, в котором содержатся операции ввода/вывода
func main() {
fmt.Println("Введите два числа")
var x, y int // создание двух целочисленных переменных
fmt.Scan(&x,&y) // считывание введённых пользователем значений в эти переменные
fmt.Println(x,"+",y,"=",x+y)
}
Эта программа применяет подпрограммы Println
и Scan
, определённые в пакете fmt
. Следует обратить внимание на амперсанды перед аргументами Scan
: если их не поставить, то числа не будут считаны в указанные переменные. Смысл этих амперсандов мы проясним в следующей главе.
Переменная представляет собой имя для некоторого объекта. Go относится к статически типизированным языкам: это означает, что каждая переменная имеет свой тип и может именовать только объект такого типа.
Перечислим несколько основных типов:
Целочисленные типы: int
, int8
, int16
, int32
, int64
. Различаются количеством бит, отведённым на хранение числа.
Целые неотрицательные типы: uint
, byte
, uint8
, uint16
, uint32
, uint64
. Тип byte
– полный синоним uint8
.
Текстовый тип: string
.
Символьный тип: rune
. Является полным синонимом int32
. Символы отождествляются с их числовыми кодами в кодировке UTF32.
Логический тип: bool
. Имеет два значения: true
и false
.
Числа с плавающей точкой: float32
и float64
. Первый из этих типов позволяет хранить число с точностью примерно 7 десятичных подряд идущих цифр, второй – с точностью примерно 14 десятичных подряд идущих цифр.
Использование одного числового типа вместо другого похожего не допускается. Например, код
var x int64 = 2
var y int32 = 3
var z = x + y
не скомпилируется. Название типа можно использовать в качестве операции приведения к этому типу. Например, следующее вполне допустимо:
var x int64 = 2
var y int32 = 3
var z = x + int64(y) // аргументы и результат типа int64
Создать переменную можно одним из следующих способов:
В переменную попадает нулевое значение: var название тип
В переменную попадает указанное значение: var название = значение
или var название тип = значение
Сокращение для предыдущего: название := значение
Важно помнить, что переменная живёт только до конца блока (обозначаемого парой фигурных скобок), в котором она определена.
Условная конструкция в языке Go имеет довольно стандартный синтаксис:
if x > 0 {
// если x положителен
} else if x == 0 {
// в противном случае, если x равен 0
} else {
// в противном случае
}
Важно помнить, что слово else
должно начинаться на той же строчке, что и закрывающаяся фигурная скобка.
Вечный цикл (можно прервать командой break
):
for {
// тело цикла
}
Цикл «пока выполнено условие»:
for условие {
// тело цикла
}
Цикл с инициализацией и модификацией состояния:
for инициализация; условие; модификация {
// тело цикла
}
Последняя из этих разновидностей обычно применяется в следующей форме:
for i := 0; i < 10; i += 1 {
// тело цикла; будет выполнено десять раз
}
Любая сколько-нибудь сложная программа основывается на более простых подпрограммах, каждая из которых решает какую-то конкретную подзадачу. В языке Go подпрограммы традиционно называются функциями. Каждая функция определяется так:
func название(вход1 тип1, вход2 тип2, ...) (выход1 тип1, выход2 тип2, ...) {
// тело функции
}
Например, вот функция, вычисляющая остаток и неполное частное от деления целых чисел:
func ModDiv(a int, b int) (mod int, div int) {
mod = a % b
div = a / b
return // команда return завершает исполнение подпрограммы
}
В языке Go довольно редко именуют выходы. Более идиоматичен следующий код:
func ModDiv(a int, b int) (int,int) {
return a%b, a/b
}
В качестве примера определения собственных функций рекомендуется изучить решения упражнений 1 и 6 ниже.
Реальность такова, что в процессе обучения Вам придётся читать (или даже писать) программы на языке Паскаль. К счастью, та реализация языка Паскаль, которая сейчас используется повсеместно в школах России (а именно, PascalABC.NET), переняла некоторые возможности сколько-нибудь современных языков программирования. По этой причине разница между простейшими программами на PascalABC.NET и на Go минимальна.
Перечислим основные отличия:
Язык Go чувствителен к регистру, Паскаль – нет.
В языке Go каждая команда заканчивается (неявной) точкой-с-запятой; в Паскале точка-с-запятой является разделителем команд: в конце блока её можно не ставить.
В языке Go блоки выделяются фигурными скобками; в Паскале блоки выделяются словами begin
и end
.
В языке Go точкой входа является функция main
; в Паскале точкой входа является блок кода между begin
и end.
на внешнем уровне.
В языке Go есть ровно один тип подпрограмм: функции; в Паскале подпрограммы, не имеющие результата, называются процедурами.
В языке Go есть команда преждевременного выхода из функции return
. Её использование обязательно, если функция имеет результат. В Паскале результат записывается в переменную с названием, совпадающим с названием функции. Преждевременный выход осуществляется специальной «процедурой» Exit
.
Сравнение и присваивание в Go обозначаются знаками ==
и =
, а в Паскале – знаками =
и :=
.
В Паскале по-другому устроены циклы.
Некоторые упражнения из нижеприведённых разобраны также и на языке Паскаль.
В каждой из этих задач на вход подаётся одно целое положительное число (далее – x), не превышающее миллиарда. Требуется напечатать
Наибольший общий делитель и наименьшее общее кратное чисел x и 20172017.
YES, если x простое, NO – в противном случае.
Сумму всех делителей числа x.
Число с порядковым номером x в последовательности 1 2 2 3 3 3 4 4 4 4 …
Тысячу знаков после запятой числа 1/x.
Во всех следующих задачах формат входных данных: число N, затем N чисел, которые требуется обработать. Требуется напечатать
Число с наименьшей суммой цифр.
Число с наибольшей суммой делителей.
Дисперсию (среднее арифметическое квадратов отклонений от среднего арифметического).
Самое частовстречающееся число (на данном этапе это упражнение довольно сложное).
Во всех решениях строчки package main
и import "fmt"
считаются написанными в начале программы.
// Упражнение 1
func GCD(a, b int) int {
// алгоритм Евклида
for b != 0 {
a,b = b,a%b
}
return a
}
func main() {
var x int
fmt.Scan(&x)
a,b := x,20172017 // эти a,b никакого отношения к a,b из функции GCD не имеют!
gcd := GCD(a,b)
lcm := (a / GCD(a,b)) * b
fmt.Println(gcd, lcm)
}
// Для сравнения упражнение 1 на Паскале
function GCD(a,b: integer): integer; begin
while b <> 0 do begin
(a,b) := (b,a mod b)
end;
GCD := a;
end;
begin
var x: integer;
read(x);
(var a, var b) := (x, 20172017);
var gcd_ab := GCD(a,b);
var lcm_ab := (a div GCD(a,b)) * b;
println(gcd_ab, lcm_ab);
end.
// Упражнение 2
func main() {
var x int
fmt.Scan(&x)
if x == 1 {
fmt.Println("NO")
return // main -- такая же функция, как и все остальные; её тоже можно преждевременно завершить
}
found := false
for d := 2; d*d <= x; d++ {
if x % d == 0 {
fmt.Println("NO")
found = true
break
}
}
if !found {
fmt.Println("YES")
}
}
// Упражнение 3
func main() {
var x int
fmt.Scan(&x)
sum := 0
for d := 1; d <= x; d++ {
if x % d == 0 {
sum += d
}
}
fmt.Println(sum)
}
// Упражнение 4
func main() {
var x int
fmt.Scan(&x)
n := 1
k := 1
for step := 1; step < x; step++ {
if k == n {
k, n = 1, n+1
} else {
k++
}
}
fmt.Println(n)
}
// Упражнение 5
func main() {
var x int
fmt.Scan(&x)
d := 0
if x > 1 {d = 10}
for i := 0; i < 1000; i++ {
fmt.Print(d / x)
d = 10*(d % x)
}
fmt.Println() // после вывода данных принято переходить на следующую строчку
}
// Упражнение 6
func SumDigits(n int) int {
sum := 0
for n > 0 {
sum += n % 10
n = n / 10
}
return sum
}
func main() {
var n int
fmt.Scan(&n)
var optimal int // кандидат на звание "самого лучшего" числа
fmt.Scan(&optimal)
for i:=1; i < n; i++ {
var x int
fmt.Scan(&x)
// наглядная иллюстрация удобств, предоставляемых собственноручно определённой функцией
if SumDigits(x) < SumDigits(optimal) {
optimal = x
}
}
fmt.Println(optimal)
}
@ 2016 arbrk1, all rights reversed