Статьи

Массивы, ломтики и базовые ООП в Go

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

В сегодняшней статье мы будем опираться на эту основу, рассматривая несколько новых концепций; в частности:
— Массивы и ломтики
— Карты
— методы

Массивы и ломтики

Есть много способов инициализации массива в Go. Давайте рассмотрим 5 примеров:

[Golang]
var numberList [5] int
var stringList [10] строка
var float64List [15] float64
[/ Golang]

Здесь мы создали три пустых массива разных типов (int, string и float64) и разных размеров (5, 10 и 15).

[Golang]
x: = [5] float64 {98, 93, 77, 82, 83}
[/ Golang]

Здесь мы создали массив размером 5 элементов типа float64, одновременно инициализируя его.

[Golang]
y: = […] int {98, 93, 77, 82, 83}
[/ Golang]

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

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

 $userList = array("Matthew", "Bruno", "Peter", "Chris"); unset($userList[0]); $userList = array_merge( $userList, array("Jaco", "James", "Michael") ); var_dump($userList); //"Bruno", "Peter", "Chris", "Jaco", "James", "Michael" с $userList = array("Matthew", "Bruno", "Peter", "Chris"); unset($userList[0]); $userList = array_merge( $userList, array("Jaco", "James", "Michael") ); var_dump($userList); //"Bruno", "Peter", "Chris", "Jaco", "James", "Michael" 

Мы инициализировали $userList в массиве, содержащем четыре имени, затем удалили первый элемент и добавили три новых в конце, используя array_merge . К сожалению, мы не можем сделать это в Go, так как массивы всегда имеют фиксированный размер. Вот пример попытки сделать это:

[Golang]
var буфер [5] строка
buffer [0] = «Роберт»
буфер [1] = «Майкл»
buffer [2] = «Джеймс»
буфер [3] = «Аарон»
буфер [4] = «Саймон»
буфер [5] = «Алекс»
[/ Golang]

Сначала мы инициализировали новую переменную, buffer , в строку из 5 элементов. Затем мы попытались сознательно добавить 6 элементов к нему, больше, чем его возможности. Если бы мы попытались запустить это, мы получили бы следующую ошибку:

 # command-line-arguments invalid array index 5 (out of bounds for 5-element array) 

В отличие от динамических языков, в Go размер массива является частью его определения; так что var buffer [5]string var buffer [6]string — это не одно и то же, как вы, вероятно, ожидаете.

Это действительно не проблема, потому что Go имеет концепцию Slice. Как сказал Роб Пайк (один из создателей Go) :

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

Одним из наиболее видимых компонентов являются срезы. Ломтики очень похожи на то, что мы привыкли использовать в PHP , Ruby и Python . Срезы предоставляют возможность работать с массивами, но в динамическом, изменчивом смысле.

Тем не менее, одна вещь, которая немного подтолкнула меня, когда я изучал Go, это то, что это слишком упрощенное определение. Вот более конкретное определение из книги Effective Go:

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

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

Это было немного спешно с массивами и срезами, поэтому давайте немного замедлим и посмотрим на оба более подробно. В частности, давайте посмотрим на следующие 3 вещи:

  1. Создание среза из существующего массива (или среза)
  2. Выращивание ломтика
  3. Модификация среза

Создание среза из существующего массива (или среза)

Хорошо, давайте возьмем наш буферный массив и создадим из него фрагмент. Посмотрите на следующий код:

[Golang]
small_buffer: = buffer [1: 4]
[/ Golang]

Это позволило создать новый фрагмент small_buffer , который состоит из элементов 2–4, указав [1:4] . 1 указывает элемент, с которого нужно начинать, 4 указывает элемент, до которого нужно перейти, но не включать. Поэтому у нас есть элементы 1, 2 и 3.

Нам не нужно указывать ни один из этих параметров. Если бы мы только указали :4 , то мы бы начали с 0 и заканчивали на элементе 4. Если бы мы указали только 2: мы бы начали с элемента 3 и заканчивали в конце буфера. Итак, довольно гибкий.

Модификация среза

Как я упоминал ранее, фрагменты связаны с базовыми фрагментами или массивами, из которых они состоят. Таким образом, если вы изменили срез, он изменит основной массив или срез. Давайте рассмотрим пример и посмотрим результаты.

[Golang]
small_buffer [1] = «Родригес»
[/ Golang]

После этого буфер будет содержать "Robert", "Michael", "Rodriguez", "Aaron", "Simon", "Alex" а small_buffer будет содержать "Michael", "Rodriguez", "Aaron", "Simon", "Alex" . Таким образом, вы можете видеть, что помимо изменения среза, он также изменяет базовый массив.

Выращивание ломтика

А что если вы хотите вырастить массив? Есть встроенный метод, добавить, что делает это действительно легко. Он делает срез из двух или более существующих срезов, и использовать его очень просто. Давайте посмотрим на пример:

[Golang]
// Расширяем существующий срез
g: = [] string {«John», «Paul»}
h: = [] string {«George», «Ringo», «Pete»}
у: = добавить (г, ч…)
[/ Golang]

Здесь мы создали два фрагмента: g & h , которые отдельно содержат полный ансамбль The Beatles. Но мы можем вызвать append, передавая g и h, инициализируя новую переменную y как комбинацию двух. Вы заметили ... после ч? Это расширяет h до набора аргументов, которые индивидуально добавляются в конец g.

Если это не совсем имеет смысла, взгляните на эту ссылку на функции Variadic . В результате получается фрагмент, содержащий [«Джон», «Пол», «Джордж», «Ринго», «Пит»]. Стоит отметить, что если мы изменим y, это не повлияет ни на g, ни на h, так как они не связаны.

Карты

Хорошо, теперь давайте посмотрим на Карты. Продолжая мою аналогию с динамическим языком, вы можете использовать ассоциативные массивы в PHP, словари в Python или хэши в Ruby. Ну, карта в Go действительно похожа. По сути, это просто именованный список (или набор) пар ключ / значение.

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

[Golang]
импорт (
«струна»
)

var retval = map [string] int {}
myHomeTown: = «Брисбен Бэй»

var tokens = strings.Fields (myHomeTown)
для i: = 0; я <лен (токены); я ++ {
retval [токены [i]] = len (токены [i])
}
[/ Golang]

Что я сделал, так это инициализировал новую переменную retval как пустую карту, которая будет содержать строки. Затем я инициализировал новую myHomeTown переменную myHomeTown . После этого, используя метод Fields в пакете strings, я пометил токеном значение myHomeTown .

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

[Golang]
жетоны: = [4] string {«Brisbane», «By», «The», «Bay»}
[/ Golang]

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

[Golang]
карта [Брисбен: 8 By: 2 The: 3 Bay: 3]
[/ Golang]

Теперь это один из способов; как насчет инициализации карты значениями с самого начала? Посмотрите на пример ниже:

[Golang]
Тип Пользовательская структура {
имя, строка клиента
возраст int
}

var users = map [string] User {

}
fmt.Println (пользователи)
[/ Golang]

Во-первых, я создал новый Struct под названием User , содержащий свойства для имени , клиента и возраста . Затем я создал карту, которая имеет строковый ключ и пользовательскую структуру в качестве значения.

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

Методы и ООП в Go

Если вы пришли из динамического языка или из таких языков, как .Net , C ++ и т. Д., Вы ожидаете, что сможете разрабатывать с использованием объектно-ориентированного (ООП) подхода. Однако Go не предоставляет поддержку для ООП, ну, не так, как вы могли ожидать. Подход немного другой .

Помните Структуры, которые мы создавали в этом и предыдущем посте? Первое, что вам нужно сделать, это думать о своих структурах как о основе класса. Затем вам нужно добавить методы в Structs впоследствии.

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

[Golang]
основной пакет

импорт (
«FMT»
«струна»
)
Тип Пользовательская структура {
FirstName, LastName, EmailAddress string
Возраст int
}
[/ Golang]

Здесь мы расширили наш предыдущий Struct, User, добавив новое строковое свойство EmailAddress .

[Golang]
func (u * User) FullName () строка {
return fmt.Sprintf (
«% s% s», u.firstname, u.lastname
)
}
[/ Golang]

Затем мы определили функцию FullName , которая не принимает параметров, но возвращает строку. Что нового (u *User) . Это означает, что пользователь будет получателем функции, эффективно добавляя функцию в Struct. Сама функция просто возвращает объединение свойств FirstName и LastName , вызывая Sprintf экспортированный из пакета fmt .

[Golang]
func main () {
u: = User {FirstName: «Matthew», LastName: «Setter»}
fmt.Println (u.FullName ())
}
[/ Golang]

Теперь давайте использовать это. В приведенном выше коде мы создали новую структуру User, указав только свойства FirstName и LastName . Затем мы вызвали FullName() для него, передавая вызов метода fmt.Println() , который выводит

Завершение

Вот и все! Мы рассмотрели основы переменных , вывода , функций и структур , а затем подошли к обзору массивов , срезов , карт и функций.

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

Скажите мне, что вы думаете в комментариях. Я что-то пропустил? Вы бы подошли к этому по-другому?