среда, 5 июня 2019 г.

Спецификация Go: составные литералы (composite literals)

Составные литералы создают значения для структур, массивов, срезов (slice) и карт (map) и создают новое значение каждый раз, когда они оцениваются. Они состоят из типа литерала, за которым следует список элементов в скобках. Каждому элементу может предшествовать соответствующий ключ.

CompositeLit  = LiteralType LiteralValue .
LiteralType   = StructType | ArrayType | "[" "..." "]" ElementType |
                SliceType | MapType | TypeName .
LiteralValue  = "{" [ ElementList [ "," ] ] "}" .
ElementList   = KeyedElement { "," KeyedElement } .
KeyedElement  = [ Key ":" ] Element .
Key           = FieldName | Expression | LiteralValue .
FieldName     = identifier .
Element       = Expression | LiteralValue .

Базовым типом LiteralType должен быть тип struct, array, slice или map (грамматика применяет это ограничение, за исключением случаев, когда тип задан как TypeName). Типы элементов и ключей должны присваиваться соответствующим типам полей, элементов и ключей литерального типа; нет дополнительного преобразования. Ключ интерпретируется как имя поля для литералов структуры, индекс для литералов массива и среза, а также ключ для литералов карты. Для литералов карты все элементы должны иметь ключ. Ошибочно указывать несколько элементов с одинаковым именем поля или значением константного ключа.

Для литералов структур (struct literals) применяются следующие правила:

  • Ключ должен быть именем поля, объявленным в типе структуры.
  • Список элементов, который не содержит ключей, должен перечислять элемент для каждого поля структуры в порядке, в котором поля объявлены.
  • Если какой-либо один элемент имеет ключ, тогда каждый элемент должен иметь ключ.
  • Список элементов, содержащий ключи, не обязательно должен иметь элемент для каждого поля структуры. Пропущенные поля получают нулевое значение для этого поля.
  • Литерал может опускать список элементов; такой литерал дает нулевое значение для своего типа.
  • Ошибочно указывать элемент для не экспортированного поля структуры, принадлежащей другому пакету.

Даны объявления

type Point3D struct { x, y, z float64 }
type Line struct { p, q Point3D }

мы можем написать

// нулевое значение для Point3D
origin := Point3D{}   

// нулевое значение для line.q.x                      
line := Line{origin, Point3D{y: -4, z: 12.3}}  

Для литералов массива и среза (slice) применяются следующие правила:

  • Каждый элемент имеет связанный целочисленный индекс, отмечающий его положение в массиве.
  • Элемент с ключом использует ключ в качестве индекса. Ключ должен быть неотрицательной константой, представимой значением типа int; и если он типизирован, это должен быть целочисленный тип (integer type).
  • Элемент без ключа использует индекс предыдущего элемента плюс один. Если у первого элемента нет ключа, его индекс равен нулю.

Взятие адреса составного литерала генерирует указатель на уникальную переменную, инициализированную значением литерала.

var pointer *Point3D = &Point3D{y: 1000}

Длина литерала массива - это длина, указанная в типе литерала. Если в литерале указано меньше элементов, чем длина, то для отсутствующих элементов устанавливается нулевое значение для типа элемента массива. Ошибочно предоставлять элементам значения индекса за пределами диапазона индекса массива. Обозначение ... определяет длину массива, равную максимальному индексу элемента плюс один.

buffer := [10]string{}             // len(buffer) == 10
intSet := [6]int{1, 2, 3, 5}       // len(intSet) == 6
days := [...]string{"Sat", "Sun"}  // len(days) == 2

Литерал среза (slice literal) описывает весь литерал базового массива. Таким образом, длина и емкость литерала среза - это максимальный индекс элемента плюс один. Литерал среза имеет вид

[]T{x1, x2, … xn}

и является сокращением для операции создания среза, примененной к массиву:

tmp := [n]T{x1, x2, … xn}
tmp[0 : n]

Внутри составного литерала массива, среза (slice) или карты (map) типа T элементы или ключи карты, которые сами являются составными литералами, могут исключать соответствующий литеральный тип, если он идентичен типу элемента или ключа T. Аналогично, элементы или ключи, которые являются адресами составных литералов могут исключать &T, когда тип элемента или ключа равен *T.

[...]Point{{1.5, -3.5}, {0, 0}}     // то же самое что и [...]Point{Point{1.5, -3.5}, Point{0, 0}}
[][]int{{1, 2, 3}, {4, 5}}          // то же самое что и [][]int{[]int{1, 2, 3}, []int{4, 5}}
[][]Point{{{0, 1}, {1, 2}}}         // то же самое что и [][]Point{[]Point{Point{0, 1}, Point{1, 2}}}
map[string]Point{"orig": {0, 0}}    // то же самое что и map[string]Point{"orig": Point{0, 0}}
map[Point]string{{0, 0}: "orig"}    // то же самое что и map[Point]string{Point{0, 0}: "orig"}

type PPoint *Point
[2]*Point{{1.5, -3.5}, {}}          // то же самое что и [2]*Point{&Point{1.5, -3.5}, &Point{}}
[2]PPoint{{1.5, -3.5}, {}}          // то же самое что и [2]PPoint{PPoint(&Point{1.5, -3.5}), PPoint(&Point{})}

Неоднозначность синтаксического анализа возникает, когда составной литерал, использующий форму TypeName типа LiteralType, появляется в качестве операнда между ключевым словом и открывающей фигурной скобкой блока оператора «if», «for» или «switch», и составной литерал не заключен в круглые скобки, квадратные или фигурные скобки. В этом редком случае вводная скобка литерала ошибочно анализируется как вводящая блок операторов. Чтобы устранить неоднозначность, составной литерал должен быть заключен в скобки.

if x == (T{a,b,c}[i]) { … }
if (x == T{a,b,c}[i]) { … }

Примеры валидных литералов массива, среза и карты:

// список простых чисел
primes := []int{2, 3, 5, 7, 9, 2147483647}
// vowels[ch] равен true если ch это гласная
vowels := [128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true}

// массив [10]float32{-1, 0, 0, 0, -0.1, -0.1, 0, 0, 0, -1}
filter := [10]float32{-1, 4: -0.1, -0.1, 9: -1}

// частота в Hz для ровной шкалы (equal-tempered scale) (A4 = 440Hz)
noteFrequency := map[string]float32{
  "C0": 16.35, "D0": 18.35, "E0": 20.60, "F0": 21.83,
  "G0": 24.50, "A0": 27.50, "B0": 30.87,
}


Читайте также:


Комментариев нет:

Отправить комментарий