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.
György Andrasek
la source
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)
}
György Andrasek
la source
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>
György Andrasek
la source
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 :)

jg-faustus
la source
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.

user181548
la source
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 (!)
György Andrasek
la source
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.

u0b34a0f6ae
la source
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.

Rob Russell
la source
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");
}
György Andrasek
la source
16

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")
}
Alvin Row
la source
3
J'ai déjà utilisé ceci: stackoverflow.com/questions/1726698/…
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;
}
György Andrasek
la source
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
György Andrasek
la source
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))
}
György Andrasek
la source
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.

jg-faustus
la source
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;
}
u0b34a0f6ae
la source
+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 .

György Andrasek
la source
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)
}
mbarkhau
la source
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.

Scott Wales
la source
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.

user181548
la source
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()
}
user181548
la source
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.

Scott Wales
la source
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)

user180100
la source
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()
}
Jeff Allen
la source
3
const ever = true

for ever {
    // infinite loop
}
György Andrasek
la source
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.
user181548
la source