Показаны сообщения с ярлыком функции в Go. Показать все сообщения
Показаны сообщения с ярлыком функции в Go. Показать все сообщения

среда, 16 декабря 2020 г.

Go style guides: группировка функций и упорядочение

  • Функции следует отсортировать в приблизительном порядке вызова.
  • Функции в файле должны быть сгруппированы по получателям.

Следовательно, экспортируемые функции должны появляться в файле первыми, после определений struct, const, var.

newXYZ()/NewXYZ() может появиться после определения типа, но перед остальными методами на приемнике.

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

Менее удачный пример:

func (s *something) Cost() {
  return calcCost(s.weights)
}

type something struct{ ... }

func calcCost(n []int) int {...}

func (s *something) Stop() {...}

func newSomething() *something {
    return &something{}
}

Более удачный пример:

type something struct{ ... }

func newSomething() *something {
    return &something{}
}

func (s *something) Cost() {
  return calcCost(s.weights)
}

func (s *something) Stop() {...}

func calcCost(n []int) int {...}


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


воскресенье, 17 мая 2020 г.

Множественное значение в контексте одного значения в Golang

Почему этот код дает ошибку компиляции?

t := time.Parse(time.RFC3339, "2018-04-06T10:49:05Z")
fmt.Println(t)

../main.go:9:17: multiple-value time.Parse() in single-value context

Ответ

Функция time.Parse возвращает два значения, time.Time и error, и вы должны использовать оба.

t, err := time.Parse(time.RFC3339, "2018-04-06T10:49:05Z")
if err != nil {
    // TODO: Обработать ошибку.
}
fmt.Println(t)

2018-04-06 10:49:05 +0000 UTC

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

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

m := map[string]float64{"pi": 3.1416}
_, exists := m["pi"] // exists == true


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


четверг, 14 мая 2020 г.

Как реализовать итератор в Golang

Go имеет встроенный цикл range для перебора срезов, массивов, строк, карт и каналов.

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

Базовый шаблон итератора

// Iterate вызывает функцию f с n = 1, 2 и 3.
func Iterate(f func(n int)) {
    for i := 1; i <= 3; i++ {
        f(i)
    }
}

В использовании:

Iterate(func(n int) { fmt.Println(n) })

1
2
3

Итератор с break

// Iterate вызывает функцию f с n = 1, 2 и 3.
// Если f возвращает true, Iterate возвращается немедленно,
// пропуская все оставшиеся значения.
func Iterate(f func(n int) (skip bool)) {
    for i := 1; i <= 3; i++ {
        if f(i) {
            return
        }
    }
}

В использовании:

Iterate(func(n int) (skip bool) {
 fmt.Println(n)
 return n == 2
})

1
2


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


среда, 13 мая 2020 г.

Анонимные функции и замыкания в Golang

Литерал функции (или лямбда) - это функция без имени.

В этом примере литерал функции передается в качестве аргумента less функции sort.Slice.

func Slice(slice interface{}, less func(i, j int) bool)

people := []string{"Alice", "Bob", "Dave"}
sort.Slice(people, func(i, j int) bool {
    return len(people[i]) < len(people[j])
})
fmt.Println(people)
// Вывод: [Bob Dave Alice]

Вы также можете использовать промежуточную переменную.

people := []string{"Alice", "Bob", "Dave"}
less := func(i, j int) bool {
    return len(people[i]) < len(people[j])
}
sort.Slice(people, less)

Обратите внимание, что функция less является замыканием: она ссылается на переменную people, которая объявлена ​​вне функции.

Замыкания

Литералы функций в Go являются замыканиями: они могут ссылаться на переменные, определенные в окружающей функции. Такие переменные

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

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

// New возвращает функцию Count.
// Count печатает количество раз, которое она был вызван.
func New() (Count func()) {
    n := 0
    return func() {
        n++
        fmt.Println(n)
    }
}

func main() {
    f1, f2 := New(), New()
    f1() // 1
    f2() // 1 (другая n)
    f1() // 2
    f2() // 2
}


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


Типы функций и значения функций в Golang

Типы функций и значения функций могут использоваться и передаваться, как и другие значения:

type Operator func(x float64) float64

// Map применяет op к каждому элементу a.
func Map(op Operator, a []float64) []float64 {
    res := make([]float64, len(a))
    for i, x := range a {
        res[i] = op(x)
    }
    return res
}

func main() {
    op := math.Abs
    a := []float64{1, -2}
    b := Map(op, a)
    fmt.Println(b) // [1 2]

    c := Map(func(x float64) float64 { return 10 * x }, b)
    fmt.Println(c) // [10, 20]
}

Второй вызов Map использует литерал функции (или лямбду).

Тип функции описывает набор всех функций с одинаковыми параметрами и типами результатов.

  • Значение неинициализированной переменной типа функции равно nil.
  • Имена параметров являются необязательными.

Следующие два типа функций идентичны.

func(x, y int) int
func(int, int) int


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


понедельник, 11 мая 2020 г.

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

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

func f() (i int, s string) {
    i = 17
    s = "abc"
    return // то же самое что и return i, s
}

Именованные возвращаемые параметры инициализируются своими нулевыми значениями.

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

Пример

Эта версия io.ReadFull, взятая из Эффективный Go, эффективно использует их.

// ReadFull читает ровно len(buf) байтов из r в buf. 
// Возвращается количество скопированных байтов и ошибка, 
// если было прочитано меньше байтов.
func ReadFull(r Reader, buf []byte) (n int, err error) {
    for len(buf) > 0 && err == nil {
        var nr int
        nr, err = r.Read(buf)
        n += nr
        buf = buf[nr:]
    }
    return
}

Код является и более простым и понятным, с именованными возвращаемыми значениями, которые правильно инициализированы и привязаны к простому return.


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


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

Спецификация Go: передача аргументов в ... параметрах

Последний входящий параметр в сигнатуре функции может иметь тип с ... префиксом. Функция с таким параметром называется variadic и может вызываться с нулевым или большим количеством аргументов для этого параметра.

Если f - это variadic функция, с конечным параметром p типа ... T, то в пределах f тип p эквивалентен типу []T. Если f вызывается без фактических аргументов для p, значение, переданное p, равно nil. В противном случае передаваемое значение является новым срезом типа []T с новым базовым массивом, чьи последовательные элементы являются фактическими аргументами, которые должны быть назначены для T. Следовательно, длина и емкость среза - это количество аргументов, связанных с р и может отличаться для каждого места вызова.

Даны функция и вызовы

func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")

в Greeting, who будет иметь значение nil в первом вызове и []string{"Joe", "Anna", "Eileen"} во втором.

Если последний аргумент присваивается типу среза []T, его можно передать без изменений в качестве значения параметра ... T, если за аргументом следует ... . В этом случае новый срез не создается.

Даны срез s и вызов

s := []string{"James", "Jasmine"}
Greeting("goodbye:", s...)

в Greeting, who будет иметь то же значение, что и s с тем же базовым массивом.


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


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

Спецификация Go: литералы функций

Литерал функции представляет собой анонимную функцию.

FunctionLit = "func" Signature FunctionBody .

func(a, b int, z float64) bool { return a*b < int(z) }

Литерал функции может быть назначен переменной или вызван напрямую.

f := func(x, y int) int { return x + y }
func(ch chan int) { ch <- ACK }(replyChan)

Литералы функций являются замыканиями (closures): они могут ссылаться на переменные, определенные в окружающей функции. Эти переменные затем разделяются между окружающей функцией и литералом функции, и они сохраняются до тех пор, пока они доступны.


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


вторник, 4 июня 2019 г.

Спецификация Go: объявления функций

Объявление функции связывает идентификатор, имя функции, с функцией.

FunctionDecl = "func" FunctionName Signature [ FunctionBody ] .
FunctionName = identifier .
FunctionBody = Block .

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

func IndexRune(s string, r rune) int {
  for i, c := range s {
    if c == r {
      return i
    }
  }
  // недопустимо: пропущен return оператор
}

Объявление функции может опускать тело. Такое объявление предоставляет сигнатуру для функции, реализованной вне Go, такой как процедура сборки (assembly routine).

func min(x int, y int) int {
  if x < y {
    return x
  }
  return y
}

func flushICache(begin, end uintptr)  // реализована внешне


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


вторник, 28 мая 2019 г.

Спецификация Go: типы указателя (pointer types), типы функций (function types)

Типы указателя (pointer types)

Тип указателя обозначает набор всех указателей на переменные данного типа, называемые базовым типом указателя. Значение неинициализированного указателя равно nil.

PointerType = "*" BaseType .
BaseType    = Type .

*Point
*[4]int

Типы функций (function types)

Тип функции (function type) обозначает набор всех функций с одинаковыми параметрами и типами результатов. Значение неинициализированной переменной типа функции равно nil.

FunctionType   = "func" Signature .
Signature      = Parameters [ Result ] .
Result         = Parameters | Type .
Parameters     = "(" [ ParameterList [ "," ] ] ")" .
ParameterList  = ParameterDecl { "," ParameterDecl } .
ParameterDecl  = [ IdentifierList ] [ "..." ] Type .

В списке параметров или результатов имена (IdentifierList) должны либо присутствовать, либо отсутствовать. Если присутствует, каждое имя обозначает один элемент (параметр или результат) указанного типа, и все непустые имена в подписи должны быть уникальными. Если отсутствует, каждый тип обозначает один элемент этого типа. Списки параметров и результатов всегда заключаются в круглые скобки, за исключением того, что если есть ровно один безымянный результат, он может быть записан как тип без скобок.

Последний входящий параметр в сигнатуре функции может иметь тип с ... префиксом. Функция с таким параметром называется variadic и может вызываться с нулевым или большим количеством аргументов для этого параметра.

func()
func(x int) int
func(a, _ int, z float32) bool
func(a, b int, z float32) (bool)
func(prefix string, values ...int)
func(a, b int, z float64, opt ...interface{}) (success bool)
func(int, int, float64) (float64, *[]int)
func(n int) func(p *T)


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


четверг, 14 марта 2019 г.

Go Code Review Comments: Синхронные функции

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

Синхронные функции позволяют локализовать go-процедуры (goroutines) в вызове, что упрощает анализ их времени жизни и позволяет избежать утечек и скачков данных. Их также проще протестировать: вызывающий может передать вход и проверить выход без необходимости опроса или синхронизации.

Если вызывающим абонентам требуется больше конкурентности, они могут легко добавить ее, вызвав функцию из отдельной go-процедуры. Напротив, бывает довольно трудно - иногда невозможно - удалить ненужную конкурентность на вызывающей стороне.


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


четверг, 14 февраля 2019 г.

Go FAQ: Когда параметры функции передаются по значению?

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

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

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


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