Thank Go!

Description
Неожиданный взгляд на язык программирования Go. Конструктив от @mikeberezin с нотками сарказма от @nalgeon
Advertising
We recommend to visit

Официальный новостной канал криптобиржи OKX | www.okx.com на русском языке.

💬 Комьюнити: t.me/okx_russian

👨‍💻 Поддержка: [email protected]

АДМИН: @DaniiOKX
Маркетинг: @CoffeeTrends

Last updated 2 weeks, 3 days ago

Here in simple language about TON and crypto

Founder: @metasalience
contact : @deftalk_bot

Last updated 3 months, 2 weeks ago

Канал о TON и все что с ним связано:
1. Аналитика
2. Инсайды
3. Авторское мнение

Ведро для спама: @ton_telegrambot

Бот с курсами криптовалют: @TonometerBot

Чат: @chaTON_ru

Админ: @filimono

Last updated 2 weeks, 5 days ago

2 weeks, 1 day ago

Заглушить логи

С появлением пакета log/slog в Go 1.21 наконец-то стало возможно нормально вести журналы без использования внешних библиотек.

Если еще не пробовали, то рекомендую простенький туториал и доку, она понятная и с примерами.

log := slog.New( slog.NewTextHandler(os.Stdout, nil), ) log.Info("operation", "count", 3, "took", 50*time.Millisecond)

time=2024\-12\-09T10:20:47.660+00:00 level=INFO msg=operation count=3 took=50ms

Но вообще сказать я хотел не о самом slog, а о том, как в нем заглушить логи (например, для тестов или бенчмарков).

Сделать это несложно — просто используйте io.Discard в качестве приемника для slog.TextHandler:

log := slog.New( slog.NewTextHandler(io.Discard, nil), ) log.Info("Prints nothing")

Удобно!

1 month ago

Скорость алгоритмов: O(1)

O(1), так же известно как «константное время». Самый лучший вариант, скорость алгоритма не зависит от количества ~~котиков~~ входных данных.

*🐾 Пример*

Вы — счастливый обладатель N котиков. Каждый котик знает, как его зовут. Если позвать «Феликс!», то прибежит только он, а остальным N-1 жопкам пофиг.

***🐈*** ***🐈*** ***🐈*** ***🐈*** ***🐈*** ***🐈*** ***🐈‍⬛*** ***🐈*** ***🐈*** ***🐈*** ↓ мяу!

В жизни время выполнения O(1) обычно встречается при работе со срезами и картами.

Срезы

У среза известен адрес в памяти первого элемента и размер каждого элемента. Следовательно, адрес i-го элемента тоже известен (first + i*size) — а значит, доступ к i-му элементу выполняется за константное время. Изменение i-го элемента аналогично — тоже O(1).

s := make([]int, 1e6) i := rand.IntN(1e6) s[i] = 42 // O(1) fmt.Println(s[i]) // O(1)

С добавлением и удалением элементов сложнее — они рано или поздно приводят к созданию нового массива под срезом — а это уже операция O(n). Но если аккуратно все посчитать, то получится, что в среднем добавление/удаление тоже константные.

Карты

Не вдаваясь в подробности скажу, что за картой тоже скрывается массив. Грубо говоря, когда вы пишете m["answer"] = 42, то Go превращает "answer" в целое число (хеш строки), после чего считает по нему нужный индекс в массиве. По этому индексу и находится значение 42.

d := map[string]int{} d["answer"] = 42 // O(1) fmt.Println(d["answer"]) // O(1)

Поэтому доступ к элементу карты по ключу — тоже O(1).

🐈

1 month, 1 week ago

Скорость алгоритмов: O-нотация

Скорость работы алгоритмов принято оценивать в О-нотации: у медленного она «о», у более быстрого «ооо», у самого быстрого «ооооо ваще огонь».

Вру, конечно.

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

Алгоритм, понятное дело, должен работать одинаковым образом что для 10 котиков, что для 100, что для 10000. Оценка же скорости нужна одна. Поэтому оценивают не конкретное количество операций, а количество операций относительно количества входных данных (котов в нашем случае).

Если у вас n котов, то чтобы определить самого жирненького, придется выполнить n взвешиваний. В таком случае говорят, что скорость работы алгоритма — O(n).

В реальности действий всегда будет не n, а несколько больше. Например, сначала нужно откалибровать весы, а в конце убрать их обратно в шкаф. Получается уже n+2 действия. Но поскольку дополнительных действий константное количество, их в оценке не учитывают: O(n+2) = O(n)

А что делать, если котики отказываются взвешиваться без рыбов? Тогда на каждого кота приходится 2 действия: выдать рыбку и взвесить, поэтому общее количество действий 2n. Но фактор 2× тоже константный, поэтому и его в оценке не учитывают: O(2n) = O(n)

Ровно так это работает и в коде:

var fattest Cat for \_, cat := range cats { if cat.weight > fattest.weight { fattest = cat } }

Здесь на каждого кота выполняется до трех действий:

  1. Выбрать очередного кота из среза
  2. Сравнить вес кота с весом жирнейшего
  3. (возможно) Обновить жирнейшего

То есть действий может быть до 3n+1 (+1 — инициализация переменной fattest). Но скорость работы алгоритма — O(n)

Таким образом, в O-нотации любые константные факторы игнорируются. Есть и другие нюансы, но с ними разберемся дальше по ходу дела.

P.S. Я пока не уверен, что реально нужна серия про скорость алгоритмов — может, для всех это совсем очевидная тема. Посмотрим, как пойдет.

🐈

4 months, 1 week ago

Go 1.23

Тут вышел Go 1.23, ну а мы с вами успели разобрать основные изменения заранее:

Итераторы
Таймеры
Уникальные значения
Скопировать каталог
Куки

Все вместе с интерактивными примерами:
https://antonz.org/go-1-23

4 months, 2 weeks ago

Большая крыса Go

Прежде чем вы решите, что я сошел с ума — речь на самом деле о типе big.Rat.

В отличие от float64, он позволяет работать с обыкновенными дробями (a/b) без потери точности.

Например, из школьного курса математики мы знаем, что 1/10 + 2/10 = 3/10. Однако, float64 другого мнения:

x := 0.1 y := 0.2 fmt.Println(x + y) // 0.30000000000000004

А вот big.Rat справляется с такими вычислениями без проблем:

x := big.NewRat(1, 10) y := big.NewRat(2, 10) z := new(big.Rat) z.Add(x, y) fmt.Println(z) // 3/10

Если вдруг придется работать с обыкновенными дробями — имейте «крысу» в виду.

4 months, 2 weeks ago

Статический HTTP-сервер

Вы, наверно, слышали про встроенный в Python статический сервер:

python \-m http.server 8080

На Go его можно реализовать в десять строчек кода (плюс импорты):

```
func main() {
port := "8000"
if len(os.Args) > 1 {
port = os.Args[1]
}

fs := http.FileServer(http.Dir("."))
http.Handle("/", fs)

log.Printf("Serving HTTP on port %s...\n", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}
```

И запускать вот так:

go run http.go 8080

5 months, 3 weeks ago

Git в примерах

Зашел я на Степик в новые курсы, а там «Основы Git». В связи этим вспомнил, что у меня тоже есть интерактивная книга / сборник рецептов по гиту, называется Git by example.

Удобный краткий формат с конкретными примерами, я сам туда постоянно подглядываю (особенно в раздел undo).

Загляните и вы, если не обзавелись еще черным поясом по гиту.

https://antonz.org/git-by-example

P.S. А вы же знаете, что по Go тоже такая есть?

5 months, 3 weeks ago

Безопасные ворота

Вот три способа безопасно закрыть ворота, один другого краше.

➊ sync.Mutex

Дубовый, но надежный способ. Защищаем изменение логического поля closed мьютексом:

```
type Gates struct {
// признак закрытия ворот
closed bool
// мьютекс для защиты closed
mu sync.Mutex
}

func (g *Gates) Close() {
g.mu.Lock()
defer g.mu.Unlock()
if g.closed {
// игнорируем повторное закрытие
return
}
// закрыть ворота
g.closed = true
// освободить ресурсы
}
```

➋ atomic.Bool

Compare-and-set на атомарном bool гарантирует, что только одна горутина сможет поменять значение с false на true:

```
type Gates struct {
// признак закрытия ворот
closed atomic.Bool
}

func (g *Gates) Close() {
if !g.closed.CompareAndSwap(false, true) {
// игнорируем повторное закрытие
return
}
// закрыли ворота,
// можно освободить ресурсы
}
```

➌ sync.Once

Once.Do гарантирует однократное выполнение в конкурентной среде, поэтому не приходится даже явно хранить состояние:

```
type Gates struct {
// гарантирует однократное выполнение
once sync.Once
}

func (g *Gates) Close() {
g.once.Do(func() {
// освободить ресурсы
})
}
```

Правда, такие ворота уже не получится открыть обратно, в отличие от предыдущих вариантов.

Кто ваш любимчик? Поделитесь в комментариях.

5 months, 3 weeks ago

Опасные ворота

Вот наши ворота:

```
type Gates struct {
// признак закрытия ворот
closed chan struct{}
}

func (g *Gates) Close() {
select {
case <-g.closed:
// игнорируем повторное закрытие
return
default:
// закрыть ворота
close(g.closed)
// освободить ресурсы
}
}
```

Метод Close — небезопасный. Если две горутины одновременно вызовут Close, обе могут провалиться в default-ветку селекта, обе попытаются закрыть канал, и второе закрытие приведет к панике.

Другими словами, здесь гонки на закрытии канала. Селект сам по себе не защищает от гонок. Sad but true.

Что с этим делать — традиционно в следующей заметке.

6 months ago

Баян [::]

Гошный баян (вообще он «полносрезное выражение» или full slice expression, но «баян» мне ближе) имеет такой синтаксис:

s[low : high : max]

Баян создает срез длиной high\-low и емкостью max\-low. Используется крайне редко.

Чтобы понять разницу между обычным срезом и баяном, рассмотрим пример.

Как вы знаете, под каждым срезом лежит массив с данными (сам срез данных не содержит). Обычно этот массив создается неявно, но мы для наглядности сделаем так:

```
arr := [5]int{1, 2, 3}
// [1 2 3 0 0]

s := arr[0:3]
// [1 2 3]

len(s) // 3
cap(s) // 5
```

Срез s указывает на массив arr. Его длина (length) равна 3, а емкость (capacity, размер массива под срезом) равна 5.

Добавление элемента в срез добавляет его в массив, поскольку емкость это позволяет:

```
s = append(s, 4)

fmt.Println(arr)
// [1 2 3 4 0]

fmt.Println(s)
// [1 2 3 4]
```

А вот что будет, если создать срез с помощью баяна:

```
arr := [5]int{1, 2, 3}
// [1 2 3 0 0]

s := arr[0:3:3]
// [1 2 3]

len(s) // 3
cap(s) // 3
```

Все как раньше, только емкость среза равна 3. Поэтому добавление элемента в срез приведет к созданию нового массива под срезом. Исходный массив arr не изменится:

```
s = append(s, 4)

fmt.Println(arr)
// [1 2 3 0 0]

fmt.Println(s)
// [1 2 3 4]
```

Такие дела. За разновидностями баянов приглашаю в комментарии.

?

We recommend to visit

Официальный новостной канал криптобиржи OKX | www.okx.com на русском языке.

💬 Комьюнити: t.me/okx_russian

👨‍💻 Поддержка: [email protected]

АДМИН: @DaniiOKX
Маркетинг: @CoffeeTrends

Last updated 2 weeks, 3 days ago

Here in simple language about TON and crypto

Founder: @metasalience
contact : @deftalk_bot

Last updated 3 months, 2 weeks ago

Канал о TON и все что с ним связано:
1. Аналитика
2. Инсайды
3. Авторское мнение

Ведро для спама: @ton_telegrambot

Бот с курсами криптовалют: @TonometerBot

Чат: @chaTON_ru

Админ: @filimono

Last updated 2 weeks, 5 days ago