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
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)
}
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>
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 :)
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 (!)
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.
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.
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.
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");
}
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")
}
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
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;
}
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.
/*
* 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))
}
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;
}
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 .
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)
}
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.
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()
}
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
.
Voici un exemple d'utilisation du package sqlite3.
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.
const ever = true
for ever {
// infinite loop
}
Avez-vous regardé cet exposé ? Il montre beaucoup de trucs sympas que vous pouvez faire (fin de l'exposé)
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()
}
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.