На главную Назад Вперёд

Основные конструкции языка Go

Особенности синтаксиса

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

  1. Язык Go является регистрозависимым: большие и маленькие буквы различаются.

  2. Арифметические операции обозначаются так: сложение (+), вычитание (-), умножение (*), деление (/), взятие остатка (%).

  3. Операции сравнения обозначаются так: равно (==), не равно (!=), меньше (<), больше (>), меньше или равно (<=), больше или равно (>=).

  4. Логические операции: не (!), и (&&), или (||).

  5. Текстовые константы берутся в двойные кавычки. Одиночные символы – в одинарные. Внутри текстовых и символьных констант допустимы экранированные последовательности, такие как символ перехода на следующую строчку (\n), символ табуляции (\t), двойная кавычка (\"), одинарная кавычка (\'). Экранирование можно отключить, взяв текст в обратные кавычки. При этом внутри текста допускается явный переход на следующую строчку.

  6. Комментарии в программе бывают однострочные (выделяются //) и блочные (начинаются /* и заканчиваются */).

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

  8. Команда изменения значения (=) обозначается одинарным знаком «равно». Обозначение двоеточие-равно (:=) используется для создания новых переменных.

  9. Есть команды увеличения (+=), уменьшения (-=), домножения (*=) и т.д. Также есть команды увеличения на 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 относится к статически типизированным языкам: это означает, что каждая переменная имеет свой тип и может именовать только объект такого типа.

Перечислим несколько основных типов:

  1. Целочисленные типы: int, int8, int16, int32, int64. Различаются количеством бит, отведённым на хранение числа.

  2. Целые неотрицательные типы: uint, byte, uint8, uint16, uint32, uint64. Тип byte – полный синоним uint8.

  3. Текстовый тип: string.

  4. Символьный тип: rune. Является полным синонимом int32. Символы отождествляются с их числовыми кодами в кодировке UTF32.

  5. Логический тип: bool. Имеет два значения: true и false.

  6. Числа с плавающей точкой: 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

Создать переменную можно одним из следующих способов:

  1. В переменную попадает нулевое значение: var название тип

  2. В переменную попадает указанное значение: var название = значение или var название тип = значение

  3. Сокращение для предыдущего: название := значение

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

Условная конструкция

Условная конструкция в языке 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 ниже.

Отличия от Pascal

Реальность такова, что в процессе обучения Вам придётся читать (или даже писать) программы на языке Паскаль. К счастью, та реализация языка Паскаль, которая сейчас используется повсеместно в школах России (а именно, PascalABC.NET), переняла некоторые возможности сколько-нибудь современных языков программирования. По этой причине разница между простейшими программами на PascalABC.NET и на Go минимальна.

Перечислим основные отличия:

Некоторые упражнения из нижеприведённых разобраны также и на языке Паскаль.

Упражнения

В каждой из этих задач на вход подаётся одно целое положительное число (далее – x), не превышающее миллиарда. Требуется напечатать

  1. Наибольший общий делитель и наименьшее общее кратное чисел x и 20172017.

  2. YES, если x простое, NO – в противном случае.

  3. Сумму всех делителей числа x.

  4. Число с порядковым номером x в последовательности 1 2 2 3 3 3 4 4 4 4 …

  5. Тысячу знаков после запятой числа 1/x.

Во всех следующих задачах формат входных данных: число N, затем N чисел, которые требуется обработать. Требуется напечатать

  1. Число с наименьшей суммой цифр.

  2. Число с наибольшей суммой делителей.

  3. Дисперсию (среднее арифметическое квадратов отклонений от среднего арифметического).

  4. Самое частовстречающееся число (на данном этапе это упражнение довольно сложное).

Решения

Во всех решениях строчки 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