Ir a ejemplos y modismos [cerrado]

91

No hay mucho código Go para aprender el idioma, y ​​estoy seguro de que no soy el único que está experimentando con él. Entonces, si descubrió algo interesante sobre el idioma, publique un ejemplo aquí.

Yo tambien estoy buscando

  • formas idiomáticas de hacer las cosas en Go,
  • Estilo de pensamiento C / C ++ "adaptado" a Go,
  • errores comunes sobre la sintaxis,
  • algo interesante, de verdad.
György Andrasek
fuente
Soporte ARM como 8 bits o 16 bits. El lenguaje D todavía no lo hace.
1
La biblioteca ( golang.org/pkg ) es una fuente excelente para aprender cómo se usa go. Personalmente, encuentro que aprender cómo se implementan las estructuras de datos es útil para aprender el idioma.
tkokasih

Respuestas:

35

Aplazar declaraciones

Una instrucción "diferir" invoca una función cuya ejecución se aplaza hasta el momento en que regresa la función circundante.

DeferStmt = "aplazar" Expresión.

La expresión debe ser una función o una llamada a un método. Cada vez que se ejecuta la instrucción "aplazar", los parámetros de la llamada a la función se evalúan y se guardan de nuevo, pero la función no se invoca. Las llamadas a funciones diferidas se ejecutan en orden LIFO inmediatamente antes de que regrese la función circundante, pero después de que se hayan evaluado los valores devueltos, si los hay.


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

Actualizar:

deferahora también es la forma idiomática de manejar panicde una manera similar a una excepción :

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)
}
rev.
fuente
17
Parece el viejo RAII (hecho explícito).
Konrad Rudolph
4
+1 ya que leí mucho sobre Go, ¡pero todavía no vi esto (hasta que me lo mostraste)!
u0b34a0f6ae
Inteligente, aunque tendría más sentido para mí si las declaraciones diferidas se ejecutaran en orden FIFO (de arriba a abajo), pero tal vez sea solo yo ...
Mike Spross
Frio. Me recuerda a scrop guards de D digitalmars.com/d/2.0/exception-safe.html
hasen
4
@Mike: si lo comparas con bloques de "prueba: .. finalmente:" LIFO anida de la misma manera. Para los pares de recursos abiertos / cerrados, etc., anidar como este es lo único que tiene sentido (la primera apertura se cerrará al final).
u0b34a0f6ae
25

Los archivos de objetos Go en realidad incluyen un encabezado de texto sin formato:

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>
rev.
fuente
6
Eso es más una característica oculta que un ejemplo idiomático
hasen
22

He visto a un par de personas quejarse del bucle for, en la línea de "¿por qué deberíamos tener que decir i = 0; i < len; i++en estos tiempos?".

No estoy de acuerdo, me gusta la construcción. Puede usar la versión larga si lo desea, pero la idiomática Go es

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

La for .. rangeconstrucción recorre todos los elementos y proporciona dos valores: el índice iy el valor v.

range también funciona en mapas y canales.

Aún así, si no le gusta forde alguna forma, puede definir each, mapetc.en unas pocas líneas:

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
}

huellas dactilares

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

Me está empezando a gustar mucho Go :)

jg-faustus
fuente
Aunque rangesolo es bueno si se compila con el mismo código que el bucle for-3.
Thomas Ahle
19

Ve y obtén tu reputación de stackoverflow

Esta es una traducción de esta respuesta .

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

Gracias a Scott Wales por su ayuda con .Read ().

Esto todavía parece bastante torpe, con las dos cadenas y dos búferes, así que si algún experto en Go tiene un consejo, hágamelo saber.

usuario181548
fuente
No estoy seguro de qué se suponía que estaba mal con el formato; Lo he restaurado.
5
Los autores de Go recomiendan gofmtsu código :-)
ℝaphink
No puedo compilarlo: $ ../go/src/cmd/6g/6g SO.go SO.go: 34: undefined: json.StringToJson
ℝaphink
@Raphink: el idioma ha cambiado desde que hice esto.
Sí, ¿sabes cuál es el equivalente más cercano al StringToJson? Solía ​​configurar un constructor internamente, ¿ahora uno tiene que proporcionar el suyo con una estructura nativa predefinida?
macbirdie
19

Aquí hay un buen ejemplo de iota de la publicación 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
fuente
5
Tenga en cuenta que los puntos y comas son innecesarios.
mk12
18

Puede intercambiar variables por asignación paralela:

x, y = y, x

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

simple pero efectivo.

u0b34a0f6ae
fuente
18

Aquí hay un modismo de la 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

La instrucción switch se activa cuando no se proporciona ninguna expresión. Entonces esto es 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

Por el momento, la versión conmutada me parece un poco más limpia.

Rob Russell
fuente
6
Whoa, completamente arrancado de VB. ;-) ( Switch True…)
Konrad Rudolph
@Konrad, ¡adelante! :) He usado ese idioma en el código VB6 antes y definitivamente puede ayudar a la legibilidad en ciertas situaciones.
Mike Spross
¿Qué es '<='? ¿Está relacionado con '<-'?
ℝaphink
@Raphink: menor o 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
fuente
14

Parámetros de resultado con nombre

Los "parámetros" de retorno o resultado de una función Go pueden recibir nombres y usarse como variables regulares, al igual que los parámetros entrantes. Cuando se nombran, se inicializan a los valores cero para sus tipos cuando comienza la función; si la función ejecuta una declaración de retorno sin argumentos, los valores actuales de los parámetros de resultado se utilizan como valores devueltos.

Los nombres no son obligatorios pero pueden hacer que el código sea más corto y claro: son documentación. Si nombramos los resultados de nextInt, resulta obvio qué int devuelto es cuál.

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

Debido a que los resultados con nombre se inicializan y vinculan a una devolución sin adornos, pueden simplificar y aclarar. Aquí hay una versión de io.ReadFull que los usa 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
fuente
1
Tengo curiosidad, ¿algún otro idioma tiene esto?
u0b34a0f6ae
1
Matlab tiene algo similar.
Dan Lorenc
pascal usa una sintaxis similar para devolver un valor.
nes1983
1
@ nes1983 Para aquellos que no lo saben, en Pascal se asigna clásicamente el valor de retorno al nombre de la función.
fuz
FORTRAN prácticamente tiene esto.
Hut8
14

De la respuesta de James Antill :

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

Además, un error potencial: la sutil diferencia entre los operadores de envío y recepción:

a <- ch // sends ch to channel a
<-ch    // reads from channel ch
György Andrasek
fuente
3
El operador de recepción en sí es ahora una operación de bloqueo, a partir de Go 1.0.3. La especificación ha sido modificada: golang.org/ref/spec#Receive_operator . Pruebe el comportamiento de bloqueo (punto muerto) aquí: 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))
}
rev.
fuente
4
Sugeriría eliminar el nombre del sitio de resolución de problemas, así como el número de identificación. Quizás reformule la pregunta. Para no estropear el problema a alguien que se tropieza con él. O tratando de hacer trampa buscando el problema en la red.
Mizipzor
1
Para el registro: este es el algoritmo de algoritmist.com/index.php/Coin_Change Es el primer resultado de Google para "cambio de moneda".
György Andrasek
13

Me gusta que puedas redefinir tipos, incluidas primitivas como int, tantas veces como quieras y adjuntar diferentes métodos. Como definir un 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

La RomanNumber()llamada es esencialmente un molde, redefine el tipo int como un tipo más específico de int. Y Println()llama String()entre bastidores.

jg-faustus
fuente
12

Devolviendo un canal

Este es un verdadero idioma que es bastante importante: cómo introducir datos en un canal y cerrarlo después. Con esto puede hacer iteradores simples (ya que el rango aceptará un canal) o 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
fuente
+1. Además, también puede pasar canales a través de canales.
György Andrasek
5
Pero tenga cuidado de no salirse de un bucle for x: = range chan {}, perderá la goroutine y toda la memoria a la que hace referencia.
Jeff Allen
3
@JeffAllen ¿qué defer close(outch);tal como la primera declaración de la goroutine?
1
Aplazar pone en cola una instrucción para su ejecución cuando la función regresa, sin importar qué punto de retorno se tome. Pero si la entrada del canal nunca se cierra, entonces la función anónima en este ejemplo nunca abandonará el bucle for.
Jeff Allen
11

Tiempo de espera para las lecturas del canal:

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

Robado de Davies Liu .

György Andrasek
fuente
11
for {
    v := <-ch
    if closed(ch) {
        break
    }
    fmt.Println(v)
}

Dado que el rango busca automáticamente un canal cerrado, podemos acortarlo a esto:

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

Hay una configuración de sistema de creación que puede usar en $ GOROOT / src

Configure su archivo MAKE con

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

Luego, puede usar las herramientas de prueba automatizadas ejecutando make test, o agregar el paquete y los objetos compartidos de cgo a su $ GOROOT con make install.

Scott Gales
fuente
7

Otra cosa interesante en Go es eso godoc. Puede ejecutarlo como servidor web en su computadora usando

godoc -http=:8080

donde 8080 es el número de puerto, y el sitio web completo en golang.org está disponible en localhost:8080.

user181548
fuente
¿Es este un programa regular o un demonio?
György Andrasek
Es un programa regular.
Black Lives Matter
7

Esta es una implementación de una pila. Ilustra la adición de métodos a un tipo.

Quería convertir la pila en parte de ella en un segmento y usar las propiedades del segmento, pero aunque logré que funcionara sin el type, no pude ver la sintaxis para definir un segmento con 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()
}
usuario 181548
fuente
10
En lugar de usar fmt.Printf(...); os.Exit();, puede usar panic(...).
notnoop
1
Eso da un rastro de pila, que no quiero.
3
¿Por qué es limitado? Go es un idioma controlado por GC. Tu pila puede ser tan profunda como quieras. Use el nuevo append () incorporado, que hará algo como la reasignación de C cuando sea necesario.
Jeff Allen
"Go no necesita genéricos", dijeron.
cubuspl42
4

Llamar al código c desde ir

Es posible acceder al nivel inferior de go utilizando el tiempo de ejecución de c.

Las funciones C tienen la forma

void package·function(...)

(tenga en cuenta que el separador de puntos es un carácter Unicode) donde los argumentos pueden ser tipos básicos, cortes, cadenas, etc. Para devolver un valor, llame

FLUSH(&ret)

(puede devolver más de un valor)

Por ejemplo, para crear una función

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

en C usas

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

Tenga en cuenta que aún debe declarar la función en un archivo go y que tendrá que ocuparse de la memoria usted mismo. No estoy seguro de si es posible llamar a bibliotecas externas usando esto, puede ser mejor usar cgo.

Mire $ GOROOT / src / pkg / runtime para ver ejemplos usados ​​en el tiempo de ejecución.

Consulte también esta respuesta para vincular el código c ++ con go.

Scott Wales
fuente
3
¿Realmente usa el "punto volador"? No me atrevo a editar, pero eso parece un poco inesperado y radical.
relajarse el
Sí, necesita compilar con 6c (o 8c, etc.). No creo que gcc maneje identificadores Unicode.
Scott Wales
1
Creo que los tipos de período AltGr + son iguales · pero con Unicode no estoy seguro. Me sorprendió mucho ver que en la fuente que leí ... ¿por qué no usar algo como ::?
u0b34a0f6ae
El carácter es MIDDLE DOT U + 00B7. El analizador puede haber sido manipulado para que lo vea como un carácter para hacer un identificador c válido, lo que creo que excluiría ::.
Scott Wales
4
El '·' es solo un truco temporal, Rob incluso se sorprendió de que todavía estuviera allí, dijo que iba a ser reemplazado por algo menos idiosincrásico.
uriel
3

¿Viste esta charla ? Muestra muchas cosas interesantes que puedes hacer (fin de la charla)

user180100
fuente
2
Sí, lo hice. Todo se reduce a "Hay mucho más ahí, pasemos al siguiente tema".
György Andrasek
Sí, aparentemente hay mucho que decir en poco tiempo
3

Una pila basada en la otra respuesta, pero usando un segmento que se agrega para no tener límite de tamaño.

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
fuente
3
const ever = true

for ever {
    // infinite loop
}
György Andrasek
fuente
25
Ejem. for { /* infinite loop */ }es suficiente.
u0b34a0f6ae
2
Por supuesto. Eso es exactamente lo que está pasando aquí. Simplemente me gusta la foreverpalabra clave. Incluso Qt tiene una macro para eso.
György Andrasek
6
pero Go no necesita una macro o un lindo alias de verdadero para hacer esto.
u0b34a0f6ae
@ kaizer.se: El punto de Jurily es que for ever(después de declarar la variable) es algo lindo que puedes hacer en Go si quieres. Parece inglés (módulo en blanco).
Frank
8
es algo lindo que también puedes hacer en C .. :-)#define ever (;;)
u0b34a0f6ae
2

Hay muchos programas pequeños en test en el directorio principal. Ejemplos:

  • peano.go imprime factoriales.
  • hilbert.go tiene algo de multiplicación de matrices.
  • iota.go tiene ejemplos de lo extraño de iota.
user181548
fuente