web-dev-qa-db-fra.com

Allez des exemples et des idiomes

Il n'y a pas beaucoup de code Go pour apprendre la langue et je suis sûr que je ne suis pas le seul à l'expérimenter. Donc, si vous avez découvert quelque chose d'intéressant sur la langue, veuillez poster un exemple ici.

Je cherche aussi

  • façons idiomatiques de faire les choses dans Go,
  • Style de pensée C/C++ "porté" vers Go,
  • pièges courants sur la syntaxe,
  • quelque chose d'intéressant, vraiment.
90
György Andrasek

Report des déclarations

Une instruction "defer" appelle une fonction dont l'exécution est différée au moment où la fonction environnante revient.

DeferStmt = "différer" l'expression.

L'expression doit être un appel de fonction ou de méthode. Chaque fois que l'instruction "defer" s'exécute, les paramètres de l'appel de fonction sont évalués et enregistrés à nouveau mais la fonction n'est pas appelée. Les appels de fonction différés sont exécutés dans l'ordre LIFO immédiatement avant le retour de la fonction environnante, mais après que les valeurs de retour, le cas échéant, ont été évaluées.


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

Mise à jour:

defer est maintenant aussi la manière idiomatique de gérer panic d'une manière semblable à une exception :

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)
}
35
György Andrasek

Les fichiers d'objet Go incluent en fait un en-tête en clair:

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>
25
György Andrasek

J'ai vu quelques personnes se plaindre de la boucle for, dans le sens de "pourquoi devrions-nous dire i = 0; i < len; i++ de nos jours? ".

Je ne suis pas d'accord, j'aime la construction for. Vous pouvez utiliser la version longue si vous le souhaitez, mais le Go idiomatique est

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

Le for .. range construct boucle sur tous les éléments et fournit deux valeurs - l'index i et la valeur v.

range fonctionne également sur les cartes et les canaux.

Néanmoins, si vous n'aimez pas for sous quelque forme que ce soit, vous pouvez définir each, map etc. en quelques lignes:

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
}

impressions

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

Je commence à aimer aller beaucoup :)

22
j-g-faustus

Voici un bel exemple d'iota de post de 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 (!)
19
György Andrasek

Allez chercher votre réputation de stackoverflow

Ceci est une traduction de cette réponse .

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

Merci à Scott Wales pour son aide avec .Read ().

Cela semble encore assez maladroit, avec les deux chaînes et les deux tampons, donc si des experts Go ont des conseils, faites-le moi savoir.

19
user181548

Voici un idiome de la page 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'instruction switch passe à true lorsqu'aucune expression n'est donnée. C'est donc équivalent à

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

Pour le moment, la version switch me semble un peu plus propre.

18
Rob Russell

Vous pouvez échanger des variables par affectation parallèle:

x, y = y, x

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

simple mais efficace.

18
u0b34a0f6ae

Type commutateurs :

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");
}
17
György Andrasek

Lors de l'importation de packages, vous pouvez redéfinir le nom comme vous le souhaitez:

package main

import f "fmt"

func main() {
    f.Printf("Hello World\n")
}
16
Alvin Row

De réponse de James Antill :

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

En outre, un piège potentiel: la différence subtile entre les opérateurs de réception et d'envoi:

a <- ch // sends ch to channel a
<-ch    // reads from channel ch
14
György Andrasek

Paramètres de résultat nommés

Les "paramètres" de retour ou de résultat d'une fonction Go peuvent recevoir des noms et être utilisés comme variables régulières, tout comme les paramètres entrants. Lorsqu'ils sont nommés, ils sont initialisés aux valeurs nulles pour leurs types lorsque la fonction commence; si la fonction exécute une instruction de retour sans arguments, les valeurs actuelles des paramètres de résultat sont utilisées comme valeurs renvoyées.

Les noms ne sont pas obligatoires mais ils peuvent rendre le code plus court et plus clair: ce sont des documents. Si nous nommons les résultats de nextInt, il devient évident lequel retourné int est lequel.

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

Parce que les résultats nommés sont initialisés et liés à un retour sans fioritures, ils peuvent simplifier et clarifier. Voici une version de io.ReadFull qui les utilise bien:

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;
}
14
György Andrasek

J'aime que vous puissiez redéfinir les types, y compris les primitives comme int, autant de fois que vous le souhaitez et attacher différentes méthodes. Comme définir un type 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))
}

Qui imprime

Number:  5 five V

L'appel RomanNumber() est essentiellement un transtypage, il redéfinit le type int comme un type plus spécifique d'int. Et Println() appelle String() dans les coulisses.

13
j-g-faustus
/* 
 * 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))
}
13
György Andrasek

Retourner une chaîne

C'est un véritable idiome qui est assez important: comment introduire des données dans un canal et le fermer ensuite. Avec cela, vous pouvez créer des itérateurs simples (puisque la plage acceptera un canal) ou des filtres.

// 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;
}
12
u0b34a0f6ae

Délai d'expiration pour les lectures de canaux:

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

Volé à Davies Li .

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

Étant donné que la plage vérifie automatiquement la présence d'un canal fermé, nous pouvons raccourcir ceci:

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

Il existe un système de création de make que vous pouvez utiliser dans $ GOROOT/src

Configurez votre makefile avec

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

Vous pouvez ensuite utiliser les outils de test automatisés en exécutant make test, ou ajouter le package et les objets partagés de cgo à votre $ GOROOT avec make install.

9
Scott Wales

Il s'agit d'une implémentation d'une pile. Il illustre l'ajout de méthodes sur un type.

Je voulais faire de la pile une partie de celle-ci en une tranche et utiliser les propriétés de la tranche, mais bien que cela fonctionne sans le type, je ne pouvais pas voir la syntaxe pour définir une tranche avec un 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()
}
7
user181548

Une autre chose intéressante dans Go est que godoc. Vous pouvez l'exécuter en tant que serveur Web sur votre ordinateur à l'aide de

godoc -http=:8080

où 8080 est le numéro de port, et le site Web entier sur golang.org est alors disponible sur localhost:8080.

7
user181548

Voici un exemple d'utilisation du package sqlite3.

http://github.com/bikal/gosqlite-example

4
Bikal Lem

Appel du code c à partir de go

Il est possible d'accéder au niveau inférieur de go en utilisant le runtime c.

Les fonctions C sont sous la forme

void package·function(...)

(notez que le séparateur de points est un caractère unicode) où les arguments peuvent être des types de base, des tranches, des chaînes, etc. Pour renvoyer un appel de valeur

FLUSH(&ret)

(vous pouvez renvoyer plusieurs valeurs)

Par exemple, pour créer une fonction

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

en C vous utilisez

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

Notez que vous devez toujours déclarer la fonction dans un fichier go et que vous devrez vous occuper de la mémoire vous-même. Je ne sais pas s'il est possible d'appeler des bibliothèques externes en utilisant cela, il peut être préférable d'utiliser cgo.

Regardez $ GOROOT/src/pkg/runtime pour des exemples utilisés dans le runtime.

Voir aussi cette réponse pour lier le code c ++ avec go.

4
Scott Wales
const ever = true

for ever {
    // infinite loop
}
3
György Andrasek

Avez-vous regardé cet exposé ? Il montre beaucoup de trucs sympas que vous pouvez faire (fin de l'exposé)

3
user180100

Une pile basée sur l'autre réponse, mais en utilisant une tranche s'ajoutant à aucune limite de taille.

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
Jeff Allen

Il y a beaucoup de petits programmes dans test dans le répertoire principal. Exemples:

  • peano.go imprime les factorielles.
  • hilbert.go a une certaine multiplication matricielle.
  • iota.go a des exemples de la chose bizarre iota.
2
user181548