суббота, 22 июня 2019 г.

Спецификация Go: пакеты

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

Организация исходного файла

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

SourceFile = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .

Пункт package

Пункт package начинает каждый исходный файл и определяет пакет, к которому принадлежит файл.

PackageClause  = "package" PackageName .
PackageName    = identifier .

PackageName не должно быть пустым идентификатором. Пример:

package math

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

Import объявления

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

ImportDecl = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
ImportSpec = [ "." | PackageName ] ImportPath .
ImportPath = string_lit .

PackageName используется в квалифицированных идентификаторах для доступа к экспортированным идентификаторам пакета в исходном импортирующем файле. Это объявлено в блоке файла. Если PackageName опущено, по умолчанию используется идентификатор, указанный в предложении пакета импортируемого пакета. Если вместо имени появляется явный период (.), все экспортированные идентификаторы пакета, объявленные в блоке пакета этого пакета, будут объявлены в блоке файла импортирующего исходного файла и должны быть доступны без квалификатора.

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

Ограничение реализации: компилятор может ограничивать ImportPaths непустыми строками, используя только символы, принадлежащие общим категориям Unicode L, M, N, P и S (графические символы без пробелов), а также может исключать символы !"#$%&'()*,:;<=>?[\]^`{|} и символ замены Unicode U+FFFD.

Предположим, что мы скомпилировали пакет, содержащий выражение пакета package math, которое экспортирует функцию Sin, и установили скомпилированный пакет в файл, обозначенный как «lib/math». В следующей таблице показано, как обращаться к Sin в файлах, которые импортируют пакет после различных типов объявлений импорта.

Import declaration          локальное имя Sin

import   "lib/math"         math.Sin
import m "lib/math"         m.Sin
import . "lib/math"         Sin

Декларация импорта объявляет отношение зависимости между импортирующим и импортированным пакетом. Запрещается импортировать сам пакет, прямо или косвенно, или напрямую импортировать пакет без ссылки на какие-либо экспортируемые идентификаторы. Чтобы импортировать пакет исключительно для его побочных эффектов (инициализация), используйте пустой идентификатор в качестве явного имени пакета:

import _ "lib/math"

Пример пакета

Вот полный Go пакет, который реализует конкуррентный отсеиватель простых чисел.

package main

import "fmt"

// Отправляем последовательность 2, 3, 4, … в канал 'ch'.
func generate(ch chan<- int) {
  for i := 2; ; i++ {
    ch <- i  // Отправляем 'i' в канал 'ch'.
  }
}

// Копируем значения из канала 'src' в канал 'dst',
// удаляем те что делимы на 'prime'.
func filter(src <-chan int, dst chan<- int, prime int) {
  for i := range src {  // Проходим в цикле по значениям 
                        // полученным из 'src'.
    if i%prime != 0 {
      dst <- i  // Отправляем 'i' в канал 'dst'.
    }
  }
}

// Отсеиватель простых чисел: 
// Гирляндный фильтр работает вместе.
func sieve() {
  ch := make(chan int)  // Создаем новый канал.
  go generate(ch)       // Запускаем generate() 
                        // как подпроцесс.
  for {
    prime := <-ch
    fmt.Print(prime, "\n")
    ch1 := make(chan int)
    go filter(ch, ch1, prime)
    ch = ch1
  }
}

func main() {
  sieve()
}


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


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

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