Страницы

Поиск по вопросам

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

пятница, 20 марта 2020 г.

Отправка почтовго сообщения из кода

#golang #sendmail


Может кто знает, как отправить сообщение из кода в Go?    


Ответы

Ответ 1



Примерно так package main import ( "log" "smtp" ) func main() { auth := smtp.PlainAuth("", "some@ya.ru", "password", "smtp.yandex.ru") err := smtp.SendMail("smtp.yandex.ru:25", auth, "from@ya.ru", []string{"to@ya.ru"}, []byte("Текст письма.")) if err != nil { log.Fatal(err) } } Еще примеры здесь: SendingMail.

суббота, 7 марта 2020 г.

Как в C++ использовать код на Go?

#cpp #golang


Добрый день! Подскажите, как использовать функции из Go в программе на C++? Буду
благодарен за простой и доступный пример.
    


Ответы

Ответ 1



Файл hello.go: package main import "fmt" import "C" //export Hello func Hello() { fmt.Println("Hello, world!") } func main() {} Файл main.cpp: #include "hello.h" int main(void) { Hello(); return 0; } Команды: $ go build -buildmode c-archive hello.go $ clang++ -lpthread -o main main.cpp hello.a Результат: $ ./main Hello, world! Подробности тут и там.

четверг, 5 марта 2020 г.

Как правильно деплоить проект?

#golang #docker #развертывание #bitbucket


Здравствуйте, есть restful сервис написанный на golang.
Раньше работал на локальном хосте, сейчас же появилась потребность деплоить все на
сервер.
Не могу понять как лучше осуществить деплой.
Создал репозиторий на бакете, а от дальше туплю.
Каждое изменение(А их будет много) копировать, билдить, потом закрывать процесс с
активным приложением go и запускать новый. Так как изменения будут очень частые и очень
интересует как можно автоматизировать этот процесс.
    


Ответы

Ответ 1



Есть такой подход, под названием Continuous Delivery (Непрерывное развертывание), который предлагает решение описанной вами проблемы. Что это за зверь и с чем его едят, описано в книге Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation / Непрерывное развертывание ПО. Автоматизация процессов сборки, тестирования и внедрения новых версий программ Небольшая выжимка о CD (цитата отсюда): Основная идея CD в том, чтобы построить конвейер (Deployment Pipeline), позволяющий каждому изменению в системе контроля версий попасть в боевое окружения стандартным и полностью автоматизированным способом. Приблизительно это может выглядеть следующим образом: Изменения вносятся в систему контроля версий (СКВ). Запускается система непрерывной интеграции (Continuous Integration, CI), собирается билд (если это компилируемый язык), прогоняются все необходимые тесты. В случае успешности предыдущего шага, билд выкатывается на окружение тестирования (QA). После успешного тестирования билд выкатывается на stage окружение (pre-production), которое максимально приближено к боевому окружению. Если все прошло хорошо, то билд попадает в боевое окружение. Тут встает в полный рост вопрос, как сделать так, чтобы все окружения, начиная с окружения разработчиков, максимально соответствовали друг другу. Для этого существуют инструменты управления конфигурацией (Configuration management system). Самое главное, что конфигурация окружений — это такой же код, и к нему точно также применимы все вышеперечисленные подходы: хранение в СКВ, CI, тестирование. Так вот, если этот процесс отлажен, все изменения проходят по нему, и, потенциально, каждое изменение может быть выкачено в продакшен, то это и называется процессом непрерывной поставки ПО.

Ответ 2



Вам в любом случае нужны решения для CI (Continuous Integration) и CD (Continuous Deployment). Вот список, из которого вы можете выбрать(ссылка). Например, как это работает с Drone: Через веб морду Drone, вы настраиваете Drone для получения проекта с репозитория Git и выполнение "запуска". Что конкретно происходит при запуске описано в следующем шаге. Вместе с кодом приложения в репозитории должен лежать .drone.yml файл, который говорит Drone как устанавливать и запускать ваше приложение. Далее вы делаете git push в ваш репозиторий, Drone проверяет репозиторий на недавно залитый Docker образ (по вашему выбору), а затем стартует его. На основе вашей конфигурации результатом успешного запуска может быть уведомление («Сборка выполнена успешно!»), создание бинарной версии или даже развертывание. Наконец, Drone удаляет Docker контейнер и переустанавливает его так, чтобы он был в исходном и известном состоянии для следующей сборки. P.S. по связке Drone + Docker есть неплохая статья на Хабре.

среда, 4 марта 2020 г.

Golang xml encoding=“windows-1251”

#xml #golang #utf_8 #xmlparser #cp1251


Пишу парсер для xml файлов, но проблема в том, что в файле кодировка:
encoding="windows-1251".
Когда перевел кодировку в ut8 то следующем кодом смог распарсить документ.  

for _, file := range files {
            _, filename := utils.Unzip("/Users/farex/upload/"+file.Filename, "/Users/farex/upload/")
            _ = filename
            ///---------- Open our xmlFile-----------------------
            for _, file := range filename {
                xmlFile, err := os.Open("/Users/farex/upload/" + file)
                // if we os.Open returns an error then handle it
                if err != nil {
                    fmt.Println(err)
                }
                fmt.Println("Successfully Opened " + file)

                if utils.Cut(file, 1) == "H" {

                    defer xmlFile.Close()

                    byteValue, _ := ioutil.ReadAll(xmlFile)
                    var zl_list models.ZL_LIST

                    xml.Unmarshal(byteValue, &zl_list)

                    fmt.Println("---ZGLV---")
                    fmt.Println("VERSION" + zl_list.ZGLV[0].VERSION)
                    fmt.Println("DATA" + zl_list.ZGLV[0].DATA)
                    fmt.Println("FILENAME" + zl_list.ZGLV[0].FILENAME)
                    fmt.Println("SD_z" + zl_list.ZGLV[0].SD_Z)


Это все работает, но задача парсить не Документы в формате utf8 , а в cp-1251.
Как можно "конвертировать" cp-1251 в utf-8 ?
Добавил меторд:

func EncodeWindows1251(ba []uint8) []uint8 {
    enc := charmap.Windows1251.NewEncoder()
    out, _ := enc.String(string(ba))
    return []uint8(out)
}


и соответственно вызываю его:

byteValue, _ := ioutil.ReadAll(xmlFile)
a := EncodeWindows1251(byteValue)
var zl_list  models.ZL_LIST
xml.Unmarshal(a, &zl_list)


Тоже не работает...
Но если из документа убрать строку .
То все работает и документ парсится... Неужели придется регуляркой удалять эту строчку
в документе?

Добавил метод наисанный ниже и за импортировал.        "golang.org/x/text/encoding/charmap".
Но:


Образец файла xml тут кодировка cp-1251
    


Ответы

Ответ 1



Для этого существует поле Decoder.CharsetReader: // CharsetReader, if non-nil, defines a function to generate // charset-conversion readers, converting from the provided // non-UTF-8 charset into UTF-8. If CharsetReader is nil or // returns an error, parsing stops with an error. One of the // the CharsetReader's result values must be non-nil. CharsetReader func(charset string, input io.Reader) (io.Reader, error) Использовать можно так: d := xml.NewDecoder(xmlFile) d.CharsetReader = func(charset string, input io.Reader) (io.Reader, error) { switch charset { case "windows-1251": return charmap.Windows1251.NewDecoder().Reader(input), nil default: return nil, fmt.Errorf("unknown charset: %s", charset) } } err := d.Decode(&zlList)

среда, 26 февраля 2020 г.

LINQ в Go. Почему нет? [закрыт]

#golang #linq #дизайн_языка


        
             
                
                    
                        
                            Закрыт. На этот вопрос невозможно дать объективный ответ.
Ответы на него в данный момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы на него можно было дать ответ, основанный на фактах и цитатах, отредактировав его.
                        
                        Закрыт 8 месяцев назад.
                                                                                
           
                
        
У меня есть вопрос про linq. Почему в Go до сих пор нет в стандарте этой чудесной
штуки. Всё, что я слышал до сих пор по эту технологию от гоферов, так это то, что это
не по-гоферному. К сожалению, этот аргумент меркнет, когда тебе нужно сделать пару
группировок, а потом ещё пару проекций. В итоге, 3-4 строки трансформируются 3-4 экрана.
По-моему выбор очевиден. Да, есть проблемы. Например, для реализации LINQ, скорее всего
потребуется reflect, что приведёт к просаживанию производительности. Но при этом, не
стоит забывать, что с таким же успехом, можно сказать, что и сам reflect приводит к
тому, что производительность просаживается. Так давайте от него откажемся. Конечно,
этого никто не сделает. Вопрос в том, где корректно использовать ту или иную технологию.

Кстати говоря, аналог reflect есть. Это статическая генерация кода. Например, так
реализованы некоторые не стандартные пакеты в Go для работы с JSON.

Для ознакомления с технологией LINQ в го, привожу ссылку.



Я завёл issue в основной репе golang. Требуется достаточно весомое обоснование для
открытие такого issue.



Продолжая моё исследования linq для golang, я нашёл следующее. Здесь человек производит
сравнение koazee и обычных циклов. Легко заметить, что он приходит к выводу, что koazee
не сильно уступает нативному golang. Обсуждение здесь. К слову, скажу, koazee -- лучшая
реализация linq для golang. Лучшая == самая быстрая и функциональная на момент 09.07.2019.



Одна из неудобных вещей в golang и linq -- это способ обращения к лямбдам. Так как
в golang нет шаблонов, придётся писать конструкции, которые похожи на эти:

From(cars).Where(func(c interface{}) bool {
    return c.(Car).year >= 2015
}).Select(func(c interface{}) interface{} {
    return c.(Car).owner
})


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

В go-linq создателям удалось добиться эмуляции шаблонов:

From(cars).Where(func(c Car) bool {
    return c.year >= 2015
}).Select(func(c Car) string {
    return c.owner
})


При этом, скорее всего (если ничего не изменилось), go-linq много медленнее, koazee.

Взято отсюда. 09.07.2019



Ещё  одна библиотека, которая ничего не упоминает про linq, но реализует похожий
функционал. Не поддерживается, к сожалению.  09.07.2019
    


Ответы

Ответ 1



На этот вопрос вам точно ответят только в Go Team, но ответ скорее всего будет около следующего. Во-первых, для LINQ à la C# нужны обобщённые коллекции типа IEnumerable, которые если и появятся, то только в Go 2.0. На интерфейсах это всё пойдёт через отражение, что медленно, и местами небезопасно. Во-вторых, само название «Language Integrated Query» говорит, что нужна поддержка со стороны языка. Это означает новые ключевые слова, причём не одно. Учитывая, что Go Team крайне неохотно добавляет ключевые слова, их добавление крайне маловероятно. Не говоря уже о том, что это синтаксический сахар. В-третьих, даже если опустить синтаксис, для LINQ понадобятся анонимные функции с выведением типов, что является ещё одним расширением языка, и что вряд ли появится даже в Go 2.0.

вторник, 25 февраля 2020 г.

Зачем нужны интерфейсы в golang?

#ооп #golang


Вопрос может показаться глупым. Я недавно изучаю golang. Естественно читал статьи,
но так и не понял.   Что конкретно я могу сделать с интерфейсами и не могу без них(или
с большими затратами)?
Просто я в коде пока их не применял и хочу разобраться, когда их стоит использовать
    


Ответы

Ответ 1



Интерфейсы в Go -- способ реализации полиморфизма. Приведу стандартный пример с фигурами: package main import ( "math" "fmt" ) // Круг. type Circle struct { x, y, r float64 } // Прямоугольник. type Rectangle struct { x1, y1, x2, y2 float64 } // Интерфейс фигуры, которому удовлетворяют все типы, имеющие соответствующий // метод вычисления площади Area(). type Figure interface { Area() float64 } // Реализация интерфейса Figure для Circle. func (c *Circle) Area() float64 { return math.Pi * c.r * c.r } // Реализация интерфейса Figure для Rectangular. func (r *Rectangle) Area() float64 { return math.Abs(r.x2 - r.x1) * math.Abs(r.y2 - r.y1) } func main() { figures := make([]Figure, 0) // Срез фигур. // Мы можем добавлять в этот срез все, что удовлетворяет интерфейсу // Figure, несмотря на то, что это элементы разных типов: figures = append(figures, &Circle{0, 0, 10}) figures = append(figures, &Rectangle{0, 0, 10, 20}) // И теперь мы можем единообразно обрабатывать эти данные разных типов. for _, figure := range figures { fmt.Printf("Area of %#v = %f\n", figure, figure.Area()) } } Результат: $ go run figures.go Area of &main.Circle{x:0, y:0, r:10} = 314.159265 Area of &main.Rectangle{x1:0, y1:0, x2:10, y2:20} = 200.000000

Ответ 2



В golang интерфейсы довольно забавная штукаБ если смотреть на них с точки зрения ООП языков. По большому счету интерфейс в golang это набор методов без имплементации. Если имплементировать эти методы для любого стракта этот стракт можно использовать как тип интерфейсб иногда это довольно удобно. Например есть функция которая должна собрать информацию о разных типах и сохранить их в базу данных, мне необходимо использовать фреймфорк который собирает все query для транзакции: func AddAllEntities(entityName string, entities []entity, defaults map[string]interface{}) { getHookScriptRes("AddPartOfSale", "salePartName", entities) for _, entity := range entities { q.NewAppend("save"+salePartName, entity.GetSaveParams(defaults)...) break } } Так же у меня есть несколько разных страктов entityOne, entityTwo, entityThree и я хочу исподьзовать их как аргумент в функции которая собирает транзакцию. Для этого я создал интерфейс с методом который вытаскивает из страктов нужные мне поля и в мапе связывает их с колонками в таблице: type saveEntity interface { GetSaveParams(defaults map[string]interface{}) map[string]interface{} } И для каждого из страктов я пишу имплементацию этого метода: func (e1 *entityOne) GetSaveParams(defaults map[string]interface{}) []interface{} { tmpMap := map[string]interface{}{ "one": defaults["one"], "two": defaults["two"], "id": e1.Id, } return tmpMap } func (e2 *entityTwo) GetSaveParams(defaults map[string]interface{}) []interface{} { tmpMap := map[string]interface{}{ "one": defaults["one"], "two": defaults["two"], "name": e2.Name, } return tmpMap } func (e3 *entityThree) GetSaveParams(defaults map[string]interface{}) []interface{} { tmpMap := map[string]interface{}{ "one": defaults["one"], "two": defaults["two"], "name": e3.Price, } return tmpMap } Теперь все мои стракты так же являются типом моего интерфейса: saveEntity Одно из самых очевидных применений интерфейсов.

понедельник, 24 февраля 2020 г.

Как правильно вставить переменную в запрос?

#golang


Здравствуйте, с го знаком не долго, поэтому вопрос немного "Ламерский".
Есть запрос Mysql

rows, err := db.Query("SELECT * FROM users WHERE user='login"';)


Login я получил раньше и он в в формате string.
Подскажите как вставить сюда переменную?

rows, err := db.Query("SELECT * FROM users WHERE user='Моя переменная"';)

    


Ответы

Ответ 1



Открываем документацию и видим: age := 27 // v vvv rows, err := db.Query("SELECT name FROM users WHERE age=?", age) Обычные "порядковые параметры на заглушках": вместо значений ставятся ?-заглушки, а последующие аргументы по порядку эти заглушки заполняют. Пример чуточку сложнее: age := 27 name := "Кнопкатык" rows, err := db.Query("SELECT name FROM users WHERE age=? AND name=?", age, name)

пятница, 21 февраля 2020 г.

Вопрос по указателям в Golang

#golang #указатели #типы_данных


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


Ответы

Ответ 1



В golang нет передачи параметров по ссылке - параметры всегда передаются по значению, т.е. копируются. Оператор & - это взятие адреса переменной, т.е. передача указателя, а не передача параметра по ссылке, т.е. как частный случай по значению передаются указатели. map, slice - тоже передаются по значению, просто у них внутри указатели и происходит копирование указателей, а не содержимого таблицы/среза. Проще всего это продемонстрировать на примере slice (среза). Внутри slice представляет собот структуру: https://github.com/golang/go/blob/master/src/runtime/slice.go type slice struct { array unsafe.Pointer len int cap int } т.е. при передаче по значению - можно изменять элементы внутри slice.array, т.к. указатель после копирования будет указывать на ту же область памяти что и исходный, но вот изменить длину или вместимость срезу уже не получится. Точнее эти изменения не будут видны снаружи вызываемой функции, т.к. длина, вместимость и (возможно) указатель array изменятся только в копии среза. Вот наглядный пример: https://play.golang.org/p/SpFFDdzeXR package main import ( "fmt" ) func f_slice_item(s []int) { s[0] = 1 } func f_slice_size(s []int) { s = append(s, 1) } func f_slice_pointer_item(s *[]int) { (*s)[0] = 3 } func f_slice_pointer_size(s *[]int) { *s = append(*s, 5) } func f_iter(s *[]int) { for i, v := range *s { fmt.Println(i, "-", v) } } func main() { var s []int fmt.Println("slice by value") s = []int{0} f_slice_item(s) fmt.Println(s) s = []int{0} f_slice_size(s) fmt.Println(s) fmt.Println() fmt.Println("slice by pointer") s = []int{0} f_slice_pointer_item(&s) fmt.Println(s) s = []int{0} f_slice_pointer_size(&s) fmt.Println(s) fmt.Println() fmt.Println("iter") f_iter(&s) } Результат выполнения: slice by value [1] [0] slice by pointer [3] [0 5] iter 0 - 0 1 - 5

воскресенье, 16 февраля 2020 г.

Вопрос по указателям в Golang

#golang #указатели #типы_данных


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


Ответы

Ответ 1



В golang нет передачи параметров по ссылке - параметры всегда передаются по значению, т.е. копируются. Оператор & - это взятие адреса переменной, т.е. передача указателя, а не передача параметра по ссылке, т.е. как частный случай по значению передаются указатели. map, slice - тоже передаются по значению, просто у них внутри указатели и происходит копирование указателей, а не содержимого таблицы/среза. Проще всего это продемонстрировать на примере slice (среза). Внутри slice представляет собот структуру: https://github.com/golang/go/blob/master/src/runtime/slice.go type slice struct { array unsafe.Pointer len int cap int } т.е. при передаче по значению - можно изменять элементы внутри slice.array, т.к. указатель после копирования будет указывать на ту же область памяти что и исходный, но вот изменить длину или вместимость срезу уже не получится. Точнее эти изменения не будут видны снаружи вызываемой функции, т.к. длина, вместимость и (возможно) указатель array изменятся только в копии среза. Вот наглядный пример: https://play.golang.org/p/SpFFDdzeXR package main import ( "fmt" ) func f_slice_item(s []int) { s[0] = 1 } func f_slice_size(s []int) { s = append(s, 1) } func f_slice_pointer_item(s *[]int) { (*s)[0] = 3 } func f_slice_pointer_size(s *[]int) { *s = append(*s, 5) } func f_iter(s *[]int) { for i, v := range *s { fmt.Println(i, "-", v) } } func main() { var s []int fmt.Println("slice by value") s = []int{0} f_slice_item(s) fmt.Println(s) s = []int{0} f_slice_size(s) fmt.Println(s) fmt.Println() fmt.Println("slice by pointer") s = []int{0} f_slice_pointer_item(&s) fmt.Println(s) s = []int{0} f_slice_pointer_size(&s) fmt.Println(s) fmt.Println() fmt.Println("iter") f_iter(&s) } Результат выполнения: slice by value [1] [0] slice by pointer [3] [0 5] iter 0 - 0 1 - 5

четверг, 13 февраля 2020 г.

Как в golang подключить другой файл?

#golang #golang_faq


Как в golang подключить другой файл?

Я начал только экспериментировать с ним и пока пишу в одном файле. 

Хотелось бы часть кода перенести в другой и подключить его к main.go
    


Ответы

Ответ 1



Import declarations — объявления импорта выдержка из документации. An import declaration states that the source file containing the declaration depends on functionality of the imported package (§Program initialization and execution) and enables access to exported identifiers of that package. The import names an identifier (PackageName) to be used for access and an ImportPath that specifies the package to be imported. мой вольный перевод: объявление импорта (import) гласит, что файл, содержащий это объявление, зависит от функциональности импортируемого пакета (см. §Program initialization and execution), и позволяет получить доступ к экспортируемым пакетом идентификаторам. объявление назначает идентификатор (PackageName) для доступа, а также ImportPath, который точно определяет местоположение импортируемого пакета. примеры: Import declaration Local name of Sin import "lib/math" math.Sin import m "lib/math" m.Sin import . "lib/math" Sin import _ "lib/math"

Ответ 2



Не подключай и всё! Просто обращайтесь к классам и функциям в нём расположенным и всё. В самом начале действительно возникают проблемы разбиения кода на файлы и выделение своих самостоятельных модулей. Подключать через import файл go расположенный в той-же директории что и главный НЕ НУЖНО. Осторожней со слешами ))) должны быть обратные \ я как-то брал путь из far ну и имел проблемы...

“cannot refer to unexported name” при вызове функции из пакета

#golang


Есть главный файл:

package main

import (
  "net/http"
  "./extensions"
)

func main() {

    router := mux.NewRouter()
    s := http.StripPrefix("/static/", http.FileServer(http.Dir("./files/"))) 
    router.HandleFunc("/", handlers.Index)
    router.PathPrefix("/static/").Handler(s)
    http.Handle("/", router) 

    handlers.sayYess()        
    http.ListenAndServe(":5000", nil) 
} 


И файл handlers.go:

package handlers

import (
  "log"
)

func sayYess() {
    log.Println("yess")
}
func Index(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "hello")
}  


Функция sayYess() не вызывается - пишет:


  cannot refer to unexported name handlers.makeCache


и 


  undefined: handlers.makeCache 


Почему другие функции пакета работают, а эта - нет?
    


Ответы

Ответ 1



Разобрался. Как ни странно, первая буква должна быть большой: func SayYess() { log.Println("yess") }

понедельник, 10 февраля 2020 г.

Это анонимная функция, замыкание или что-то еще?

#golang


В документации к пакету нашел следующий код:

check:=func(err error) {
      if err != nill {
      log.Fatal(err)
      }
 }


Далее по коду  происходит вызов:

check(err)


как это правильно называется и для чего так делают? Почему нельзя было просто создать
обыкновенную функцию :

func check(err error) {
 if err != nil {
     log.Fatal(err)
     }
 }


и вызывать так же, как предыдущую
    


Ответы

Ответ 1



Это не анонимная функция и не замыкание. Это "функция как переменная" (function as value). Почему нельзя было просто создать обыкновенную функцию? Без контекста не ясно. Возможно переменная check переопределятся в момент выполнения. Анонимная функция Анонимная функция - функция, которая не может быть переопределена или вызвана повторно. Простыми словами, функция не имеет имени (анонимная) поэтому невозможно "назвать" или её повторно. например: I callItAsCallback(data, func(response string) { print(response) }) https://play.golang.org/p/Z8InpVJL9W II go func() { print("Hello!") } () https://play.golang.org/p/6Q0ITEMbta Функция как значение Функция как значение - функция, которая может быть переопределена или вызвана повторно. Функция имеет имя переменной в которой она определена. var check func() if time.Now().Second()%2 == 0 { check = func() { print("четное") } } else { check = func() { print("нечетное") } } check() Замыкание Замыкание - функция, которая хранит данные из области видимости родительской функции. Это как "характер" функции, ее особенность. Замыкания не возможны без функций "высшего порядка" (функции возвращающие другие функции). Исключение горутины. I В этом примере замыкание исользовано для инкапсуляции приватных значений. Не очень полезно в go. https://play.golang.org/p/7_ES6AKjlI II Класический пример примитивнейшей "фабрики"(паттерн проектирования). package main func main() { MakePizza("сыром!")() MakePizza("грибами!")() } func MakePizza(with string) func() { msg := "Пицца с " return func() { println(msg + with) } } Пицца с сыром! Пицца с грибами! https://play.golang.org/p/RuQV2b04nn Та же фабрика, но без замыкания. Это функция высшего порядка возвращающая анонимную функцию. https://play.golang.org/p/YhAM0kYWba III Ну и напоследок класический пример замыкания горутин в цикле. !!!! Этот пример заведомо неправильный! package main import "time" func main() { for i := 0; i < 10; i++ { go func() { println(i) }() } time.Sleep(time.Second) } 10 10 10 10 10 10 10 10 10 10 https://play.golang.org/p/jfHE9mGCV4 Правильный вариант, но это уже обычная анонимная функция. package main import "time" func main() { for i := 0; i < 10; i++ { go func(i int) { println(i) }(i) } time.Sleep(time.Second) } 0 1 2 3 4 5 6 7 8 9 https://play.golang.org/p/lPFt-_HRY6 Заключение Анонимные функции используются как callback(передаются как параметер в другую функцию) или как возвращаемое значение в функциях "высшего порядка". Функция как значение может быть использована как callback, как возвращаемое значение в функциях "высшего порядка" и для динамического переопределения логики внутри другой функции(неоправданно, но возможно). Функция высшего порядка - функция возврщающая другие функции, как с замыканием так и без. Замыкание - это эффект который может нести анонимная или "функция как значение" функция возвращаемая из функции высшего порядка или запущенная как горутина. Особенность в том, что функция может ссылаться на значения из родительской области видимости после "завершения" родительской функции.

Ответ 2



Все зависит от того как часто происходит вызов функции, возможно так автор пытался немного оптимизировать память. Если это огромный пакет, то возможно в этом есть смысл. Посмотри на пакет "encoding/json" там в процессе сериализации происходит составление кеша методов для каждого типа данных, что в дальнейшем ускоряет процесс кодирования / декодирования.

воскресенье, 9 февраля 2020 г.

Как определить, является ли заданная строка десятичным представлением натурального числа от 1 до 2^64-1 включительно?

#c #golang


Еще условие: строка не должна начинаться с "0".

Например:


"0" – нет
"1" – да
"07" – нет
"18446744073709551615" (264 − 1) – да
"18446744073709551616" (264) – нет

    


Ответы

Ответ 1



Как вариант: package main import ( "fmt" "strconv" "strings" ) func checkStr(str string) bool { if strings.TrimPrefix(str, "0") != str { fmt.Println("Начинается с 0") return false } if _, err := strconv.ParseUint(str, 10, 64); err != nil { fmt.Println(err) return false } return true } func main() { fmt.Println(checkStr("18446744073709551615")) fmt.Println(checkStr("18446744073709551616")) fmt.Println(checkStr("1b")) fmt.Println(checkStr("-1234567")) fmt.Println(checkStr("07")) }

Ответ 2



Учитывая, что требуется особое поведение для 0, -, пробелов, пределов (264-1), конца строки — не сложнее с нуля распознавание строки как натурального числа реализовать: #include #include bool is_posint64(const char* s) { if (!s || !*s || !('1' <= *s && *s <= '9')) return false; uint64_t n = *s++ - '0'; for ( ; *s; ++s) { if (*s < '0' || *s > '9' || n > UINT64_MAX / 10 || (uint64_t)(*s - '0') > (UINT64_MAX - 10*n)) return false; n = 10*n + (*s - '0'); } return true; } Пример.

Ответ 3



Возможный вариант на си: const static int WIDTH = 21; void f(char *s) { size_t len = strlen(s); if (len == 0) { printf("%*s: empty string\n", WIDTH, s); return; } if (s[0] == '0') { printf("%*s: starts with zero\n", WIDTH, s); return; } if (s[0] == '-') { printf("%*s: negative\n", WIDTH, s); return; } char *last = &s[len]; char *newlast = last; uint64_t n = strtoull(s, &newlast, 10); if (errno != 0) { printf("%*s: error: %s\n", WIDTH, s, strerror(errno)); errno = 0; return; } if (last != newlast) { printf("%*s: extra characters: %s\n", WIDTH, s, newlast); return; } printf("%*s: number %lu\n", WIDTH, s, n); }

Ответ 4



Может кому-то пригодится еще один вариант на C. Число 216 − 1 записано как строка-шаблон. Если в какой-то позиции цифра во входной строке меньше, чем в шаблоне, то приемлемо еще столько же любых цифр, сколько осталось до конца шаблона. Если больше, то на одну меньше. Если равна, то проверяем следующие цифры строки и шаблона. #include #include int validate_index(const char *str) { const char *template = "18446744073709551615"; static int length = 0; if (length == 0) length = strlen(template); int i, length2 = length; // строка пустая или начинается с цифры 0 if (str[0] == '\0' || str[0] == '0') return 0; for (i = 0; i < length; i++) { char c = str[i]; // строка закончена if (c == '\0') return -1; // символ не цифра if (c < '0' || c > '9') return i; // сравнение с шаблоном if (c == template[i]) continue; if (c > template[i]) length2--; break; } for (; i < length2; i++) { char c = str[i]; // строка закончена if (c == '\0') return -1; // символ не цифра if (c < '0' || c > '9') return i; } // строка не закончена if (str[i] != '\0') return i; return -1; } int main() { const char *strs[] = { "0", "1", "07", "1aaa", "12345", "18446744073709551614", "18446744073709551615", "18446744073709551616", "184467440737095516150", "28446744073709551615", "18446744074709551615", }; int i; for (i = 0; i < sizeof strs / sizeof *strs; i++) { int r = validate_index(strs[i]); if (r == -1) printf("\"%s\" - yes\n", strs[i]); else printf("\"%.*s[%c]%s\" - no\n", r, strs[i], strs[i][r], &strs[i][r+1]); } return 0; } Результат: "[0]" - no "1" - yes "[0]7" - no "1[a]aa" - no "12345" - yes "18446744073709551614" - yes "18446744073709551615" - yes "1844674407370955161[6]" - no "18446744073709551615[0]" - no "2844674407370955161[5]" - no "1844674407470955161[5]" - no

Как отсортировать map[string]int по значению?

#сортировка #golang


Как отсортировать map в Go по убыванию значений?

Пример:

map[string]int{"value1": 10, "value2": 20,"value3": 30}

    


Ответы

Ответ 1



Ну смотрите вы можете преобразовать map к массиву определенной структуры []struct и потому использовать функцию sort.Slice. Пример (playground): m := map[string]int{"value1": 10, "value2": 20,"value3": 30} type key_value struct { Key string Value int } var sorted_struct []key_value for key, value := range m { sorted_struct = append(sorted_struct , key_value {key, value}) } sort.Slice(sorted_struct , func(i, j int) bool { return sorted_struct[i].Value > sorted_struct[j].Value }) for _, key_value := range sorted_struct { fmt.Printf("%s, %d\n", key_value.Key, key_value.Value) } //OUTPUT: //value3, 30 //value2, 20 //value1, 10 Преобразовать отсортированный массив определенной структуры к map, можно следующим образом. Преобразование []struct к map yourMap := structs.Map(sortedStructArray) Но для этого надо подключить пакет structs.

Ответ 2



Мапы в го не имеют определённой сортировки. A map is an unordered group of elements of one type, called the element type, indexed by a set of unique keys of another type, called the key type. (…) Вам надо будет достать все значения и ключи и отсортировать их с помощью пакета sort: m := map[string]int{"value1": 10, "value2": 20, "value3": 30} type kv struct { k string v int } kvs := make([]kv, 0, len(m)) for k, v := range m { kvs = append(kvs, kv{k, v}) } sort.Slice(kvs, func(i, j int) bool { return kvs[i].v > kvs[j].v }) fmt.Println(kvs) Playground: https://play.golang.org/p/bk8vaGJRiou.

Ответ 3



Примеры очень похожи, практически идентичны, но! В одном случае создание слайса структур выполнено var sorted_struct []key_value а во втором kvs := make([]kv, 0, len(m)) Как лучше? Второй? Ведь если мы знаем размер, то наверное лучше указать?

пятница, 7 февраля 2020 г.

Golang пакет time возвращение времени на русском

#golang


Здравствуйте, подскажите как правильно реализовать дату и время на русском в go?
    


Ответы

Ответ 1



Дата и время на русском это с месяцами/днями недели словами или просто в привычном формате? Если просто в ДД.ММ.ГГГГ ЧЧ:ММ:СС, то можно так fmt.Println(time.Now().Format("02.01.2006 15:04:05")) В отличие от php, c и т.п. в стандартном пакете Go символы форматирования это не буквы со спец-символами, а образцы. Например 02 это день месяца с ведущим нулём, 01 - номер месяца с ведущим нулём. Просто 2 это день месяца без ведущего нуля и т.п. В результате формат времени выглядит как представление даты 2006-01-02T15:04:05.999999999Z07:00 в том формате как вам нужно. Образцы можно посмотреть в константах пакета time https://golang.org/pkg/time/#pkg-constants

Golang: запись вида map[string]interface{}{}

#golang #map


Разбирая открытый код проекта, встретил вот такую запись

args := map[string]interface{}{}

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

Весь контекст в котором используется запись в качестве request передается route вида
controller/action в качестве Params как я понял json:

func (connect *Connect) Request(request string, params string) (interface{}, error) {
    req, err := connect.factory.NewRequest(Host)
    if err != nil {
        return nil, err
    }

    args := map[string]interface{}{}
    json.Unmarshal([]byte(params), &args)

    resp, err := req.Do(request, args)
    if err != nil {
        return nil, err
    }

    result, err := resp.GetMethodResult()
    if err != nil {
        return nil, err
    }

    return result, nil
}


Прошу приведите пример.
    


Ответы

Ответ 1



map[string]interface{} - это карта, где ключ string, а значение удовлетворяет типу interface{} interface{} - это пустой интерфейс, ему удовлетворяет любой объект (аналог void* из C) а последние "{}" это инициализация карты без значений (типа как map[int]int{1:2, 3:4} ) можно переписать в виде args := make(map[string]interface{}) или более развернуто type any interface{} var args map[string]any args = make(map[string]any,0) // или просто args = make(map[string]any)

Ответ 2



map[string]interface{} - это лишь указание типа, что ваша переменная типа ключ - строковая, и переменная типа interface{}. когда вы объявляете переменную такого типа: var a map[string]interface{} a["test"] = 1 \\ panic: assignment to entry in nil map , то будет ошибка. Нужно инициализировать пустую мапу: var a map[string]interface{} a = map[string]interface{}{} a["test"] = 1 , после чего, с ней можно работать.

среда, 5 февраля 2020 г.

сделать POST запрос к серверу средствами golang

#json #post #golang


Подскажите как сделать POST запрос к серверу средствами golang. Тело запроса должно
содержать JSON данные.
    


Ответы

Ответ 1



Простейший способ: data := []byte(`{"foo":"bar"}`) r := bytes.NewReader(data) resp, err := http.Post("http://example.com/upload", "application/json", r) if err != nil { return err } См. также https://golang.org/pkg/net/http/#Client.Post. https://play.golang.org/p/YzPQULc8pc

Ответ 2



Нашёл решение в просторах интернета: func main() { url := "http://restapi3.apiary.io/notes" fmt.Println("URL:>", url) var jsonStr = []byte(`{"title":"Buy cheese and bread for breakfast."}`) req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr)) req.Header.Set("X-Custom-Header", "myvalue") req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, err := client.Do(req) if err != nil { panic(err) } defer resp.Body.Close() fmt.Println("response Status:", resp.Status) fmt.Println("response Headers:", resp.Header) body, _ := ioutil.ReadAll(resp.Body) fmt.Println("response Body:", string(body)) }

Ответ 3



Пример запроса к микросервису: func sendRequest(reader myReader) ([]byte, bool) { var flag bool var body []byte if err := reader.SetAllToStringsIncludeNulls(); err != nil { helpers.ErrHandler(err) } else { jsonReader := helpers.CreateJson(reader) if req, err := http.NewRequest("POST", Uri, bytes.NewBuffer(jsonReader)); err != nil { helpers.ErrHandler(err) } else { req.Header.Set("Content-Type", "application/json") client := &http.Client{} if resp, err := client.Do(req); err != nil { helpers.ErrHandler(err) } else { defer resp.Body.Close() if body, err = ioutil.ReadAll(resp.Body); err != nil { helpers.ErrHandler(err) } flag = true } } } return body, flag }

Среда разработки для Go [закрыт]

#golang #ide


        
             
                
                    
                        
                            Закрыт. На этот вопрос невозможно дать объективный ответ.
Ответы на него в данный момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы на него можно было дать ответ, основанный на фактах и цитатах, отредактировав его.
                        
                        Закрыт 2 года назад.
                                                                                
           
                
        
Кто какую среду разработки может посоветовать для Go? 
Хотелось бы видеть в IDE:

Подсветку синтаксиса
Авто подстановку
Кросс платформенное решение
Автоматическую сборку кода

Как дополнение:

Работа через ssh
    


Ответы

Ответ 1



Goclipse? Это кроссплатформенный плагин для Eclipse. Features: Syntax highlighting Automatic building of files Run configuration support ...

Ответ 2



Gedit как среда для Go на Linux. Плюсы: бесплатно, легковесно, просто.

Ответ 3



WebStorm + Go плагин. Плагин активно развивается, поддерживает перечисленные фичи (в том числе кроссплатформенность). Сразу можно и фронтенд разрабатывать в одной среде.

Ответ 4



Чтобы превратить Atom в IDE для Go, достаточно установить два пакета и отладчик: apm install go-plus go-debug go get github.com/derekparker/delve/cmd/dlv go-plus — по сути, это "метапакет", он ничего сам по себе не делает, исключительно устанавливает следующее: autocomplete-go занимается автодополнением gofmt автоформатирует файлы при сохранении и чистит импорты builder-go собирает пакет при каждом сохранении gometalinter-linter анализирует код на проблемы целым набором инстументов navigator-godef ищет определение выделенного идентификатора tester-go отображает покрытие кода тестами gorename переименовывает идентификатор по всему коду go-debug — удобная обёртка для отладчика Delve, позволяющая ставить точки останова и осматривать состояние вдоль всего стека любой горутины. Сам Delve, обычно, прекрасно устанавливается с помощью go get, при определённых условиях даже в Windows. Я написал пошаговую инструкцию по установке всего этого добра на чистую Debian-подобную ОС и детали о том, как заставить это работать на Windows.

Ответ 5



IDE from JetBrains. Пара плагинов и все работает прекрасно. При этом настройка легкая. https://www.jetbrains.com/go/

Ответ 6



GoLand от JetBrains ;) Так же рекомендую WebStorm, но мне GoLand больше нравится

Ответ 7



Vim + https://github.com/jnwhiteh/vim-golang

Ответ 8



http://go-ide.com/

Ответ 9



Могу предложить VS Code, настроенную как написано по ссылке: https://github.com/Microsoft/vscode-go В итоге получаете: Подсветку синтаксиса Авто подстановку Кросс платформенное решение Автоматическую сборку кода Возможность отладки Авто импорт/удаление пакетов Интеграцию с Git и множество других полезных фич. Уверен если поискать все эти плагины можно и на Vim поставить.

суббота, 1 февраля 2020 г.

Как красиво передать slice с разными скалярными типами в функцию?

#golang #типы_данных


Есть функция, которая обрезает slice со второго элемента и принимает аргумент с типом
[]interface{}

func SliceShift(slice []interface{}) []interface{} {
    if len(slice) > 1 {
        return slice[1:]
    }
    return make([]interface{}, 0)
}


Если я попытаюсь передать в аргументе срез строк

SliceShift([]string{"a", "b", "c"})


то получаю ошибку: cannot use []string literal (type []string) as type []interface
{} in argument to SliceShift.
Не очень понимаю почему она происходит, но нагулил решение:

str := []string{"a", "b", "c"}
newStr := make([]interface{}, len(str))
for i, v := range str {
    newStr[i] = v
}


Проблема в том, что мне не хочется каждый раз писать make([]interface{}, ...) и цикл
копирования.
Можно ли это как-то избежать?
    


Ответы

Ответ 1



API довольно странное и будет и далее порождать подобные проблемы. Делайте хелпер: type any = interface{} func ss2anys(ss []string) (anys []any) { anys = make([]any, len(ss)) for i := range ss { anys[i] = ss[i] } return anys } Playground: https://play.golang.org/p/B2xyfjUYYQu.

Ответ 2



Из faq по golang Can I convert a []T to an []interface{}? Not directly. It is disallowed by the language specification because the two types do not have the same representation in memory. It is necessary to copy the elements individually to the destination slice. This example converts a slice of int to a slice of interface{}: t := []int{1, 2, 3, 4} s := make([]interface{}, len(t)) for i, v := range t { s[i] = v } Поэтому тут либо функции хелперы под каждый тип, либо так еще можно https://play.golang.org/p/zwqv9ulWW_B package main import ( "fmt" "log" ) func main() { fmt.Printf("String test: %v\n", SliceShift([]string{"a", "b", "c"})) fmt.Printf("String test: %v\n", SliceShift([]string{})) fmt.Printf("Int test: %v\n", SliceShift([]int{1, 2, 3, 4, 5})) names := []string{"Igor", "John", "Garry"} names = SliceShift(names).([]string) fmt.Printf("Names test: %v\n", names) } func SliceShift(slice interface{}) interface{} { switch v := slice.(type) { case []string: if len(v) > 1 { return v[1:] } return make([]string, 0) case []int: if len(v) > 1 { return v[1:] } return make([]int, 0) default: log.Panicf("unknown slice type %#v", v) } return make([]interface{}, 0) }

Ответ 3



Попробуйте так : func SliceShift(slice interface{}) interface{} { s := slice.([]string) return s[1:] } func main() { fmt.Println(SliceShift([]string{"a", "b", "c"})) } вот рабочий пример если в функцию нужно передавать разные типы (можно сделать в SliceShift 2й аргумент который будет указывать тип) и внутри функции проверку к какому типу преобразовать. Вот пример: func SliceShift(slice interface{}, tp int) interface{} { switch (tp) { case 0: return slice.([]string)[1:] case 1: return slice.([]uint64)[1:] case 2: return slice.([]byte)[1:] case 3: return slice.([]float32)[1:] } return struct{}{} } func main() { fmt.Println(SliceShift([]byte("test"), 2)) fmt.Println(SliceShift([]string{"a", "b", "c"}, 0)) } только учтите что передав не корректные типы будет паника.

среда, 29 января 2020 г.

Golang быстрее чем С? [закрыт]

#c #golang #инспекция_кода


        
             
                
                    
                        
                            Закрыт. На этот вопрос невозможно дать объективный ответ.
Ответы на него в данный момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы на него можно было дать ответ, основанный на фактах и цитатах, отредактировав его.
                        
                        Закрыт 2 года назад.
                                                                                
           
                
        
Решил малость побаловаться и сравнить скорость выполнения Golang и C. Просто интересно
было, насколько мой любимый Go уступает Сишке. 

Первое, что пришло в голову, это функция переворачивания строки. Написал две абсолютно
одинаковые программки: на C и на Go, которые в цикле переворачивают строку 10000001
раз и подсчитывают затраченное время. Ну, и запустил, само собой. 

Сказать, что счёт разгромный — это ничего не сказать, но он совершенно противоположен
ожидаемому)))

Программа на Go в среднем отрабатывает в три раза БЫСТРЕЕ аналогичной программы на
С!!! Не могу в это поверить, может, я просто не умею готовить Си и написал кривой код?

Go:

package main

import (
    "fmt"
    "time"
)

func main() {
    var t time.Time
    var s = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
mollit anim id est laborum."

    t = time.Now()
    b  := []byte(s)
    for i := 0; i < 10000001; i++ {
        reverse(b)
    }
    fmt.Println(time.Since(t).String())
    fmt.Println(string(b))
}

func reverse(s []byte) {
    var i = 0
    var j = len(s) - 1

    for i < j {
        s[i], s[j] = s[j], s[i]
        i++
        j--
    }
}


С:

#include 
#include 
#include 

void reverse(char s[]);

int main()
{
    time_t start, end;
    char s[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
mollit anim id est laborum.";
    long i;

    start = time(NULL);
    for (i = 0; i < 10000001; i++) {
        reverse(s);
    }
    end = time(NULL);

    printf("%f\n", difftime(end, start));
    printf("%s\n", s);
    return 0;
}

void reverse(char s[])
{
    int i = 0;
    int j = strlen(s) - 1;
    int tmp;

    for (; i < j;) {
        tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
        i++;
        j--;
    }
}


go version

go version go1.9 darwin/amd64


cc -v

Apple LLVM version 9.0.0 (clang-900.0.37)
Target: x86_64-apple-darwin16.7.0

    


Ответы

Ответ 1



GCC 4.9 (-O3) #include #include #include void reverse(char s[]); int main() { time_t start, end; char s[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; long i; start = time(NULL); for (i = 0; i < 10000001; i++) { reverse(s); } end = time(NULL); printf("%f\n", difftime(end, start)); printf("%s\n", s); return 0; } void reverse(char s[]) { int i = 0; int j = strlen(s) - 1; int tmp; for (; i < j;) { tmp = s[i]; s[i] = s[j]; s[j] = tmp; i++; j--; } } 5.000000 #include #include #include void reverse(char s[]); int len; int main() { time_t start, end; char s[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; long i; len = strlen(s); start = time(NULL); for (i = 0; i < 10000001; i++) { reverse(s); } end = time(NULL); printf("%f\n", difftime(end, start)); printf("%s\n", s); return 0; } void reverse(char s[]) { int i = 0; int j = len - 1; int tmp; for (; i < j;) { tmp = s[i]; s[i] = s[j]; s[j] = tmp; i++; j--; } } 3.000000 Golang 1.7.1 package main import ( "fmt" "time" ) var lengt int func main() { var t time.Time var s = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." lengt = len(s); t = time.Now() b := []byte(s) for i := 0; i < 10000001; i++ { reverse(b) } fmt.Println(time.Since(t).String()) fmt.Println(string(b)) } func reverse(s []byte) { var i = 0 var j = lengt - 1 for i < j { s[i], s[j] = s[j], s[i] i++ j-- } } 3.573949391s Немного настораживает слишком небольшая разница и нули. -O3 непредсказуем. Добавлю вариант, предложенный в комментариях @avp и мной: #include #include #include #include void reverse(char[], int); int main() { clock_t start, end; char s[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; long i; int len = strlen(s); start = clock(); for (i = 0; i < 10000001; i++) { reverse(s, len); } end = clock(); printf("%lf\n", (double)(end-start)/CLOCKS_PER_SEC); printf("%s\n", s); return 0; } void reverse(char s[], int len) { uint64_t *i8 = (uint64_t*) s, *j8 = (uint64_t*)(s+len-8), t8; while (i8 <= j8) { t8 = __builtin_bswap64(*i8); *i8 = __builtin_bswap64(*j8); *j8 = t8; i8++; j8--; } char *i1 = (char*)i8, *j1 = ((char*)j8)+7, t1; while (i1 < j1) { t1 = *i1; *i1 = *j1; *j1 = t1; i1++; j1--; } } 1.168988 Исправлена важная ошибка в коде функции reverse() на базе __builtin_bswap64(), потому вопрос поднялся в топ.

Ответ 2



Во-первых, неправильно использовать time_t и time для замера времени, поскольку оно уже в секундах. Надо использовать clock_t и clock. Во-вторых, чего никто указатели не использует? Запускаю твою программу на Go на ideone https://ideone.com/An2Ndj и получаю 1.93s. Запускаю твою же программу на Си https://ideone.com/7N8TTV - получаю 1.73s. Переделываю на указатели вместо индексов https://ideone.com/yQ2l5J - 1.50s. И вариант @Harry на плюсах с std::reverse https://ideone.com/46PqbD - 1.32s. А вообще, интересно, что варианты на Си делают ещё один проход по строке, чтобы вычислить её длину. Вариант на плюсах сразу имеет начальный и конечный указатели, а Go, вероятно, хранит длину массива где-то отдельно. Так что в варианте на си надо избавиться от strlen внутри функции и явно передавать длину. void reverse(char *str, size_t len) Код из вопроса https://ideone.com/vvwUVs - 2.04s. Код с указателями https://ideone.com/jtwTeF - 1.38s. Странно, но в этом случае кода из вопроса незначительно замедлился. Предположу, что это связано с проходом по строке в обратном направлении, при котором возникают сложности с попаданием строки в кэш процессора. Очень странно, что тот же эффект не наблюдается на коде с указателями. В любом случае интересно попробовать пройти только вторую половину строки и посмотреть, что будет. volatile int ignored = strlen(str + len / 2); Вариант из вопроса https://ideone.com/GSqG9i ускоряется до 1.68s. Вариант с указатеелями https://ideone.com/Tuluoy замедляется до 1.46s. Замечу, что 1.68 для этого кода - это быстрее обоих вариантов - и варианта с strlen, и варианта без него. А вот 1.46 - это почти 1.50 и получается, что разницы между strlen для всей строки и для её половины правктически нет. Как видно, Си всё-таки быстрее. А побеждают пока плюсы с их std::reverse, но сишный вариант с 1.38 очень близок к этим 1.32 :)