Перегляньте приклади та ідіоми [закрито]


91

Існує не так багато коду Go, з якого можна вивчити мову, і я впевнений, що я не єдиний, хто експериментує з ним. Отже, якщо ви дізнались щось цікаве про мову, розмістіть тут приклад.

Я теж шукаю

  • ідіоматичні способи робити щось у Go,
  • C / C ++ стиль мислення "перенесений" на Go,
  • загальні підводні камені щодо синтаксису,
  • що-небудь цікаве, справді.

Підтримка ARM, така як 8-розрядна або 16-розрядна. D мови все ще немає.

1
Бібліотека ( golang.org/pkg ) є чудовим джерелом для вивчення того, як використовується go. Особисто я вважаю, що вивчення того, як впроваджуються структури даних, є корисним для вивчення мови.
tkokasih

Відповіді:


35

Відкласти заяви

Оператор "defer" викликає функцію, виконання якої відкладено до моменту, коли повертається оточуюча функція.

DeferStmt = "defer" Вираз.

Вираз повинен бути викликом функції або методу. Кожного разу, коли виконується оператор "defer", параметри виклику функції обчислюються та зберігаються заново, але функція не викликається. Відкладені виклики функцій виконуються в порядку LIFO безпосередньо перед тим, як повертається функція, що оточує, але після того, як значення обернених значень, якщо такі є, були оцінені.


lock(l);
defer unlock(l);  // unlocking happens before surrounding function returns

// prints 3 2 1 0 before surrounding function returns
for i := 0; i <= 3; i++ {
    defer fmt.Print(i);
}

Оновлення:

deferтепер також ідіоматичний спосіб обробки panicу винятковий спосіб:

package main

import "fmt"

func main() {
    f()
    fmt.Println("Returned normally from f.")
}

func f() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in f", r)
        }
    }()
    fmt.Println("Calling g.")
    g(0)
    fmt.Println("Returned normally from g.")
}

func g(i int) {
    if i > 3 {
        fmt.Println("Panicking!")
        panic(fmt.Sprintf("%v", i))
    }
    defer fmt.Println("Defer in g", i)
    fmt.Println("Printing in g", i)
    g(i+1)
}

17
Схоже на старий добрий RAII (зроблено явно).
Конрад Рудольф

4
+1, оскільки я багато читав про Go, але все одно цього не бачив (поки ви мені не показали)!
u0b34a0f6ae

Розумний, хоча для мене було б більш розумно, якщо відкладати оператори, коли вони виконуються у порядку FIFO (зверху вниз), але, можливо, це лише я ...
Майк Спросс,

Класно. Нагадує мені захисники від скрапу від D digitalmars.com/d/2.0/exception-safe.html
hasen

4
@Mike: якщо порівнювати з блоками "try: .. нарешті:" LIFO гніздиться так само. Для пар відкриття / закриття ресурсів тощо подібне вкладання є єдиним, що має сенс (Перше відкриття закриється останнім).
u0b34a0f6ae

25

Файли об’єктів Go фактично містять заголовок прозорого тексту:

jurily@jurily ~/workspace/go/euler31 $ 6g euler31.go
jurily@jurily ~/workspace/go/euler31 $ cat euler31.6
amd64
  exports automatically generated from
  euler31.go in package "main"
    import

$$  // exports
  package main
    var main.coin [9]int
    func main.howmany (amount int, max int) (? int)
    func main.main ()
    var main.initdone· uint8
    func main.init ()

$$  // local types
  type main.dsigddd_1·1 struct { ? int }

$$

!
<binary segment>

6
Це більше схоже на приховану функцію, ніж на ідіоматичний приклад
hasen

22

Я бачив пару людей, які скаржились на цикл for, на зразок "чому ми повинні говорити i = 0; i < len; i++в наші дні?".

Я не згоден, мені подобається конструкція for. Ви можете використовувати довгу версію, якщо хочете, але ідіоматичний Go є

var a = []int{1, 2, 3}
for i, v := range a {
    fmt.Println(i, v)
}

for .. rangeКонструкція циклу по всіх елементах і поставок два значення - індекс iі значення v.

range також працює на картах та каналах.

Тим НЕ менше, якщо ви НЕ любите forв будь-якій формі, ви можете визначити each, і mapт.д. в кілька рядків:

type IntArr []int

// 'each' takes a function argument.
// The function must accept two ints, the index and value,
// and will be called on each element in turn.
func (a IntArr) each(fn func(index, value int)) {
    for i, v := range a {
        fn(i, v)
    }
}

func main() {
    var a = IntArr([]int{2, 0, 0, 9}) // create int slice and cast to IntArr
    var fnPrint = func(i, v int) {
        fmt.Println(i, ":", v)
    } // create a function

    a.each(fnPrint) // call on each element
}

відбитки

0 : 2
1 : 0
2 : 0
3 : 9

Мені починає дуже подобатися Go :)


Хоча rangeце просто приємно, якщо він компілюється з тим самим кодом, що і цикл for-3.
Thomas Ahle

19

Йдіть і отримайте свою репутацію stackoverflow

Це переклад цієї відповіді .

package main

import (
    "json"
    "fmt"
    "http"
    "os"
    "strings"
)

func die(message string) {
    fmt.Printf("%s.\n", message);
    os.Exit(1);
}

func main() {
    kinopiko_flair := "https://stackoverflow.com/users/flair/181548.json"
    response, _, err := http.Get(kinopiko_flair)
    if err != nil {
        die(fmt.Sprintf("Error getting %s", kinopiko_flair))
    }

    var nr int
    const buf_size = 0x1000
    buf := make([]byte, buf_size)

    nr, err = response.Body.Read(buf)
    if err != nil && error != os.EOF {
        die(fmt.Sprintf("Error reading response: %s", err.String()))
    }
    if nr >= buf_size { die ("Buffer overrun") }
    response.Body.Close()

    json_text := strings.Split(string(buf), "\000", 2)
    parsed, ok, errtok := json.StringToJson(json_text[0])
    if ! ok {
        die(fmt.Sprintf("Error parsing JSON %s at %s", json_text, errtok))
    }

    fmt.Printf("Your stackoverflow.com reputation is %s\n", parsed.Get ("reputation"))
}

Дякуємо Скотту Уельсу за допомогу в .Read ().

Це все ще виглядає досить незграбно, з двома рядками та двома буферами, тому якщо у будь-якого експерта Go є порада, дайте мені знати.


Я не впевнений, що мало бути не так із форматуванням; Я його відновив.

5
Автори Go рекомендують до gofmtвашого коду :-)
ℝaphink

Я не можу скомпілювати: $ ../go/src/cmd/6g/6g SO.go SO.go: 34: undefined: json.StringToJson
phaphink

@Raphink: мова змінилася з моменту мого створення.

Так, ви знаєте, можливо, що є найближчим еквівалентом StringToJson? Раніше він створював будівельник внутрішньо, тепер потрібно надати власному попередньо визначену власну структуру?
macbirdie

19

Ось гарний приклад йоти з допису Кінопіко :

type ByteSize float64
const (
    _ = iota;   // ignore first value by assigning to blank identifier
    KB ByteSize = 1<<(10*iota)
    MB
    GB
    TB
    PB
    YB
)

// This implicitly repeats to fill in all the values (!)

5
Зверніть увагу, що крапка з комою непотрібна.
mk12

18

Ви можете поміняти місцями змінні паралельним призначенням:

x, y = y, x

// or in an array
a[j], a[i] = a[i], a[j]

просто, але ефективно.


18

Ось ідіома від ефективної Go сторінки

switch {
case '0' <= c && c <= '9':
    return c - '0'
case 'a' <= c && c <= 'f':
    return c - 'a' + 10
case 'A' <= c && c <= 'F':
    return c - 'A' + 10
}
return 0

Оператор switch вмикається true, коли не вказано жодного виразу. Отже, це еквівалентно

if '0' <= c && c <= '9' {
    return c - '0'
} else if 'a' <= c && c <= 'f' {
    return c - 'a' + 10
} else if 'A' <= c && c <= 'F' {
    return c - 'A' + 10
}
return 0

На даний момент версія комутатора для мене виглядає трохи чистішою.


6
Ого, повністю вирвано з VB. ;-) ( Switch True...)
Конрад Рудольф

@Konrad, бий мене! :) Я вже використовував цю ідіому в коді VB6, і це, безумовно, може допомогти читати в певних ситуаціях.
Mike Spross,

Що таке '<='? Це пов’язано з „<-“?
phaphink

@Raphink: менше, ніж або рівно.
Paul Ruane

17

Вимикачі типу :

switch i := x.(type) {
case nil:
    printString("x is nil");
case int:
    printInt(i);  // i is an int
case float:
    printFloat(i);  // i is a float
case func(int) float:
    printFunction(i);  // i is a function
case bool, string:
    printString("type is bool or string");  // i is an interface{}
default:
    printString("don't know the type");
}


14

Названі параметри результату

"Параметри" повернення або результату функції Go можуть отримувати імена та використовуватися як звичайні змінні, як і вхідні параметри. Коли їх називають, вони ініціалізуються до нульових значень для своїх типів, коли функція починається; якщо функція виконує оператор return без аргументів, поточні значення параметрів результату використовуються як повернені значення.

Назви не є обов'язковими, але вони можуть зробити код коротшим і зрозумілішим: вони є документацією. Якщо ми називаємо результати nextInt, стає очевидним, який повернутий int є яким.

func nextInt(b []byte, pos int) (value, nextPos int) {

Оскільки іменовані результати ініціалізуються і прив’язані до неприкрашеного повернення, вони можуть як спростити, так і пояснити. Ось версія io.ReadFull, яка добре їх використовує:

func ReadFull(r Reader, buf []byte) (n int, err os.Error) {
    for len(buf) > 0 && err == nil {
        var nr int;
        nr, err = r.Read(buf);
        n += nr;
        buf = buf[nr:len(buf)];
    }
    return;
}

1
Мені цікаво - чи є в будь-якій іншій мові це?
u0b34a0f6ae

1
У Matlab є щось подібне.
Dan Lorenc

pascal використовує подібний синтаксис для повернення одного значення.
nes1983

1
@ nes1983 Для тих, хто не знає, у Паскалі ви класично призначаєте значення, що повертається, до імені функції.
fuz

FORTRAN в значній мірі це має.
Хат8

14

З відповіді Джеймса Антілья :

foo := <-ch     // This blocks.
foo, ok := <-ch // This returns immediately.

Крім того, потенційна помилка: незначна різниця між операторами прийому та відправлення:

a <- ch // sends ch to channel a
<-ch    // reads from channel ch

3
Приймаючий оператор сам по собі є тепер блокує операція, від Go 1.0.3. Специфікацію було змінено: golang.org/ref/spec#Receive_operator . Будь ласка, спробуйте поведінку блокування (тупик) тут: play.golang.org/p/0yurtWW4Q3
Deleplace

13
/* 
 * How many different ways can £2 be made using any number of coins?
 * Now with 100% less semicolons!
 */

package main
import "fmt"


/* This line took me over 10 minutes to figure out.
 *  "[...]" means "figure out the size yourself"
 * If you only specify "[]", it will try to create a slice, which is a reference to an existing array.
 * Also, ":=" doesn't work here.
 */
var coin = [...]int{0, 1, 2, 5, 10, 20, 50, 100, 200}

func howmany(amount int, max int) int {
    if amount == 0 { return 1 }
    if amount < 0 { return 0 }
    if max <= 0 && amount >= 1 { return 0 }

    // recursion works as expected
    return howmany(amount, max-1) + howmany(amount-coin[max], max)
}


func main() {
    fmt.Println(howmany(200, len(coin)-1))
}

4
Я б запропонував видалити назву сайту для вирішення проблем, а також ідентифікаційний номер. Можливо, переформулюйте питання. Щоби не зіпсувати проблему, хтось спотикається про неї. Або намагаються обдурити, шукаючи проблему в мережі з цього приводу.
Мізіпзор

1
Для протоколу: це алгоритм від algorithmist.com/index.php/Coin_Change Це перший результат Google щодо "зміни монети".
Дьєрдь Андрасек,

13

Мені подобається, що ви можете перевизначати типи, включаючи примітиви, такі як int, скільки завгодно разів і додавати різні методи. Як і визначення типу RomanNumeral:

package main

import (
    "fmt"
    "strings"
)

var numText = "zero one two three four five six seven eight nine ten"
var numRoman = "- I II III IV V VI VII IX X"
var aText = strings.Split(numText, " ")
var aRoman = strings.Split(numRoman, " ")

type TextNumber int
type RomanNumber int

func (n TextNumber) String() string {
    return aText[n]
}

func (n RomanNumber) String() string {
    return aRoman[n]
}

func main() {
    var i = 5
    fmt.Println("Number: ", i, TextNumber(i), RomanNumber(i))
}

Що роздруковує

Number:  5 five V

RomanNumber()Виклик по суті лита, він перевизначає Int типу в якості більш конкретного типу міжнар. І Println()дзвінки String()за лаштунки.


12

Повернення каналу

Це справжня ідіома, яка є досить важливою: як подати дані в канал і закрити їх потім. За допомогою цього ви можете зробити прості ітератори (оскільки діапазон приймає канал) або фільтри.

// return a channel that doubles the values in the input channel
func DoublingIterator(input chan int) chan int {
    outch := make(chan int);
    // start a goroutine to feed the channel (asynchronously)
    go func() {
        for x := range input {
            outch <- 2*x;    
        }
        // close the channel we created and control
        close(outch);
    }();
    return outch;
}

+1. Крім того, ви також можете передавати канали через канали.
Дьєрдь Андрасек,

5
Але будьте обережні, щоб не вирватися з циклу for x: = range chan {}, ви випустите програму та всю пам’ять, на яку вона посилається.
Джефф Аллен,

3
@JeffAllen як щодо defer close(outch);першого висловлювання про програму?

1
Відкласти черги на виконання для виконання, коли функція повертається, незалежно від того, яка точка повернення взята. Але якщо вхід каналу ніколи не закривається, то анонімна функція в цьому прикладі ніколи не залишить цикл for.
Джефф Аллен,


11
for {
    v := <-ch
    if closed(ch) {
        break
    }
    fmt.Println(v)
}

Оскільки діапазон автоматично перевіряє закритий канал, ми можемо скоротити до цього:

for v := range ch {
    fmt.Println(v)
}

9

Налаштована система make, яку ви можете використовувати в $ GOROOT / src

Налаштуйте ваш make-файл за допомогою

TARG=foobar           # Name of package to compile
GOFILES=foo.go bar.go # Go sources
CGOFILES=bang.cgo     # Sources to run cgo on
OFILES=a_c_file.$O    # Sources compiled with $Oc
                      # $O is the arch number (6 for x86_64)

include $(GOROOT)/src/Make.$(GOARCH)
include $(GOROOT)/src/Make.pkg

Потім ви можете використовувати засоби автоматизованого тестування, запустивши make test, або додати пакет та спільні об’єкти з cgo до вашого $ GOROOT за допомогою make install.


7

Ще одна цікава річ у Go - це godoc. Ви можете запустити його як веб-сервер на своєму комп’ютері за допомогою

godoc -http=:8080

де 8080 - це номер порту, а весь веб-сайт за адресою golang.org доступний за адресою localhost:8080.


Це звичайна програма чи демон?
Дьєрдь Андрасек

Це звичайна програма.
Джеремі

7

Це реалізація стека. Це ілюструє додавання методів до типу.

Я хотів перетворити частину стека на фрагмент і використовувати властивості фрагмента, але, хоча я працював без цього type, я не міг побачити синтаксис для визначення фрагмента за допомогою type.

package main

import "fmt"
import "os"

const stack_max = 100

type Stack2 struct {
    stack [stack_max]string
    size  int
}

func (s *Stack2) push(pushed_string string) {
    n := s.size
    if n >= stack_max-1 {
        fmt.Print("Oh noes\n")
        os.Exit(1)
    }
    s.size++
    s.stack[n] = pushed_string
}

func (s *Stack2) pop() string {
    n := s.size
    if n == 0 {
        fmt.Print("Underflow\n")
        os.Exit(1)
    }
    top := s.stack[n-1]
    s.size--
    return top
}

func (s *Stack2) print_all() {
    n := s.size
    fmt.Printf("Stack size is %d\n", n)
    for i := 0; i < n; i++ {
        fmt.Printf("%d:\t%s\n", i, s.stack[i])
    }
}

func main() {
    stack := new(Stack2)
    stack.print_all()
    stack.push("boo")
    stack.print_all()
    popped := stack.pop()
    fmt.Printf("Stack top is %s\n", popped)
    stack.print_all()
    stack.push("moo")
    stack.push("zoo")
    stack.print_all()
    popped2 := stack.pop()
    fmt.Printf("Stack top is %s\n", popped2)
    stack.print_all()
}

10
Замість того, щоб використовувати fmt.Printf(...); os.Exit();, ви можете використовувати panic(...).
notnoop

1
Це дає трасування стека, чого я не хочу.

3
Чому це обмежено? Go - це керована, gc'd-мова. Ваш стек може бути настільки глибоким, наскільки ви хочете. Використовуйте вбудований додаток append (), який зробить щось на зразок C realloc, коли це буде потрібно.
Джефф Аллен,

"Go не потребує дженериків", - сказали вони.
cubuspl42

4

Виклик коду c з go

Можна отримати доступ до нижчого рівня go за допомогою середовища виконання c.

C функції мають форму

void package·function(...)

(зверніть увагу, що розділювач крапок є символом унікоду), де аргументи можуть бути основними типами go, фрагментами, рядками тощо. Щоб повернути виклик значення

FLUSH(&ret)

(можна повернути більше одного значення)

Наприклад, для створення функції

package foo
bar( a int32, b string )(c float32 ){
    c = 1.3 + float32(a - int32(len(b))
}

в С ви використовуєте

#include "runtime.h"
void foo·bar(int32 a, String b, float32 c){
    c = 1.3 + a - b.len;
    FLUSH(&c);
}

Зверніть увагу, що ви все одно повинні оголосити функцію у файлі go, і що вам доведеться подбати про пам'ять самостійно. Я не впевнений, чи можливо викликати зовнішні бібліотеки за допомогою цього, можливо, краще використовувати cgo.

Подивіться на $ GOROOT / src / pkg / runtime для прикладів, що використовуються під час виконання.

Дивіться також цю відповідь для зв’язування коду c ++ з go.


3
Чи справді в ньому використовується "літаюча точка"? Я не наважуюсь редагувати, але це видається дещо несподіваним і радикальним.
розслабтесь

Так, вам потрібно скомпілювати з 6c (або 8c тощо). Я не думаю, що gcc обробляє ідентифікатори unicode.
Скотт Уельс

1
Я думаю, що період AltGr + вводить однаково · але з Unicode я не впевнений. Був дуже здивований, побачивши, що у джерелі, яке я прочитав .. чому б не використати щось на зразок ::?
u0b34a0f6ae

Персонаж - СЕРЕДНЯ КРАПКА U + 00B7. Можливо, синтаксичний аналізатор був обдурений, так що він розглядає це як символ для створення дійсного ідентифікатора c, який, на мою думку, виключає ::.
Скотт Уельс

4
'·' - це лише тимчасовий хакер, Роб навіть здивувався, що він все ще там, і він сказав, що його замінять на щось менш ідіосинкратичне.
uriel


3

Ти дивився цю розмову ? Він показує багато цікавих речей, які ви можете зробити (кінець розмови)


2
Так. Це зводиться до "Там є набагато більше, переходимо до наступної теми".
Дьєрдь Андрасек,

Так, мабуть, багато що можна сказати за короткий час

3

Стек, заснований на іншій відповіді, але використовуючи фрагмент, що додається, не має обмеження розміру.

package main

import "fmt"
import "os"

type Stack2 struct {
        // initial storage space for the stack
        stack [10]string
        cur   []string
}

func (s *Stack2) push(pushed_string string) {
        s.cur = append(s.cur, pushed_string)
}

func (s *Stack2) pop() (popped string) {
        if len(s.cur) == 0 {
                fmt.Print("Underflow\n")
                os.Exit(1)
        }
        popped = s.cur[len(s.cur)-1]
        s.cur = s.cur[0 : len(s.cur)-1]
        return
}

func (s *Stack2) print_all() {
        fmt.Printf("Stack size is %d\n", len(s.cur))
        for i, s := range s.cur {
                fmt.Printf("%d:\t%s\n", i, s)
        }
}

func NewStack() (stack *Stack2) {
        stack = new(Stack2)
        // init the slice to an empty slice of the underlying storage
        stack.cur = stack.stack[0:0]
        return
}

func main() {
        stack := NewStack()
        stack.print_all()
        stack.push("boo")
        stack.print_all()
        popped := stack.pop()
        fmt.Printf("Stack top is %s\n", popped)
        stack.print_all()
        stack.push("moo")
        stack.push("zoo")
        stack.print_all()
        popped2 := stack.pop()
        fmt.Printf("Stack top is %s\n", popped2)
        stack.print_all()
}

3
const ever = true

for ever {
    // infinite loop
}

25
гм. for { /* infinite loop */ }достатньо.
u0b34a0f6ae

2
Звичайно. Це саме те, що тут відбувається. Мені просто подобається foreverключове слово. Навіть Qt має макрос для цього.
Дьєрдь Андрасек

6
але для цього Go не потрібен макрос або милий псевдонім true.
u0b34a0f6ae

@ kaizer.se: Суть Юрі полягає в тому, що for ever(після оголошення змінної) це щось миле, що ви можете зробити в Go, якщо хочете. Це схоже на англійську (за модулем порожнє).
Френк

8
це щось миле, що ти можеш зробити і на Сі .. :-)#define ever (;;)
u0b34a0f6ae

2

В testосновному каталозі є багато невеликих програм . Приклади:

  • peano.go друкує факторіали.
  • hilbert.go має деяке множення матриць.
  • iota.go має приклади дивної йоти.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.