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

jg-faustus
fonte
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.

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

u0b34a0f6ae
fonte
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.

Rob Russell
fonte
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");
}
György Andrasek
fonte
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;
}
György Andrasek
fonte
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
György Andrasek
fonte
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))
}
György Andrasek
fonte
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.

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

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

Scott Wales
fonte
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.

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

Scott Wales
fonte
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)

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

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