Golang вопросы собеседований
13.5K subscribers
631 photos
3 videos
1 file
433 links
@notxxx1 - админ

@Golang_google - Golang для разработчиков

@itchannels_telegram - 🔥лучшие из ит

@golangl - chat

@golangtests - golang tests

@golang_jobsgo - go chat jobs

@ai_machinelearning_big_data - AI

@data_analysis_ml

РКН: clck.ru/3FmtKd
加入频道
👣 Задача. Нам дан связный список целых чисел, отсортированных в порядке возрастания. Наша задача – удалить те узлы, значения которых фигурируют в списке более одного раза. Нужно не только создать список уникальных значений, но и полностью удалить все прочие узлы с этими значениями.

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

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

В контексте поставленной задачи
давайте продумаем каждую ситуацию, в которой может оказаться узел, и как она соотносится с фактом потенциального дублирования конкретного значения:
•Недублирующийся узел указывает на дублирующийся узел
•Дублирующийся узел указывает на дублирующийся узел
•Недублирующийся узел указывает на дублирующийся узел
•Дублирующийся узел указывает на недублирующийся узел


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

Но задумайтесь, что случится, если вы достигнете конечного (nil), имея строку дублей. Допустим, мы работаем с 2 -> 3 -> 3 -> nil, давайте разберём вышеприведённые случаи узел за узлом:

2 -> 3

2 не равно 3, поэтому здесь мы никаких изменений в узел вносить не будем и вновь вызовем функцию, на этот раз передав ей в качестве аргумента узел '3'.

3 -> 3
a b


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

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

3 -> nil
b

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

Вот что можно сделать для решения этой проблемы: мы можем не только полагаться на флаг, сигнализирующий о дубле и посылаемый вниз по пути с рекурсивным изменением. Вдобавок давайте создадим флаг, сигнализирующий, не всплыли ли мы вверх с того уровня, где было дублирующееся значение, смежное с nil. Таким образом, у нас сохранится уловка, позволяющая добраться до nil, и вместе с тем мы сможем безопасно перезаписывать указатели. А если бы мы достигли уровня, имея этот флаг со значением true, мы переключаем узлы и устанавливаем его в false – так сообщается, что дубля в конце у нас уже нет.

📌Решение

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Задача: Конференц-залы II.

Сложность задачи: Средняя

Условие задачи:
Дан массив интервалов времени проведения совещаний, intervals, где intervals[i] = [start(i), end(i)]. Найдите минимальное требуемое количество конференц-залов.

Пример:
Ввод: intervals = [[0,30],[5,10],[15,20]]
Вывод: 2

Ввод: intervals = [[7,10],[2,4]]
Вывод: 1

📌Решение

Пишите свое решение в комментариях👇

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
💡 Задача: Самые дешевые авиабилеты в пределах K остановок

Условие: Имеется n городов, соединенных некоторым количеством рейсов. Вам дан массив flights, где flights[i] = [fromi, toi, pricei] означает, что существует рейс из города fromi в город toi со стоимостью pricei.

Также даны три целых числа src, dst и k, возвращаем самую дешевую цену из src в dst с не более чем k остановками. Если такого маршрута не существует, возвращается -1.

Пример:

Ввод:
n = 4, flights = [[0,1,100],[1,2,100],[2,0,100],[1,3,600],[2,3,200]], src = 0, dst = 3, k = 1
Вывод: 700

Ввод: n = 3, flights = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 1
Вывод: 200

Решение

Пишите свое решение в комментариях👇

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
💡 Задача: Лучшая команда, в которой нет конфликтов

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

Однако в баскетбольной команде не должно быть конфликтов. Конфликт возникает, если младший игрок набрал строго больше очков, чем старший. Конфликт не возникает между игроками одного возраста.

Задав два списка, scores и ages, в которых каждый scores[i] и ages[i] представляет собой счет и возраст i-го игрока соответственно, верните наибольший общий балл среди всех возможных баскетбольных команд.

Пример:

Ввод:
scores = [1,3,5,10,15], ages = [1,2,3,4,5]
Вывод: 34

Ввод: scores = [4,5,6,5], ages = [2,1,2,1]
Вывод: 16

Пишите свое решение в комментариях👇

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Какие рекомендации по безопасности нужно учитывать при разработке Golang приложений?

При разработке Golang приложений необходимо учитывать следующие рекомендации по безопасности:

Валидация ввода: Никогда не доверяйте входным данным. Всегда проводите валидацию и фильтрацию пользовательского ввода, чтобы предотвратить атаки типа SQL-инъекции, XSS и другие уязвимости.

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

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

Защита от утечек памяти: Убедитесь, что ваши приложения Golang не страдают от утечек памяти. Правильно управляйте ресурсами и освобождайте память после использования.

Шифрование данных: При передаче и хранении конфиденциальных данных используйте шифрование для защиты данных от несанкционированного доступа.

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

Пишите свой ответ в комментариях👇

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Что такое Overhead от стандартного профайлера?
Стандартный профайлер в Go имеет некоторый незначительный overhead, который может оказать влияние на производительность вашего приложения. Однако этот overhead обычно незначительный и не должен существенно замедлять ваше приложение.

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

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

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

Если вы хотите более точно измерить overhead профайлера в вашем конкретном случае, вы можете использовать инструменты профилирования, такие как go tool pprof, чтобы анализировать профилировочные данные и определить, какое влияние профайлер оказывает на производительность вашего приложения.

В целом, стандартный профайлер в Go предоставляет ценную информацию о производительности вашего приложения, и его overhead обычно не является значительным. Однако, стоит оценить его использование в зависимости от конкретной ситуации и требований вашего приложения.

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Обновленный гайд с примерами от команды Go по использованию profile-guided optimization для версии 1.21.

https://go.dev/blog/pgo

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
🎉 Релиз Go 1.21.1 и 1.20.8

🔐 Новые релизы включают исправления безопасности для cmd/go (CVE-2023-39320), html/template (CVE-2023-39318, CVE-2023-39319) и crypto/tls.

🗣 Announcement: https://groups.google.com/g/golang-announce/c/Fm51GRLNRvM

⬇️ Загрузить: https://go.dev/dl/#go1.21.1

@golang_interview
👣 Стандартный набор метрик prometheus в Go-программе?
В Go-программе для сбора и экспорта метрик в Prometheus обычно используется пакет github.com/prometheus/client_golang/prometheus. Этот пакет предоставляет стандартный набор метрик и инструментов для работы с Prometheus.

Вот несколько основных типов метрик, которые можно использовать с помощью пакета prometheus:

Counter: Счетчики (Counter) представляют собой метрики, которые увеличиваются только вверх и никогда не уменьшаются. Они полезны для подсчета количества событий или запросов. Пример:

counter := prometheus.NewCounter(prometheus.CounterOpts{
Name: "my_counter",
Help: "This is my counter",
})
counter.Inc()

Gauge: Градусники (Gauge) представляют собой метрики, которые могут изменяться вверх и вниз. Они полезны для отслеживания изменяющихся значений, таких как количество активных соединений или текущая нагрузка на систему. Пример:

gauge := prometheus.NewGauge(prometheus.GaugeOpts{
Name: "my_gauge",
Help: "This is my gauge",
})
gauge.Set(42)

Histogram: Гистограммы (Histogram) представляют собой метрики, которые измеряют распределение значений в заданном диапазоне. Они полезны для измерения времени выполнения операций или размера запросов. Пример:

histogram := prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "my_histogram",
Help: "This is my histogram",
Buckets: []float64{0.1, 0.5, 1, 2, 5},
})
histogram.Observe(0.6)

Summary: Суммары (Summary) также представляют собой метрики, которые измеряют распределение значений, но с учетом квантилей. Они полезны для измерения производительности и времени выполнения операций. Пример:

summary := prometheus.NewSummary(prometheus.SummaryOpts{
Name: "my_summary",
Help: "This is my summary",
})

summary.Observe(2.5)
Кроме того, пакет github.com/prometheus/client_golang/prometheus также предоставляет дополнительные инструменты для регистрации метрик (Register), создания коллекторов (Collector), экспорта метрик (HTTPHandler) и т.д.

Это лишь небольшой обзор стандартного набора метрик, доступных в Go-программе через пакет prometheus. Более подробную информацию о создании и использовании метрик в Prometheus вы можете найти в официальной документации Prometheus для Go-программы: https://pkg.go.dev/github.com/prometheus/client_golang/prometheus

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

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
📌 Предположим, ваша функция должна возвращать детализированные Recoverable и Fatal ошибки. Как это реализовано в пакете net? Как это надо делать в современном Go

В пакете net в стандартной библиотеке Go возвращение детализированных ошибок реализовано с помощью ошибок, которые являются значениями пользовательских типов, реализующих интерфейс net.Error. Этот интерфейс определен следующим образом:

type Error interface {
error
Timeout() bool // возвращает true, если ошибка связана с таймаутом
Temporary() bool // возвращает true, если ошибка является временной
}

Интерфейс net.Error включает методы Timeout() и Temporary(), которые позволяют клиентам проверять, является ли ошибка связанной с таймаутом или временной. Это позволяет клиентам принимать соответствующие действия в зависимости от типа ошибки.

В современном Go, начиная с версии 1.13, рекомендуется использовать синтаксис ошибок, основанный на типах, с помощью применения пакета errors и интерфейса fmt.Formatter. Этот подход позволяет создавать богатые по содержанию и информативные ошибки, которые могут хранить дополнительные данные и форматироваться в нужном формате при выводе.

Вот пример, как можно реализовать детализированные ошибки в современном Go:

package main

import (
"errors"
"fmt"
)

type RecoverableError struct {
Message string
}

func (e *RecoverableError) Error() string {
return fmt.Sprintf("RecoverableError: %s", e.Message)
}

type FatalError struct {
Message string
}

func (e *FatalError) Error() string {
return fmt.Sprintf("FatalError: %s", e.Message)
}

func main() {
err := doSomething()

switch {
case errors.Is(err, &RecoverableError{}):
fmt.Println("Recoverable error:", err)
case errors.Is(err, &FatalError{}):
fmt.Println("Fatal error:", err)
default:
fmt.Println("Unknown error:", err)
}
}

func doSomething() error {
// Ваша логика здесь

return &RecoverableError{Message: "Something went wrong"}
}

В этом примере у нас есть два типа ошибок: RecoverableError и FatalError. Оба типа реализуют интерфейс error и предоставляют дополнительные данные в своих методах Error(). Функция doSomething() возвращает ошибку типа RecoverableError.

Функция main() использует функцию errors.Is() для проверки типа ошибки и выполняет соответствующие действия в зависимости от типа ошибки.

Важно отметить, что настройка и использование детализированных ошибок может различаться в зависимости от конкретной задачи и предпочтений разработчика. Рекомендуется следовать общим рекомендациям и принципам языка Go при работе с обработкой ошибок. Например, рекомендуется использовать типы ошибок, которые являются значениями пользовательских типов и реализуют интерфейс error, чтобы их можно было легко сравнивать с помощью функции errors.Is(), как показано в примере выше. Также рекомендуется предоставлять информативные сообщения об ошибках, чтобы облегчить отладку и понимание причины ошибки.

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

@golang_interview
👣 Как встроить стандартный профайлер в свое приложение?
В Go вы можете использовать встроенный профайлер для сбора информации о производительности вашего приложения. Для этого вам нужно импортировать пакет net/http/pprof и зарегистрировать его обработчики HTTP.

Вот простой пример того, как встроить стандартный профайлер в свое приложение:

package main

import (
"log"
"net/http"
_ "net/http/pprof"
)

func main() {
// Регистрируем обработчики профайлера
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()

// Ваше приложение

// ...
}
В этом примере мы импортируем пакет net/http/pprof и регистрируем его обработчики HTTP с помощью функции http.ListenAndServe(). Обработчики будут доступны по адресу localhost:6060.

После запуска вашего приложения вы можете открыть веб-браузер и перейти по адресу http://localhost:6060/debug/pprof/, чтобы получить доступ к различным профилировочным эндпоинтам. Например:

http://localhost:6060/debug/pprof/profile - профилирование CPU

http://localhost:6060/debug/pprof/heap - профилирование памяти

http://localhost:6060/debug/pprof/block - профилирование блокировок

http://localhost:6060/debug/pprof/goroutine - профилирование горутин

Вы можете использовать инструменты, такие как go tool pprof, чтобы анализировать собранные профилировочные данные и получать информацию о времени выполнения, утечках памяти, блокировках и других аспектах производительности вашего приложения.

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

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Какие типы мьютексов предоставляет stdlib?

Стандартная библиотека Go (stdlib) предоставляет два типа мьютексов для синхронизации доступа к общим ресурсам:

sync.Mutex: Это самый простой тип мьютекса, который предоставляется стандартной библиотекой Go. Он обеспечивает эксклюзивную блокировку (exclusive lock), что означает, что только одна горутина может захватить мьютекс и получить доступ к общему ресурсу. Если другая горутина пытается захватить мьютекс, пока он уже заблокирован, она будет ожидать его освобождения.

Пример использования sync.Mutex:

var mutex sync.Mutex
var sharedResource int

// Горутина 1
mutex.Lock()
sharedResource = 42
mutex.Unlock()

// Горутина 2
mutex.Lock()
fmt.Println(sharedResource)
mutex.Unlock()

sync.RWMutex: Этот тип мьютекса, называемый также мьютексом чтения/записи (read/write mutex), обеспечивает более гибкую блокировку. Он позволяет нескольким горутинам захватывать мьютекс только для чтения (shared lock), разрешая параллельный доступ к общему ресурсу для чтения. Однако, при записи (exclusive lock) мьютекс блокируется, и другие горутины должны ждать его освобождения.

Пример использования sync.RWMutex:

var rwMutex sync.RWMutex
var sharedResource int

// Горутина 1 для записи
rwMutex.Lock()
sharedResource = 42
rwMutex.Unlock()

// Горутина 2 для чтения
rwMutex.RLock()
fmt.Println(sharedResource)
rwMutex.RUnlock()


Оба типа мьютексов, sync.Mutex и sync.RWMutex, являются потокобезопасными и предоставляют механизмы для синхронизации доступа к общим ресурсам в многопоточной среде. Выбор между ними зависит от требований вашего кода: если вам нужна только эксклюзивная блокировка, используйте sync.Mutex, а если вам требуется поддержка одновременного чтения и блокировки записи, используйте sync.RWMutex.

Пишите свой ответ в комментариях👇

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Fixing For Loops in Go 1.22

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

https://go.dev/blog/loopvar-preview

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Каков порядок перебора map?

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

Если вам требуется определенный порядок элементов, вам необходимо явно сортировать ключи или значения перед их использованием. Например, вы можете сначала извлечь ключи из map в срез (slice), отсортировать этот срез и затем итерироваться по отсортированным ключам для доступа к значениям.

Пример сортировки ключей map перед итерацией:

m := map[string]int{
"banana": 2,
"apple": 1,
"cherry": 3,
}

keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}

sort.Strings(keys)

for _, k := range keys {
fmt.Println(k, m[k])
}

В этом примере мы создаем map с неупорядоченными ключами и значениями. Затем мы извлекаем ключи из map в срез keys, сортируем этот срез с помощью sort.Strings, а затем итерируемся по отсортированным ключам, чтобы получить доступ к соответствующим значениям.

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

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Где следует поместить описание интерфейса: в пакете с реализацией или в пакете, где этот интерфейс используется? Почему?

Описание интерфейса следует помещать в пакете, где этот интерфейс используется, а не в пакете с реализацией. Это согласуется с принципом разделения интерфейса и реализации (Interface Segregation Principle) из принципов SOLID.

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

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

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

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Как вы отсортируете массив структур по алфавиту по полю Name?

Для сортировки массива структур по алфавиту по полю Name в Go, вы можете использовать интерфейс sort.Interface и функцию sort.Sort() из пакета sort.

Вот пример кода, который демонстрирует, как отсортировать массив структур по полю Name:

package main

import (
"fmt"
"sort"
)

type Person struct {
Name string
Age int
}

type ByName []Person

func (a ByName) Len() int { return len(a) }
func (a ByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByName) Less(i, j int) bool { return a[i].Name < a[j].Name }

func main() {
people := []Person{
{Name: "Alice", Age: 25},
{Name: "Charlie", Age: 30},
{Name: "Bob", Age: 20},
}

sort.Sort(ByName(people))

for _, person := range people {
fmt.Println(person.Name, person.Age)
}
}
В этом примере мы определяем тип Person для представления структуры человека с полями Name и Age. Затем мы определяем тип ByName, который является срезом структур Person. Мы также реализуем методы Len(), Swap(), и Less() для типа ByName, чтобы он соответствовал интерфейсу sort.Interface.

Затем мы создаем срез структур Person и заполняем его некоторыми значениями. Далее мы вызываем sort.Sort(ByName(people)), чтобы отсортировать срез структур по полю Name. Наконец, мы проходимся по отсортированному срезу и выводим отсортированные значения.

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

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Можно ли использовать один и тот же буфер []byte в нескольких горутинах?

В Go можно использовать один и тот же буфер []byte в нескольких горутинах, но это требует дополнительных мер предосторожности для обеспечения безопасности и избежания гонок данных (data races).

Одним из подходов к безопасному использованию одного буфера []byte в нескольких горутинах является синхронизация доступа к нему с помощью мьютексов или других механизмов синхронизации, таких как sync.RWMutex. Мьютексы позволяют горутинам синхронизировать свой доступ к общему ресурсу и предотвращать одновременную запись или чтение из буфера.

Вот пример использования мьютекса для безопасного доступа к общему буферу []byte:

var bufferMutex sync.Mutex
var buffer []byte

func writeToBuffer(data []byte) {
bufferMutex.Lock()
defer bufferMutex.Unlock()
// Здесь происходит запись в буфер
// ...
}

func readFromBuffer() []byte {
bufferMutex.Lock()
defer bufferMutex.Unlock()
// Здесь происходит чтение из буфера
// ...
return buffer
}

В этом примере мьютекс bufferMutex используется для блокировки доступа к буферу []byte перед его записью или чтением. Это гарантирует, что только одна горутина имеет доступ к буферу в определенный момент времени.

Однако, важно помнить, что использование общего буфера []byte может быть проблематичным, особенно если горутины модифицируют его содержимое. Если горутины выполняют параллельные записи в буфер, могут возникнуть состояния гонки и непредсказуемые результаты. В таких случаях рекомендуется использовать другие механизмы синхронизации, такие как каналы (channels) или пулы буферов, чтобы гарантировать безопасность и предсказуемость работы с данными в многопоточной среде.

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

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Способы поиска проблем производительности на проде?

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

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

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

Логирование: Разместите подробные и информативные логи в вашем приложении. Логирование может помочь идентифицировать узкие места в вашем коде или обнаружить неожиданные события или проблемы использования ресурсов.

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

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

Масштабирование и горизонтальное масштабирование: Если ваше приложение испытывает проблемы с производительностью на проде, рассмотрите возможность масштабирования, как вертикального (увеличение ресурсов на одном сервере) и горизонтального (добавление дополнительных серверов). Это может помочь распределить нагрузку и повысить производительность.

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

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

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

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

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
👣 Сколько времени в минутах займет у вас написание процедуры обращения односвязного списка?

package main

import "fmt"

type Node struct {
Val int
Next *Node
}

func reverseLinkedList(head *Node) *Node {
var prev *Node
curr := head
for curr != nil {
Next := curr.Next
curr.Next = prev
prev = curr
curr = Next
}
return prev
}

// можно сократить curr
// func reverseLinkedList(head *Node) *Node {
// var prev *Node
// for head != nil {
// Next := head.Next
// head.Next = prev
// prev = head
// head = Next
// }
// return prev
// }

// можно сократить Next (через множественное присваивание)
// func reverseLinkedList(head *Node) *Node {
// var prev *Node
// for head != nil {
// head.Next, prev, head = prev, head, head.Next
// }
// return prev
// }

// или создавая новые узлы
// func reverseLinkedList(head *Node) *Node {
// var result *Node
// curr := head
// for curr != nil {
// result = &Node{curr.Val, result}
// curr = curr.Next
// }
// return result
// }

func printLinkedList(head *Node) {
current := head
for current != nil {
fmt.Printf("%d -> ", current.Val)
current = current.Next
}
fmt.Println("nil")
}

func main() {
node1 := &Node{Val: 1}
node2 := &Node{Val: 2}
node3 := &Node{Val: 3}
node4 := &Node{Val: 4}
node5 := &Node{Val: 5}
node1.Next = node2
node2.Next = node3
node3.Next = node4
node4.Next = node5
fmt.Println("Исходный список:")
printLinkedList(node1)
reversedHead := reverseLinkedList(node1)
fmt.Println("Обращенный список:")
printLinkedList(reversedHead)
}


Пишите свое решение в комментариях👇

@golang_interview
Please open Telegram to view this post
VIEW IN TELEGRAM
⚽️ Задача: Где приземлится мяч

Сложность: Средняя

Условие задачи: Дан двумерный массив, определяющий короб, а также n-ое количество мячей.

Каждая ячейка данной коробки имеет диагональную перегородку, которая может перенаправлять движение мяча.

- Перегородка ячейки типа "левый верхний угол —> правый нижний угол" имеет представление 1.
- Перегородка ячейки типа "правый верхний угол —> левый нижний угол" имеет представление -1.

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

Необходимо вернуть массив, который будет показывать добрался ли i-ый мяч до дна коробки (интерпретируется 1) или же уткнулся в стену (-1).

Пример:

Ввод:
grid = [[1,1,1,-1,-1],[1,1,1,-1,-1],[-1,-1,-1,1,1],[1,1,1,1,-1],[-1,-1,-1,-1,-1]]
Вывод: [1,-1,-1,-1,-1]
Объяснение: *во вложении

Ввод: grid = [[-1]]
Вывод: [-1]
Объяснение: мяч уткнется в левую стенку коробки

📌 Решение

Пишите свое решение в комментариях👇

@golang_interview