Vai esempi e modi di dire [chiuso]


91

Non c'è molto codice Go da cui imparare la lingua e sono sicuro di non essere l'unico a sperimentarlo. Quindi, se hai scoperto qualcosa di interessante sulla lingua, per favore pubblica un esempio qui.

Sto anche cercando

  • modi idiomatici per fare le cose in Go,
  • Stile di pensiero C / C ++ "portato" su Go,
  • insidie ​​comuni sulla sintassi,
  • qualcosa di interessante, davvero.

Supporto ARM come 8 bit o 16 bit. La lingua D ancora non lo fa.

1
La libreria ( golang.org/pkg ) è un'ottima fonte per imparare come viene usato go. Personalmente, trovo che imparare come vengono implementate le strutture dati sia utile per imparare la lingua.
tkokasih

Risposte:


35

Rinviare dichiarazioni

Un'istruzione "defer" richiama una funzione la cui esecuzione è rimandata al momento in cui ritorna la funzione circostante.

DeferStmt = "defer" espressione.

L'espressione deve essere una chiamata di funzione o di metodo. Ogni volta che viene eseguita l'istruzione "defer", i parametri della chiamata di funzione vengono valutati e salvati di nuovo ma la funzione non viene richiamata. Le chiamate di funzione differite vengono eseguite in ordine LIFO immediatamente prima della restituzione della funzione circostante, ma dopo che i valori restituiti, se presenti, sono stati valutati.


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

Aggiornare:

deferè ora anche il modo idiomatico di gestire panicin modo eccezionale :

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
Sembra il buon vecchio RAII (reso esplicito).
Konrad Rudolph,

4
+1 da quando ho letto molto su Go, ma ancora non l'ho visto (fino a quando non me l'hai mostrato)!
u0b34a0f6ae

Intelligente, anche se avrebbe più senso per me se rinviare le dichiarazioni vengono eseguite in ordine FIFO (dall'alto verso il basso), ma forse sono solo io ...
Mike Spross

Freddo. Mi ricorda le guardie di rottami da D digitalmars.com/d/2.0/exception-safe.html
hasen

4
@ Mike: se confronti con blocchi di "prova: .. finalmente:" LIFO si annida allo stesso modo. Per le coppie di apertura / chiusura di risorse, ecc., L'annidamento in questo modo è l'unica cosa che ha senso (la prima apertura si chiuderà per ultima).
u0b34a0f6ae

25

I file oggetto Go in realtà includono un'intestazione in chiaro:

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
È più simile a una caratteristica nascosta che a un esempio idiomatico
hasen

22

Ho visto un paio di persone lamentarsi del for-loop, sulla falsariga di "perché dovremmo dire i = 0; i < len; i++in questo giorno ed età?".

Non sono d'accordo, mi piace il costrutto for. Puoi usare la versione lunga se lo desideri, ma l'idiomatico Go lo è

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

Il for .. rangecostrutto ripercorre tutti gli elementi e fornisce due valori: l'indice ie il valore v.

range funziona anche su mappe e canali.

Tuttavia, se non ti piace forin qualsiasi forma, puoi definire each, mapecc. In poche righe:

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
}

stampe

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

Sto iniziando a piacermi molto Go :)


Anche se rangeè bello solo se è compilato con lo stesso codice del ciclo for-3.
Thomas Ahle

19

Vai e ottieni la tua reputazione di stackoverflow

Questa è una traduzione di questa risposta .

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

Grazie a Scott Wales per l'aiuto con .Read ().

Questo sembra ancora abbastanza goffo, con le due corde e due buffer, quindi se qualche esperto di Go ha un consiglio, fammelo sapere.


Non sono sicuro di cosa avrebbe dovuto esserci di sbagliato nella formattazione; L'ho restaurato.

5
Gli autori di Go consigliano il gofmttuo codice :-)
ℝaphink

Non riesco a compilarlo: $ ../go/src/cmd/6g/6g SO.go SO.go: 34: undefined: json.StringToJson
ℝaphink

@Raphink: la lingua è cambiata da quando l'ho creata.

Sì, sai forse qual è l'equivalente più vicino a StringToJson? Si creava internamente un builder, ora si deve fornire il proprio con una struttura nativa predefinita?
macbirdie

19

Ecco un bell'esempio di iota dal post di 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
Notare che i punti e virgola non sono necessari.
mk12

18

È possibile scambiare variabili mediante assegnazione parallela:

x, y = y, x

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

semplice ma efficace.


18

Ecco un idioma dalla pagina Effective 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

L'istruzione switch diventa true quando non viene fornita alcuna espressione. Quindi questo è equivalente a

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

Al momento, la versione con interruttore mi sembra un po 'più pulita.


6
Whoa, completamente strappato da VB. ;-) ( Switch True…)
Konrad Rudolph

@Konrad, battimi! :) Ho già usato quell'idioma nel codice VB6 e sicuramente può aiutare la leggibilità in determinate situazioni.
Mike Spross

Cos'è "<="? È correlato a "<-"?
ℝaphink

@Raphink: minore o uguale a.
Paul Ruane

17

Tipo interruttori :

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

Parametri dei risultati denominati

È possibile assegnare nomi ai "parametri" di ritorno o di risultato di una funzione Go e utilizzarli come variabili regolari, proprio come i parametri in entrata. Quando vengono nominati, vengono inizializzati ai valori zero per i loro tipi quando inizia la funzione; se la funzione esegue un'istruzione return senza argomenti, i valori correnti dei parametri del risultato vengono utilizzati come valori restituiti.

I nomi non sono obbligatori ma possono rendere il codice più breve e più chiaro: sono documentazione. Se nominiamo i risultati di nextInt diventa ovvio quale int restituito è quale.

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

Poiché i risultati con nome sono inizializzati e legati a un ritorno disadorno, possono semplificare e chiarire. Ecco una versione di io.ReadFull che li utilizza bene:

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
Sono curioso: ce l'ha qualche altra lingua?
u0b34a0f6ae

1
Matlab ha qualcosa di simile.
Dan Lorenc

pascal utilizza una sintassi simile per restituire un valore.
nes1983

1
@ nes1983 Per chi non lo sapesse, in Pascal assegni classicamente il valore di ritorno al nome della funzione.
fuz

FORTRAN ha praticamente questo.
Hut8

14

Dalla risposta di James Antill :

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

Inoltre, una potenziale trappola: la sottile differenza tra gli operatori di ricezione e invio:

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

3
Lo stesso operatore di ricezione è ora un'operazione di blocco, a partire da Go 1.0.3. La specifica è stata modificata: golang.org/ref/spec#Receive_operator . Prova il comportamento di blocco (deadlock) qui: 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
Suggerirei di rimuovere il nome del sito per la risoluzione dei problemi e il numero di identificazione. Forse riformulare la domanda. Per non rovinare il problema a chi ci inciampa. O cercando di imbrogliare cercando il problema in rete per quella materia.
Mizipzor

1
Per la cronaca: questo è l'algoritmo di algoritmist.com/index.php/Coin_Change. È il primo risultato di Google per "cambio moneta".
György Andrasek

13

Mi piace che tu possa ridefinire i tipi, comprese le primitive come int, tutte le volte che vuoi e collegare metodi diversi. Come definire un tipo 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))
}

Che stampa

Number:  5 five V

La RomanNumber()chiamata è essenzialmente un cast, ridefinisce il tipo int come un tipo più specifico di int. E Println()chiama String()dietro le quinte.


12

Restituzione di un canale

Questo è un vero idioma abbastanza importante: come inserire dati in un canale e chiuderlo in seguito. Con questo puoi creare semplici iteratori (dato che range accetterà un canale) o filtri.

// 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. Inoltre, puoi anche far passare i canali attraverso i canali.
György Andrasek

5
Ma fai attenzione a non rompere un ciclo for x: = range chan {}, perderai la goroutine e tutta la memoria a cui fa riferimento.
Jeff Allen

3
@ JeffAllen che ne dici defer close(outch);come prima dichiarazione della goroutine?

1
Defer accoda un'istruzione per l'esecuzione quando la funzione ritorna, indipendentemente dal punto di ritorno preso. Ma se l'ingresso del canale non viene mai chiuso, la funzione anonima in questo esempio non lascerà mai il ciclo for.
Jeff Allen

11

Timeout per le letture dei canali:

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

Rubato a Davies Liu .


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

Poiché l'intervallo verifica automaticamente la presenza di un canale chiuso, possiamo accorciarlo a questo:

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

9

C'è un sistema di creazione che puoi usare in $ GOROOT / src

Imposta il tuo makefile con

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

È quindi possibile utilizzare gli strumenti di test automatizzati eseguendo make test o aggiungere il pacchetto e gli oggetti condivisi da cgo a $ GOROOT con make install.


7

Un'altra cosa interessante in Go è quella godoc. Puoi eseguirlo come server web sul tuo computer usando

godoc -http=:8080

dove 8080 è il numero di porta e l'intero sito Web su golang.org è quindi disponibile all'indirizzo localhost:8080.


È un programma normale o un demone?
György Andrasek

È un programma regolare.
Jeremy

7

Questa è un'implementazione di uno stack. Illustra l'aggiunta di metodi a un tipo.

Volevo trasformarne la parte dello stack in una sezione e utilizzare le proprietà della sezione, ma sebbene typeriuscissi a farlo funzionare senza , non riuscivo a vedere la sintassi per definire una sezione con un file 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
Piuttosto che usare fmt.Printf(...); os.Exit();, puoi usare panic(...).
notnoop

1
Questo dà una traccia dello stack, che non voglio.

3
Perché è limitato? Go è un linguaggio gestito e gc. Il tuo stack può essere profondo quanto vuoi. Usa il nuovo builtin append (), che farà qualcosa di simile al realloc di C quando necessario.
Jeff Allen

"Go non ha bisogno di farmaci generici", hanno detto.
cubuspl42

4

Chiamare il codice c da go

È possibile accedere al livello inferiore di go utilizzando il runtime c.

Le funzioni C sono nella forma

void package·function(...)

(nota che il separatore di punti è un carattere unicode) dove gli argomenti possono essere tipi di go di base, sezioni, stringhe ecc. Per restituire una chiamata a valore

FLUSH(&ret)

(puoi restituire più di un valore)

Ad esempio, per creare una funzione

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

in C usi

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

Nota che dovresti comunque dichiarare la funzione in un file go e che dovrai prenderti cura della memoria da solo. Non sono sicuro che sia possibile chiamare librerie esterne usando questo, potrebbe essere meglio usare cgo.

Guarda $ GOROOT / src / pkg / runtime per esempi usati nel runtime.

Vedi anche questa risposta per collegare il codice c ++ con go.


3
Usa il "punto volante", davvero? Non oso modificare, ma sembra un po 'inaspettato e radicale.
rilassarsi il

Sì, devi compilare con 6c (o 8c, ecc.). Non credo che gcc gestisca gli identificatori Unicode.
Scott Wales

1
Penso che il periodo AltGr + digiti lo stesso · ma con Unicode non sono sicuro. Sono rimasto molto sorpreso di vedere che nei sorgenti ho letto .. perché non usare qualcosa come ::?
u0b34a0f6ae

Il personaggio è MIDDLE DOT U + 00B7. Il parser potrebbe essere stato falsificato in modo che lo veda come un carattere per creare un identificatore c valido, che credo escluderebbe ::.
Scott Wales

4
Il "·" è solo un trucco temporaneo, rob è stato persino sorpreso che fosse ancora lì, ha detto che sarebbe stato sostituito con qualcosa di meno idiosincratico.
uriel


3

Hai guardato questo discorso ? Mostra molte cose interessanti che puoi fare (fine del discorso)


2
Si l'ho fatto. Si riduce a "C'è molto di più lì dentro, passiamo all'argomento successivo".
György Andrasek

Sì, a quanto pare c'è molto da dire con poco tempo

3

Uno stack basato sull'altra risposta, ma utilizzando l'aggiunta di slice per non avere limiti di dimensione.

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 */ }è abbastanza.
u0b34a0f6ae

2
Ovviamente. È esattamente quello che sta succedendo qui. Mi piace solo la foreverparola chiave. Anche Qt ha una macro per questo.
György Andrasek

6
ma Go non ha bisogno di una macro o di un simpatico alias di true per farlo.
u0b34a0f6ae

@ kaizer.se: Il punto di Jurily è che for ever(dopo aver dichiarato la variabile) è qualcosa di carino che puoi fare in Go se vuoi. Sembra inglese (modulo lo spazio vuoto).
Frank

8
è qualcosa di carino che puoi fare anche in C .. :-)#define ever (;;)
u0b34a0f6ae

2

Ci sono molti piccoli programmi nella testdirectory principale. Esempi:

  • peano.go stampa fattoriali.
  • hilbert.go ha qualche moltiplicazione di matrici.
  • iota.go ha esempi della cosa strana iota.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.