Aller des exemples et des idiomes [fermé]


91

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

Je recherche aussi

  • manières idiomatiques de faire les choses dans Go,
  • Style de pensée C / C ++ «porté» vers Go,
  • écueils communs concernant la syntaxe,
  • quelque chose d'intéressant, vraiment.

Prise en charge ARM telle que 8 bits ou 16 bits. D langue ne le font toujours pas.

1
La bibliothèque ( golang.org/pkg ) est une excellente source pour apprendre comment go est utilisé. Personnellement, je trouve qu'apprendre comment les structures de données sont implémentées est utile pour apprendre la langue.
tkokasih

Réponses:


35

Différer les déclarations

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

DeferStmt = Expression "différer".

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

Mettre à jour:

deferest maintenant aussi la manière idiomatique de gérer panicde manière exceptionnelle :

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
Ressemble au bon vieux RAII (rendu explicite).
Konrad Rudolph

4
+1 depuis que j'ai beaucoup lu sur Go, mais je n'ai toujours pas vu cela (jusqu'à ce que vous me montriez)!
u0b34a0f6ae

Intelligent, même si cela aurait plus de sens pour moi si les déclarations
différées étaient

Cool. Cela me rappelle les gardes scrop de D digitalmars.com/d/2.0/exception-safe.html
hasen

4
@Mike: si vous comparez avec des blocs de "essayez: .. enfin:" LIFO se niche de la même manière. Pour les paires ouverture / fermeture de ressources, etc., une imbrication comme celle-ci est la seule chose qui ait du sens (la première ouverture se fermera en dernier).
u0b34a0f6ae

25

Les fichiers objets 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>

6
Cela ressemble plus à une fonctionnalité cachée qu'à un exemple idiomatique
hasen

22

J'ai vu quelques personnes se plaindre de la boucle for, du genre "pourquoi devrions-nous avoir à 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)
}

La for .. rangeconstruction boucle sur tous les éléments et fournit deux valeurs: l'index iet la valeur v.

range fonctionne également sur les cartes et les canaux.

Néanmoins, si vous n'aimez pas forsous quelque forme que ce soit, vous pouvez définir each, mapetc. 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 beaucoup Go :)


Bien que ce rangene soit bien que s'il est compilé avec le même code que la boucle for-3.
Thomas Ahle

19

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 deux tampons, donc si des experts de Go ont des conseils, faites le moi savoir.


Je ne suis pas sûr de ce qui était censé être le problème avec le formatage; Je l'ai restauré.

5
Les auteurs de Go recommandent à gofmtvotre code :-)
ℝaphink

Je ne peux pas le compiler: $ ../go/src/cmd/6g/6g SO.go SO.go: 34: undefined: json.StringToJson
ℝaphink

@Raphink: la langue a changé depuis que j'ai fait ça.

Ouais, savez-vous peut-être quel est l'équivalent le plus proche du StringToJson? Auparavant, il mettait en place un constructeur en interne, maintenant il faut fournir le sien avec une structure native prédéfinie?
macbirdie

19

Voici un bel exemple d'iota du 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 (!)

5
Notez que les points-virgules ne sont pas nécessaires.
mk12 le

18

Vous pouvez permuter les 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

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. Donc c'est é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.


6
Whoa, complètement arraché de VB. ;-) ( Switch True…)
Konrad Rudolph

@Konrad, battez-moi! :) J'ai déjà utilisé cet idiome dans le code VB6 et cela peut certainement aider à la lisibilité dans certaines situations.
Mike Spross

Qu'est-ce que «<=»? Est-ce lié à «<-»?
ℝaphink

@Raphink: inférieur à ou égal.
Paul Ruane

17

Commutateurs de type :

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

Paramètres de résultat nommés

Les "paramètres" de retour ou de résultat d'une fonction Go peuvent être nommés et 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 argument, 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 de la documentation. Si nous nommons les résultats de nextInt, il devient évident quel int renvoyé est lequel.

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

Étant donné que les résultats nommés sont initialisés et liés à un retour sans fioritures, ils peuvent simplifier autant que 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;
}

1
Je suis curieux - est-ce qu'une autre langue a cela?
u0b34a0f6ae

1
Matlab a quelque chose de similaire.
Dan Lorenc

pascal utilise une syntaxe similaire pour renvoyer une valeur.
nes1983

1
@ nes1983 Pour ceux qui ne savent pas, en Pascal, vous attribuez classiquement la valeur de retour au nom de la fonction.
fuz le

FORTRAN a à peu près ça.
Hut8

14

D'après la réponse de James Antill :

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

Aussi, un écueil 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

3
L'opérateur de réception lui - même est maintenant une opération de blocage, à partir de Go 1.0.3. La spécification a été modifiée: golang.org/ref/spec#Receive_operator . Veuillez essayer le comportement de blocage (blocage) ici: 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
Je suggérerais de supprimer le nom du site de résolution de problèmes ainsi que le numéro d'identification. Peut-être reformulez la question. Pour ne pas gâcher le problème à quelqu'un qui trébuche dessus. Ou en essayant de tricher en recherchant le problème sur le net d'ailleurs.
Mizipzor

1
Pour mémoire: il s'agit de l'algorithme de algorithmist.com/index.php/Coin_Change C'est le premier résultat Google pour le "changement de pièce".
György Andrasek

13

J'aime que vous puissiez redéfinir des types, y compris des 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' RomanNumber()appel est essentiellement un cast, il redéfinit le type int comme un type plus spécifique d'int. Et des Println()appels String()dans les coulisses.


12

Retourner une chaîne

C'est un vrai idiome qui est assez important: comment introduire des données dans un canal et le fermer par la suite. Avec cela, vous pouvez faire des itérateurs simples (puisque range 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;
}

+1. En outre, vous pouvez également faire passer les chaînes à travers les chaînes.
György Andrasek

5
Mais veillez à ne pas sortir d'une boucle for x: = range chan {}, vous fuiriez le goroutine, et toute la mémoire qu'il référence.
Jeff Allen

3
@JeffAllen que diriez-vous defer close(outch);de la première déclaration du goroutine?

1
Defer met en file d'attente une instruction pour exécution lorsque la fonction retourne, quel que soit le point de retour utilisé. Mais si l'entrée de canal n'est jamais fermée, la fonction anonyme de cet exemple ne quittera jamais la boucle for.
Jeff Allen

11

Timeout pour les lectures de canal:

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

Volé à Davies Liu .


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

Puisque range vérifie automatiquement un canal fermé, nous pouvons raccourcir ceci:

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

9

Il y a une configuration de make system 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.


7

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

godoc -http=:8080

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


Est-ce un programme régulier ou un démon?
György Andrasek

C'est un programme régulier.
Jeremy

7

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

Je voulais en faire une partie de la pile 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()
}

10
Plutôt que d'utiliser fmt.Printf(...); os.Exit();, vous pouvez utiliser panic(...).
notnoop

1
Cela donne une trace de pile, ce que je ne veux pas.

3
Pourquoi est-ce limité? Go est un langage géré et gc. Votre pile peut être aussi profonde que vous le souhaitez. Utilisez le nouveau intégré append (), qui fera quelque chose comme la réallocation de C quand il en aura besoin.
Jeff Allen

"Go n'a pas besoin de génériques", ont-ils dit.
cubuspl42

4

Appeler le code C depuis 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 go de base, des tranches, des chaînes, etc. Pour renvoyer un appel de valeur

FLUSH(&ret)

(vous pouvez renvoyer plus d'une valeur)

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 du code C ++ avec go.


3
Utilise-t-il vraiment le «point volant»? Je n'ose pas éditer, mais cela semble un peu inattendu et radical.
détendre

Oui, vous devez compiler avec 6c (ou 8c, etc.). Je ne pense pas que gcc gère les identifiants Unicode.
Scott Wales

1
Je pense que la période AltGr + est la même · mais avec unicode, je ne suis pas sûr. J'ai été très surpris de voir que dans la source j'ai lu .. pourquoi ne pas utiliser quelque chose comme ::?
u0b34a0f6ae

Le caractère est MIDDLE DOT U + 00B7. L'analyseur a peut-être été truqué pour qu'il le voit comme un caractère afin de créer un identificateur c valide, ce qui, je crois, empêcherait ::.
Scott Wales

4
Le '·' est juste un hack temporaire, Rob était même surpris qu'il soit toujours là, il a dit qu'il allait être remplacé par quelque chose de moins idiosyncratique.
uriel


3

Avez-vous regardé cette conférence ? Il montre beaucoup de trucs sympas que vous pouvez faire (fin de la conférence)


2
Oui je l'ai fait. Cela se résume à "Il y a beaucoup plus là-dedans, passons au sujet suivant".
György Andrasek

Oui, apparemment beaucoup à dire avec peu de temps

3

Une pile basée sur l'autre réponse, mais utilisant l'ajout de tranche pour ne pas avoir de 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
const ever = true

for ever {
    // infinite loop
}

25
ahem. for { /* infinite loop */ }est assez.
u0b34a0f6ae

2
Bien sûr. C'est exactement ce qui se passe ici. J'aime juste le forevermot - clé. Même Qt a une macro pour cela.
György Andrasek

6
mais Go n'a pas besoin d'une macro ou d'un joli alias de true pour ce faire.
u0b34a0f6ae

@ kaizer.se: Le point de Jurily est que for ever(après avoir déclaré la variable) est quelque chose de mignon que vous pouvez faire dans Go si vous le souhaitez. Il ressemble à l'anglais (modulo le blanc).
Frank

8
c'est quelque chose de mignon que vous pouvez faire en C aussi .. :-)#define ever (;;)
u0b34a0f6ae

2

Il y a beaucoup de petits programmes testdans 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.
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.