Exemplos e expressões idiomáticas de Go [fechado]


91

Não há muito código Go para aprender a linguagem, e tenho certeza de que não sou o único fazendo experiências com isso. Então, se você descobriu algo interessante sobre o idioma, poste um exemplo aqui.

Eu também estou procurando

  • maneiras idiomáticas de fazer as coisas em Go,
  • Estilo de pensamento C / C ++ "adaptado" para ir,
  • armadilhas comuns sobre a sintaxe,
  • qualquer coisa interessante, realmente.

Suporte a ARM, como 8 bits ou 16 bits. A linguagem D ainda não.

1
A biblioteca ( golang.org/pkg ) é uma excelente fonte para aprender como o go é usado. Pessoalmente, acho que aprender como as estruturas de dados são implementadas é útil para aprender a linguagem.
— tkokasih

Respostas:


35

Adiar declarações

Uma instrução "adiar" invoca uma função cuja execução é adiada para o momento em que a função circundante retorna.

DeferStmt = "adiar" Expressão.

A expressão deve ser uma função ou chamada de método. Cada vez que a instrução "adiar" é executada, os parâmetros da chamada de função são avaliados e salvos novamente, mas a função não é chamada. Chamadas de função adiadas são executadas em ordem LIFO imediatamente antes do retorno da função circundante, mas depois que os valores de retorno, se houver, forem avaliados.


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

Atualizar:

deferé agora também a maneira idiomática de manusear panicem uma excepção semelhante forma:

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
Parece o bom e velho RAII (explicitado).
— Konrad Rudolph

4
+1 já que li muito sobre Go, mas ainda não vi isso (até você me mostrar)!
— u0b34a0f6ae

Inteligente, embora faria mais sentido para mim adiar as instruções onde executadas em ordem FIFO (de cima para baixo), mas talvez seja só eu ...
— Mike Spross

Legal. Lembra-me dos guardas de scrop de D digitalmars.com/d/2.0/exception-safe.html
— hasen

4
@Mike: se você comparar com blocos de "try: .. finally:" ninhos LIFO da mesma maneira. Para pares de recursos abertos / fechados, etc., aninhar assim é a única coisa que faz sentido (a primeira abertura fecha por último).
— u0b34a0f6ae

25

Os arquivos de objeto Go na verdade incluem um cabeçalho de texto não criptografado:

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
Isso é mais um recurso oculto do que um exemplo idiomático
— hasen

22

Tenho visto algumas pessoas reclamando do loop for, do tipo "por que deveríamos dizer i = 0; i < len; i++nos dias de hoje?".

Eu discordo, gosto da construção for. Você pode usar a versão longa se desejar, mas a idiomática Go é

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

A for .. rangeconstrução percorre todos os elementos e fornece dois valores - o índice ie o valor v.

range também funciona em mapas e canais.

Ainda assim, se você não gostar forde alguma forma, você pode definir each, mapetc. em algumas linhas:

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
}

estampas

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

Estou começando a gostar muito do Go :)


Porém, rangesó é bom se for compilado com o mesmo código do loop for-3.
— Thomas Ahle

19

Vá e obtenha sua reputação de stackoverflow

Esta é uma tradução desta resposta .

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

Agradecimentos a Scott Wales pela ajuda com .Read ().

Isso ainda parece bastante desajeitado, com as duas strings e dois buffers, então, se algum especialista em Go tiver conselhos, me avise.


Não tenho certeza do que deveria estar errado com a formatação; Eu o restaurei.

5
Os autores do Go recomendam para o gofmtseu código :-)
— ℝaphink

Não consigo compilá-lo: $ ../go/src/cmd/6g/6g SO.go SO.go: 34: undefined: json.StringToJson
— ℝaphink

@Raphink: a linguagem mudou desde que fiz isso.

Sim, você sabe o que é o equivalente mais próximo do StringToJson? Costumava configurar um construtor internamente, agora é preciso fornecer o seu próprio com uma estrutura nativa predefinida?
— macbirdie

19

Aqui está um bom exemplo de iota da postagem 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
Observe que os pontos-e-vírgulas são desnecessários.
— mk12

18

Você pode trocar variáveis ​​por atribuição paralela:

x, y = y, x

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

simples mas efetivo.


18

Aqui está um idioma da página 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

A instrução switch muda para true quando nenhuma expressão é fornecida. Então, isso é 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

No momento, a versão switch parece um pouco mais limpa para mim.


6
Whoa, completamente arrancado do VB. ;-) ( Switch True…)
— Konrad Rudolph

@Konrad, chegue antes de mim! :) Já usei esse idioma no código VB6 antes e definitivamente pode ajudar na legibilidade em certas situações.
— Mike Spross de

O que é '<='? Está relacionado a '<-'?
— ℝaphink

@Raphink: menor que ou igual.
— Paul Ruane

17

Interruptores de tipo :

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

Parâmetros de resultado nomeados

Os "parâmetros" de retorno ou resultado de uma função Go podem receber nomes e usados ​​como variáveis ​​regulares, assim como os parâmetros de entrada. Quando nomeados, eles são inicializados com os valores zero para seus tipos quando a função começa; se a função executa uma instrução de retorno sem argumentos, os valores atuais dos parâmetros de resultado são usados ​​como os valores retornados.

Os nomes não são obrigatórios, mas podem tornar o código mais curto e claro: eles são documentação. Se nomearmos os resultados de nextInt, ficará óbvio qual int retornado é qual.

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

Como os resultados nomeados são inicializados e vinculados a um retorno sem adornos, eles podem simplificar e esclarecer. Esta é uma versão de io.ReadFull que os usa bem:

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
Estou curioso - algum outro idioma tem isso?
— u0b34a0f6ae

1
O Matlab tem algo semelhante.
— Dan Lorenc

pascal usa sintaxe semelhante para retornar um valor.
— nes1983

1
@ nes1983 Para quem não sabe, em Pascal você classicamente atribui o valor de retorno ao nome da função.
— fuz

FORTRAN praticamente tem isso.
— Hut8 de

14

Da resposta de James Antill :

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

Além disso, uma armadilha potencial: a diferença sutil entre os operadores de recebimento e envio:

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

3
O próprio operador de recepção agora é uma operação de bloqueio, a partir do Go 1.0.3. A especificação foi modificada: golang.org/ref/spec#Receive_operator . Tente o comportamento de bloqueio (deadlock) aqui: 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
Eu sugeriria remover o nome do site de solução de problemas, bem como o número de identificação. Talvez reformule a pergunta. Para não estragar o problema para quem está tropeçando nele. Ou tentando trapacear procurando o problema na net.
— Mizipzor

1
Para registro: este é o algoritmo de algorithmist.com/index.php/Coin_Change. É o primeiro resultado do Google para "troca de moeda".
— György Andrasek

13

Gosto que você possa redefinir tipos, incluindo primitivos como int, quantas vezes quiser e anexar métodos diferentes. Como definir um 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))
}

Que imprime

Number:  5 five V

A RomanNumber()chamada é essencialmente uma conversão, ela redefine o tipo int como um tipo mais específico de int. E Println()ligações String()nos bastidores.


12

Retornando um canal

Este é um verdadeiro idioma muito importante: como inserir dados em um canal e fechá-lo depois. Com isso, você pode fazer iteradores simples (já que o intervalo aceitará um canal) ou filtros.

// 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. Além disso, você pode passar canais por canais também.
— György Andrasek

5
Mas tome cuidado para não quebrar um loop for x: = range chan {}, você vazará a goroutine e toda a memória a que ela faz referência.
— Jeff Allen

3
@JeffAllen que tal defer close(outch);como a primeira declaração do goroutine?

1
Adiar enfileira uma instrução para execução quando a função retornar, não importa qual ponto de retorno seja obtido. Mas se a entrada do canal nunca for fechada, a função anônima neste exemplo nunca sairá do loop for.
— Jeff Allen

11

Tempo limite para leituras de canal:

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

Roubado de Davies Liu .


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

Como o alcance verifica automaticamente se há um canal fechado, podemos encurtar para isso:

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

9

Existe um make system configurado que você pode usar em $ GOROOT / src

Configure seu makefile com

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

Você pode então usar as ferramentas de teste automatizadas executando make test ou adicionar o pacote e objetos compartilhados de cgo ao seu $ GOROOT com make install.


7

Outra coisa interessante no Go é isso godoc. Você pode executá-lo como um servidor web em seu computador usando

godoc -http=:8080

onde 8080 é o número da porta e todo o site em golang.org está disponível em localhost:8080.


Este é um programa normal ou um daemon?
— György Andrasek de

É um programa regular.
— Jeremy

7

Esta é uma implementação de uma pilha. Ele ilustra a adição de métodos em um tipo.

Eu queria transformar a pilha em uma fatia e usar as propriedades da fatia, mas embora tenha feito isso funcionar sem o type, não consegui ver a sintaxe para definir uma fatia com um 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
Em vez de usar fmt.Printf(...); os.Exit();, você pode usar panic(...).
— notnoop

1
Isso fornece um rastreamento de pilha, o que eu não quero.

3
Por que é limitado? Go é uma linguagem gerida, gc'd. Sua pilha pode ser tão grande quanto você quiser. Use o novo append () embutido, que fará algo como o realloc do C quando for necessário.
— Jeff Allen

“Go não precisa de genéricos”, disseram.
— cubuspl42

4

Chamando o código c de início

É possível acessar o nível inferior de go usando o tempo de execução c.

As funções C estão na forma

void package·function(...)

(note que o separador de pontos é um caractere Unicode) onde os argumentos podem ser tipos básicos de go, fatias, strings etc. Para retornar uma chamada de valor

FLUSH(&ret)

(você pode retornar mais de um valor)

Por exemplo, para criar uma função

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

em C você usa

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

Observe que você ainda deve declarar a função em um arquivo go e que terá que cuidar da memória sozinho. Não tenho certeza se é possível chamar bibliotecas externas usando isso, talvez seja melhor usar cgo.

Veja $ GOROOT / src / pkg / runtime para exemplos usados ​​em runtime.

Veja também esta resposta para vincular o código c ++ a go.


3
Ele usa o "ponto voador", realmente? Não me atrevo a editar, mas isso parece um pouco inesperado e radical.
— relaxe

Sim, você precisa compilar com 6c (ou 8c, etc). Eu não acho que o gcc lida com identificadores Unicode.
— Scott Wales

1
Acho que AltGr + tipos de período são iguais, mas com Unicode não tenho certeza. Fiquei muito surpreso ao ver que li na fonte .. por que não usar algo como ::?
— u0b34a0f6ae

O personagem é MIDDLE DOT U + 00B7. O analisador pode ter sido falsificado para que ele veja isso como um caractere a fim de fazer um identificador c válido, o que eu acredito que impediria ::.
— Scott Wales

4
O '·' é apenas um hack temporário, o roubo até ficou surpreso por ele ainda estar lá, ele disse que seria substituído por algo menos idiossincrático.
— uriel


3

Você assistiu a essa palestra ? Mostra muitas coisas legais que você pode fazer (fim da palestra)


2
Sim eu fiz. Tudo se resume a "Há muito mais aí, vamos passar para o próximo tópico."
— György Andrasek

Sim, aparentemente muito a dizer em pouco tempo

3

Uma pilha baseada na outra resposta, mas usando acréscimo de fatia para não ter limite de tamanho.

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 */ }basta.
— u0b34a0f6ae

2
Claro. Isso é exatamente o que está acontecendo aqui. Eu apenas gosto da foreverpalavra - chave. Até o Qt tem uma macro para isso.
— György Andrasek

6
mas Go não precisa de uma macro ou um apelido fofo de true para fazer isso.
— u0b34a0f6ae

@ kaizer.se: O ponto de Jurily é que for ever(depois de declarar a variável) é algo bonito que você pode fazer no Go, se quiser. Parece inglês (modulo the blank).
— Frank de

8
é algo fofo que você também pode fazer em C .. :-)#define ever (;;)
— u0b34a0f6ae

2

Existem vários pequenos programas no testdiretório principal. Exemplos:

  • peano.go imprime fatoriais.
  • hilbert.go tem alguma multiplicação de matriz.
  • iota.go tem exemplos da coisa estranha do iota.
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.