DevGang
Авторизоваться

Golang: Основной синтаксис

В настоящее время существует так много популярных языков программирования, которые используются для создания сервисов и систем, таких как Node.js, PHP и Python. И все они имеют свои преимущества и недостатки, и от этого сильно зависит то, для чего программист будет использовать тот или иной язык программирования. Если вам нужно написать программу с очень эффективным параллелизмом, но все еще читаемую, я познакомлю вас с этим языком, Go (он же Голанг).

Цель этой статьи - показать вам, как написать программу на Go. Я расскажу вам о часто используемом синтаксисе и покажу некоторые советы и приемы, о которых вы могли не знать в Go. В любом случае, вы также можете найти официальный учебник по Go на https://tour.golang.org/ для получения более подробной информации, но если вы хотите быстро изучить и понять Go и получить некоторые хитрости, эта статья - то, что вам нужно.

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

Прежде всего, если в вашей системе нет компилятора Go, перейдите по адресу https://golang.org/dl/, чтобы загрузить подходящий для вас установщик с вашей системой и установить в его, после чего вы сможете создать и запустить код Go.

Синтаксис

Основной код Go

package main

import "fmt"

func main() {
 fmt.Println("Bye, asteroid!")
}

Как выполнить программу Go

$ go run bye_asteroid.go // build and run
Bye, asteroid!

$ go build -o bin test.go // build
$ ./bin // and run
Bye, asteroid!

Go - это компилируемый язык, как C / C ++, поэтому он работает быстрее, чем некоторые интерпретируемые языки, такие как Node.JS и Python. Приведенный сценарий показывает, как выполнить код без создания двоичного файла (Golang создает двоичный файл для команды запуска, но двоичный файл скрыт), а также показывает, как создать и запустить его двоичный файл.

Приведенный выше код является очень простой программой для отображения текста «Bye, asteroid!» (Поскольку астероид только что прошел нашу землю 10 августа). Golang имеет концепцию пакетов, которая позволяет программистам создавать свои пакеты и позволяет импортировать пакеты других разработчиков. Вы можете увидеть в приведенном выше коде ключевое словом package, это основной пакет, который является корневым для каждой программы Go. А внутри основного пакета есть main функция, которая также является (второй) первой функцией, запускаемой после выполнения программы Go.

Почему я сказал «второй»? Потому что есть функция «init»,которая является фактически первой функцией, которая должна быть выполнена перед выполнением функции «main».

package main

import "fmt"

func init() {
 fmt.Println("Hello, world!")
}

func main() {
 fmt.Println("Bye, asteroid!")
}

/*
>>> RESULT <<<
Hello, world!
Bye, asteroid!
*/

Как видите, сообщение от функции init отображается перед сообщением main. Цель существования функции init - инициализация данных для любых пакетов с функцией init, а не только для основного пакета. Это означает, что если ваша программа вызывает пакет с функцией init в пакете, функция init будет выполнена сразу же после импорта пакета.

Пакеты

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

Структура папки и скрипт для запуска кода Go

Одно важное правило для создания пакета: «в одной папке должен быть только один пакет»

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

Переменные

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

Объявление переменной

package main

import "fmt"

const globalConstant = 2.72
const ExportedGlobalConstant = 3.14

func main() {
 var first string
 first = "This is first string"
 
 var second = "This is second string"
 
 third := "This is third string"
 
 fmt.Println(first, second, third)
}

Есть три способа объявления переменной. Из приведенного выше примера первый - это объявление с указанием типа переменной вручную. Второй это объявление переменной без явного указания типа, тип присваивается автоматически. И третий также получает тип не явно, но без объявления переменной. Для третьего, он обязательно добавлять двоеточие (:) для объявления переменной. Без двоеточия компилятор выдаст ошибку, если вы не объявили переменную раньше.

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

Как получить доступ к глобальной переменной другого пакета.

// ./main.go
package main

import (
 "fmt"
 "./data"
)

func main() {
 fmt.Println(data.Message)
}

// ./data/data.go
package data

Message := "This is message from data package"

Типы переменных

package main
import "fmt"

func main() {
 a := 1    // var a int
 b := 3.14 // var b float
 c := "hi" // var c string
 d := true // var d bool
 fmt.Println(a, b, c, d)

 e := []int{1, 2, 3} // slice
 e = append(e, 4)
 fmt.Println(e, len(e), e[0], e[1:3], e[1:], e[:2])
 
 f := make(map[string]int) // map
 f["one"] = 1
 f["two"] = 2
 fmt.Println(f, len(f), f["one"], f["three"])
}

/*
>>> OUTPUT <<<
1 3.14 hi true
[1 2 3 4] 4 1 [2 3] [2 3 4] [1 2]
map[one:1 two:2] 2 1 0
*/

В Go есть много типов переменных, но я показал вам только часто используемые в приведенном выше коде. Как и другие языки, Go имеет int, float, string и boolean в качестве примитивных типов.

В Go есть слайс, похожий на вектор в C ++ или список в Python, в который вы можете добавить элемент. Go на самом деле имеет массив, который очень похож на слайс, но имеет фиксированную длину.

И последний тип переменной в примере кода это map (похож на map в C ++ или dict в Python). Map используется для сопоставления ключа и значения. 

package main

import "fmt"

type Person struct{
 Name    string `json:"name"`
 Age     int    `json:"age"`
 isAdmin bool
}

func main() {
 p := Person{
   Name:    "Mike",
   Age:     16,
   isAdmin: false,
 }
 fmt.Println(p, p.Name, p.Age, p.isAdmin)
}

/*
>>> OUTPUT <<<
{Mike 16 false} Mike 16 false
*/

Следующий тип - «структура». Он позволяет программистам определять новый тип данных, который содержит в себе другие типы данных (называемые полями). Каждое поле может быть экспортировано по тому же правилу, что и глобальная переменная с первой заглавной буквой. Это означает, что поля «Name» и «Age» могут быть доступны другим пакетам, а isAdmin - нет. Поле может быть присвоен «tag» (например `json: "name"` ) в качестве метаданных для некоторых библиотек, таких как библиотека JSON, которая использует «tag» для сопоставления полей структуры Go и полей JSON.

Преобразование типов

package main

import (
 "fmt"
 "strconv"
 "reflect"
)

func main() {
 a := 3.14
 b := int(a)
 fmt.Println(b, reflect.TypeOf(b))
 
 c := "12.34"
 d, _ := strconv.ParseFloat(c, 64)
 fmt.Println(d, reflect.TypeOf(d))
 
 e := false
 f := fmt.Sprint(e)
 fmt.Println(f, reflect.TypeOf(f))
}

/*
>>> OUTPUT <<<
3 int
12.34 float64
false string
*/

Некоторые пары типов могут быть преобразованы путем непосредственного приведения (int(1.23), float(3), uint32(5)), но для некоторых требуется дополнительная библиотека для преобразования, такая как string-int, int-string, bool-string и string-bool.

Нулевые значения

package main

import "fmt"

func main() {
 var a int
 var b float64
 var c bool
 var d string
 fmt.Println(a, b, c, d)
 
 var e []int
 var f map[string]float64
 fmt.Println(e, f)
 
 type person struct{
   name string
   age int
 }
 var g person
 var h *person
 fmt.Println(g, h)
}

/*
>>> OUTPUT <<<
0 0 false
[] map[]
{ 0} 
*/

Если вы объявляете переменную без присвоения ей какого-либо значения, значение переменной будет установлено равным нулевому значению ее типа. (Вам может быть любопытно, что такое *person и nil. Это указатель и его нулевое значение, для которого я создам отдельную статью, думаю, она будет следующей 😂).

Операции управления потоком

Условия

package main

import "fmt"

func main() {
 // if, else
 a := 5
 if a > 3 {
   a = a - 3
 } else if a == 3 {
   a = 0
 } else {
   a = a + 3
 }
 fmt.Println(a)
 
 // switch, case
 b := "NO"
 switch b {
 case "YES":
   b = "Y"
 case "NO":
   b = "N"
 default:
   b = "X"
 }
 fmt.Println(b)
}

/*
>>> OUTPUT <<<
2
N
*/

В Go есть операторы управления потоком, как и в других языках: if, else, switch, case , но в Go есть только один оператор цикла, который называется for. Потому что вы можете заменить выражение while на выражение for, как в примере ниже.

Цикл

package main

import "fmt"

func main() {
 // for
 c := 3
 for i := 0; i < c; i++ {
   fmt.Print(i, " ")
 }
 fmt.Println()
 
 // replace while with for
 for c > 0 {
   fmt.Print(c, " ")
   c--
 }
 fmt.Println() // for with range
 d := []int{0, 1, 1, 2, 3, 5, 8}
 for _, i := range d {
   fmt.Print(i, " ")
 }
 fmt.Println()
}

/*
>>> OUTPUT <<<
0 1 2 
3 2 1 
0 1 1 2 3 5 8 
*/

Как вы можете видеть в примере, вы можете использовать «for» для той же цели, что и «while» в других языках. Более того, вы можете перебирать некоторые итерируемые переменные, такие как array, slice и map, используя «range», он возвращает два значения: индекс / ключ и значение.

Defer

package main

import "fmt"

func main() {
 f()
}

func f() (int, error) {
 defer fmt.Println("DEFER 1")
 defer fmt.Println("DEFER 2")
 defer fmt.Println("DEFER 3")
 fmt.Println("BODY")
 return fmt.Println("RETURN")
}

/*
>>> OUTPUT <<<
BODY
RETURN
DEFER 3
DEFER 2
DEFER 1
*/

«Отложенное» утверждение - это специальное утверждение в Go. Вы можете вызывать любые функции после оператора defer . Функции будут храниться в стеке и будут вызываться после возврата функции вызывающей стороны. Как видно из примера, существует обратный порядок вызовов функций.

Функции

Функциональные компоненты

Функция Go состоит из 4 частей.

  1. Имя: должно быть названо в camelCase / CamelCase.
  2. Аргументы: функция может принимать ноль или более аргументов. Для двух или более последовательных аргументов одного и того же типа вы можете определить тип в конце последнего аргумента (например, «string» в примере).
  3. Типы возврата: функция может возвращать ноль или более значений. Если возвращает более одного значения, вам необходимо заключить их в скобки.
  4. Тело: это логика функции.

Возвращаемые значения

package main

import "fmt"

func main() {
 fmt.Println(threeTimes("Thank You"))
}

func threeTimes(msg string) (tMsg string) {
 tMsg = msg + ", " + msg + ", " + msg
 return
}

/*
>>> OUTPUT <<<
Thank You, Thank You, Thank You
*/

Вы также можете объявить имена возвращаемых переменных. Таким образом, вам не нужно писать их после ключевого слова return.

Экспортируемые / неэкспортируемые функции

package main

import "fmt"

func main() {
 s := Sum(10)
 f := factorial(10)
 fmt.Println(s, f)
}

func Sum(n int) int {
 // exported function
 sum := 0
 for i := 1; i <= n; i++ {
    sum += i
 }
 return sum
}

func factorial(n int) int {
 // unexported function
 fac := 1
 for i := 1; i <= n; i++ {
   fac *= 1
 }
 return fac
}

/*
>>> OUTPUT <<<
55 3628800
*/

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

Анонимные функции

package main

import "fmt"

func main() {
 a := 1
 b := 1
 c := func(x int) int {
   b *= 2
   return x * 2
 }(a)
 fmt.Println(a, b, c)
}

/*
>>> OUTPUT <<<
1 2 2
*/

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

package main

import "fmt"

func main() {
 myFunc := func(x int) int {
   return x * x
 }
 fmt.Println(myFunc(2), myFunc(3))
}

/*
>>> OUTPUT <<<
4 9
*/

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

Пустые идентификаторы

package main

import "fmt"

func main() {
 x, _ := evenOnly(10)
 fmt.Println(x)
}

func evenOnly(n int) (int, error) {
 if n%2 == 0 {
   return n / 2, nil
 }
 return 0, fmt.Errorf("not even")
}

/*
>>> OUTPUT <<<
5
*/

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

Следующие темы

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

  • Указатель, метод и интерфейс
  • Go Routine

Перевод статьи: Learn Golang Basic Syntax in 10 Minutes
Источник: medium.com

#Golang #Начинающим
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу