Idź przykłady i idiomy [zamknięte]


91

Nie ma zbyt wiele kodu Go, z którego można by się nauczyć języka, i jestem pewien, że nie tylko ja z nim eksperymentuję. Jeśli więc znalazłeś coś interesującego w tym języku, zamieść tutaj przykład.

Ja też szukam

  • idiomatyczne sposoby robienia rzeczy w Go,
  • Styl myślenia C / C ++ „przeniesiony” do Go,
  • typowe pułapki dotyczące składni,
  • naprawdę coś interesującego.

Obsługa ARM, na przykład 8-bitowa lub 16-bitowa. Język D nadal nie.

1
Biblioteka ( golang.org/pkg ) jest doskonałym źródłem informacji o używaniu go. Osobiście uważam, że poznanie sposobu implementacji struktur danych jest pomocne w nauce języka.
tkokasih

Odpowiedzi:


35

Odrocz oświadczenia

Instrukcja „defer” wywołuje funkcję, której wykonanie jest odroczone do momentu powrotu funkcji otaczającej.

DeferStmt = "defer" Wyrażenie.

Wyrażenie musi być funkcją lub wywołaniem metody. Za każdym razem, gdy wykonywana jest instrukcja „defer”, parametry wywołania funkcji są oceniane i zapisywane na nowo, ale funkcja nie jest wywoływana. Odroczone wywołania funkcji są wykonywane w kolejności LIFO bezpośrednio przed zwróceniem funkcji otaczającej, ale po oszacowaniu wartości zwracanych, jeśli istnieją.


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);
}

Aktualizacja:

deferjest teraz również idiomatycznym sposobem obsługi panicw sposób podobny do wyjątków :

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
Wygląda na stary dobry RAII (wyraźnie zaznaczony).
Konrad Rudolph

4
+1, ponieważ dużo czytałem o Go, ale nadal tego nie widziałem (dopóki mi nie pokazałeś)!
u0b34a0f6ae

Sprytne, chociaż dla mnie bardziej sensowne byłoby, gdyby odraczanie instrukcji było wykonywane w kolejności FIFO (od góry do dołu), ale może to tylko ja ...
Mike Spross

Fajne. Przypomina mi osłonę przeciwnika z D digitalmars.com/d/2.0/exception-safe.html
hasen

4
@Mike: jeśli porównasz z blokami „try: .. w końcu:” LIFO zagnieżdża się w ten sam sposób. W przypadku par otwierania / zamykania zasobów itp. Takie zagnieżdżanie jest jedyną rzeczą, która ma sens (pierwsze otwarcie zamknie się jako ostatnie).
u0b34a0f6ae

25

Pliki obiektów Go zawierają w rzeczywistości nagłówek w postaci zwykłego tekstu:

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
To bardziej jak ukryte cechy niż na przykład idiomatyczne
hasen

22

Widziałem kilka osób narzekających na pętlę for, w stylu „dlaczego powinniśmy mówić i = 0; i < len; i++w dzisiejszych czasach?”.

Nie zgadzam się, podoba mi się konstrukcja for. Jeśli chcesz, możesz użyć długiej wersji, ale idiomatyczny Go jest

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

for .. rangeKonstrukt pętle nad wszystkimi elementami i zasila dwie wartości - indeksi oraz wartośćv .

range działa również na mapach i kanałach.

Mimo to, jeśli nie podoba forw jakiejkolwiek formie, można określić each, mapitd. W kilku zdaniach:

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
}

wydruki

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

Zaczynam bardzo lubić Go :)


Chociaż rangejest fajny tylko wtedy, gdy jest skompilowany do tego samego kodu co pętla for-3.
Thomas Ahle

19

Idź i zdobądź swoją reputację przepełnienia stosów

To jest tłumaczenie tej odpowiedzi .

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"))
}

Dzięki Scotta Walesa za pomoc z .Read ().

To wciąż wygląda dość niezgrabnie, z dwoma ciągami i dwoma buforami, więc jeśli któryś z ekspertów Go ma radę, daj mi znać.


Nie jestem pewien, co powinno być nie tak z formatowaniem; Przywróciłem to.

5
Autorzy Go polecają gofmtTwój kod :-)
ℝaphink

Nie mogę tego skompilować: $ ../go/src/cmd/6g/6g SO.go SO.go: 34: undefined: json.StringToJson
ℝaphink

@Raphink: język się zmienił, odkąd to zrobiłem.

Tak, czy wiesz, może co jest najbliższym odpowiednikiem StringToJson? Kiedyś wewnętrznie konfigurował program budujący, teraz trzeba zapewnić jego własną z predefiniowaną natywną strukturą?
macbirdie,

19

Oto ładny przykład joty z postu Kinopiko :

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
Zwróć uwagę, że średniki są niepotrzebne.
mk12,

18

Możesz zamieniać zmienne przez równoległe przypisanie:

x, y = y, x

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

proste ale efektywne.


18

Oto idiom z Skuteczna Go stronie

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

Instrukcja switch włącza wartość true, gdy nie jest podane żadne wyrażenie. Więc to jest równoważne

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

W tej chwili wersja na Switcha wygląda dla mnie trochę czyściej.


6
Whoa, całkowicie wyrwany z VB. ;-) ( Switch True…)
Konrad Rudolph

@Konrad, pokonaj mnie! :) Używałem już tego idiomu w kodzie VB6 i zdecydowanie może pomóc w czytelności w pewnych sytuacjach.
Mike Spross

Co to jest „<=”? Czy jest to związane z „<-”?
ℝaphink

@Raphink: mniej niż lub równa się.
Paul Ruane

17

Typ przełączników :

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

Nazwane parametry wyników

Zwracającym lub wynikowym „parametrom” funkcji Go można nadać nazwy i używać ich jako zwykłych zmiennych, tak jak parametry przychodzące. Gdy są nazwane, są inicjowane z zerowymi wartościami dla ich typów, gdy rozpoczyna się funkcja; jeśli funkcja wykonuje instrukcję return bez argumentów, bieżące wartości parametrów wynikowych są używane jako wartości zwracane.

Nazwy nie są obowiązkowe, ale mogą sprawić, że kod będzie krótszy i bardziej przejrzysty: są dokumentacją. Jeśli nazwiemy wyniki nextInt, stanie się oczywiste, który zwrócony int jest który.

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

Ponieważ nazwane wyniki są inicjalizowane i powiązane z nie zdobionym zwrotem, mogą one zarówno upraszczać, jak i wyjaśniać. Oto wersja io.ReadFull, która dobrze je wykorzystuje:

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
Jestem ciekawy - czy ma to jakiś inny język?
u0b34a0f6ae

1
Matlab ma coś podobnego.
Dan Lorenc

pascal używa podobnej składni do zwracania jednej wartości.
nes1983

1
@ nes1983 Dla tych, którzy nie wiedzą, w Pascalu klasycznie przypisujesz wartość zwracaną do nazwy funkcji.
fuz

FORTRAN prawie to ma.
Hut8

14

Z Jamesa Antilla :

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

Potencjalna pułapka: subtelna różnica między operatorami odbierania i wysyłania:

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

3
Sam operator odbierający jest teraz operacją blokującą, od wersji Go 1.0.3. Specyfikacja została zmodyfikowana: golang.org/ref/spec#Receive_operator . Wypróbuj zachowanie blokujące (zakleszczenie) tutaj: play.golang.org/p/0yurtWW4Q3
Deleplace Stycznia

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
Sugerowałbym usunięcie nazwy strony rozwiązywania problemów oraz numeru identyfikacyjnego. Może przeformułuj pytanie. Żeby nie zepsuć problemu komuś, kto się o to potyka. Lub próbować oszukiwać, szukając problemu w sieci.
Mizipzor

1
Dla przypomnienia: to jest algorytm z Algorytmist.com/index.php/Coin_Change To pierwszy wynik Google dotyczący „wymiany monet”.
György Andrasek

13

Podoba mi się, że możesz przedefiniować typy, w tym prymitywy, takie jak int, tyle razy, ile chcesz, i dołączyć różne metody. Podobnie jak definiowanie typu 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))
}

Który się drukuje

Number:  5 five V

RomanNumber()Wezwanie jest w istocie obsada, to redefiniuje typu int jako bardziej konkretnego typu int. I Println()dzwoni String()za kulisami.


12

Zwracanie kanału

To prawdziwy idiom, który jest dość ważny: jak wprowadzić dane do kanału, a następnie go zamknąć. Dzięki temu możesz tworzyć proste iteratory (ponieważ zakres zaakceptuje kanał) lub filtry.

// 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. Możesz także przepuszczać kanały przez kanały.
György Andrasek

5
Ale uważaj, aby nie wyrwać się z pętli for x: = range chan {}, ponieważ wycieknie goroutine i cała pamięć, do której się odnosi.
Jeff Allen,

3
@JeffAllen co powiesz defer close(outch);na pierwsze stwierdzenie goroutine?

1
Odroczenie kolejki instrukcji do wykonania, gdy funkcja zwraca, bez względu na to, który punkt powrotu zostanie przyjęty. Ale jeśli wejście kanału nigdy nie zostanie zamknięte, funkcja anonimowa w tym przykładzie nigdy nie opuści pętli for.
Jeff Allen

11

Limit czasu odczytu kanałów:

ticker := time.NewTicker(ns);
select {
    case v := <- chan_target:
        do_something_with_v;
    case <- ticker.C:
        handle_timeout;
}

Skradziony Daviesowi Liu .


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

Ponieważ zakres automatycznie sprawdza zamknięty kanał, możemy skrócić do tego:

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

9

Jest skonfigurowany system make, którego możesz użyć w $ GOROOT / src

Skonfiguruj swój plik makefile za pomocą

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

Następnie możesz użyć narzędzi do automatycznego testowania, uruchamiając make test lub dodać pakiet i obiekty udostępnione z cgo do $ GOROOT za pomocą polecenia make install.


7

Inną interesującą rzeczą w Go jest to godoc. Możesz uruchomić go jako serwer WWW na swoim komputerze za pomocą

godoc -http=:8080

gdzie 8080 to numer portu, a cała witryna internetowa pod adresem golang.org jest dostępna pod adresem localhost:8080.


Czy to jest zwykły program czy demon?
György Andrasek

To jest regularny program.
Jeremy,

7

To jest implementacja stosu. Ilustruje dodawanie metod do typu.

Chciałem przekształcić część stosu w plasterek i użyć właściwości tego wycinka, ale chociaż udało mi się to działać bez type, nie widziałem składni definiowania plasterka za pomocą 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
Zamiast używać fmt.Printf(...); os.Exit();, możesz użyć panic(...).
notnoop

1
To daje ślad stosu, którego nie chcę.

3
Dlaczego jest ograniczona? Go jest zarządzanym językiem. Twój stack może być tak głęboki, jak chcesz. Użyj nowej wbudowanej funkcji append (), która w razie potrzeby wykona coś w rodzaju ponownego przydzielenia w C.
Jeff Allen,

„Go nie potrzebuje leków generycznych” - powiedzieli.
cubuspl42

4

Dzwonię z kodu c z drogi

Dostęp do niższego poziomu go można uzyskać za pomocą środowiska uruchomieniowego c.

Funkcje C są w postaci

void package·function(...)

(zwróć uwagę, że separator kropek to znak Unicode), gdzie argumentami mogą być podstawowe typy go, plasterki, ciągi znaków itp. Aby zwrócić wywołanie wartości

FLUSH(&ret)

(możesz zwrócić więcej niż jedną wartość)

Na przykład, aby utworzyć funkcję

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

w C używasz

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

Zwróć uwagę, że nadal powinieneś zadeklarować funkcję w pliku go i że będziesz musiał sam zadbać o pamięć. Nie jestem pewien, czy można przy użyciu tego wywoływać biblioteki zewnętrzne, może lepiej użyć cgo.

Spójrz na $ GOROOT / src / pkg / runtime, aby zobaczyć przykłady używane w środowisku wykonawczym.

Zobacz także tę odpowiedź, aby połączyć kod C ++ z go.


3
Czy naprawdę używa „latającej kropki”? Nie śmiem edytować, ale wydaje mi się to trochę nieoczekiwane i radykalne.
zrelaksuj się

Tak, musisz skompilować się z 6c (lub 8c itp.). Nie sądzę, że gcc obsługuje identyfikatory Unicode.
Scott Wales

1
Myślę, że AltGr + okresy są takie same · ale z Unicode nie jestem pewien. Byłem bardzo zaskoczony, widząc, że w źródle, które przeczytałem… dlaczego nie użyć czegoś takiego jak ::?
u0b34a0f6ae

Znak to ŚRODKOWA KROPKA U + 00B7. Parser mógł zostać sfałszowany tak, że widzi to jako znak w celu utworzenia prawidłowego identyfikatora c, co moim zdaniem wykluczałoby ::.
Scott Wales,

4
Znak „·” to tylko tymczasowe włamanie, Rob był nawet zaskoczony, że wciąż tam jest, powiedział, że zostanie zastąpiony czymś mniej osobliwym.
uriel


3

Oglądałeś tę rozmowę ? Pokazuje wiele fajnych rzeczy, które możesz zrobić (koniec rozmowy)


2
Tak. Sprowadza się do stwierdzenia: „Jest w tym dużo więcej, przejdźmy do następnego tematu”.
György Andrasek

Tak, najwyraźniej dużo do powiedzenia w krótkim czasie

3

Stos oparty na drugiej odpowiedzi, ale przy użyciu dołączania plasterków nie ma ograniczenia rozmiaru.

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
ahem. for { /* infinite loop */ }wystarczy.
u0b34a0f6ae

2
Oczywiście. Dokładnie to się tutaj dzieje. Po prostu podoba mi się foreversłowo kluczowe. Nawet Qt ma do tego makro.
György Andrasek

6
ale Go nie potrzebuje do tego makra ani uroczego aliasu true.
u0b34a0f6ae

@ kaizer.se: Jurily twierdzi, że for ever(po zadeklarowaniu zmiennej) jest to coś fajnego, co możesz zrobić w Go, jeśli chcesz. Wygląda jak angielski (modulo the blank).
Frank

8
to jest coś #define ever (;;)
fajnego,

2

W testgłównym katalogu znajduje się wiele małych programów . Przykłady:

  • peano.go drukuje silnie.
  • hilbert.go ma pewne mnożenie macierzy.
  • iota.go ma przykłady dziwnej joty.
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.