go - Ve ejemplos y modismos
(25)
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 parece bastante torpe todavía, con las dos cadenas y dos búferes, así que si algún experto de Go tiene algún consejo, házmelo saber.
No hay un montón de código Go para aprender el idioma, y estoy seguro de que no soy el único que está experimentando con él. Por lo tanto, si descubrió algo interesante sobre el idioma, publique un ejemplo aquí.
También estoy buscando
- formas idiomáticas de hacer cosas en Go,
- Estilo de pensamiento C / C ++ "portado" a Go,
- errores comunes sobre la sintaxis,
- algo interesante, realmente.
Devolviendo un canal
Este es un verdadero modismo que es bastante importante: cómo alimentar datos en un canal y cerrarlos después. Con esto puedes 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;
}
¿Viste esta charla ? Muestra muchas cosas interesantes que puedes hacer (final de la charla)
Al importar paquetes, puede redefinir el nombre a cualquier cosa que desee:
package main
import f "fmt"
func main()
{
f.Printf("Hello World/n");
}
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 (!)
Aquí hay un ejemplo de ir usando el paquete sqlite3.
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 cambia a true cuando no se da 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 del interruptor me parece un poco más limpia.
De la respuesta de James Antill :
foo := <-ch; // This blocks.
foo, ok := <-ch; // This returns immediately.
Además, una posible trampa: la sutil diferencia entre los operadores de recepción y envío:
a <- ch; // sends ch to channel a
<-ch; // reads from channel ch
Esta es una implementación de una pila. Ilustra cómo agregar métodos a un tipo.
Quería hacer que la pila formara parte de ella en una división y usar las propiedades de la división, pero aunque conseguí que funcionara sin el type
, no pude ver la sintaxis para definir una división con 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 ();
}
Hay muchos programas pequeños en test
en el directorio principal. Ejemplos:
-
peano.go
imprime factoriales. -
hilbert.go
tiene alguna multiplicación de matriz. -
iota.go
tiene ejemplos de lo extraño de iota.
Hay un sistema make configurado que puede usar en $ GOROOT / src
Configura tu 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
A continuación, puede usar las herramientas de prueba automatizadas ejecutando make test o agregando el paquete y los objetos compartidos de cgo a su $ GOROOT con make install.
He visto a un par de personas quejándose sobre el ciclo for, en la línea de "¿por qué deberíamos decir i = 0; i < len; i++
en este día y edad?".
No estoy de acuerdo, me gusta el constructo. Puedes usar la versión larga si lo deseas, pero el idiomático Go es
var a = []int{1,2,3};
for i, v := range a {
fmt.Println(i, v);
}
La construcción del for .. range
recorre todos los elementos y proporciona dos valores: el índice i
el valor v
.
range
también funciona en mapas y canales.
Aún así, si no le gusta en cualquier forma, puede definir each
, map
, etc. 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
Estoy empezando a gustar Ir mucho :)
Los archivos de objetos Go incluyen un encabezado de texto claro:
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>
Me gusta que pueda redefinir tipos, incluidas primitivas como int, tantas veces como desee y adjuntar diferentes métodos. Como definir un tipo RomanNumeral:
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, " ", 0)
var aRoman = strings.Split(numRoman, " ", 0)
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 llamada RomanNumber()
es esencialmente un elenco, redefine el tipo int como un tipo más específico de int. Y Println()
llama a String()
detrás de las escenas.
Otra cosa interesante en Go es ese godoc
. Puede ejecutarlo como un servidor web en su computadora utilizando
godoc -http=:8080
donde 8080 es el número de puerto, y todo el sitio web en golang.org está disponible en localhost:8080
.
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
Tiempo de espera para lecturas de canales:
ticker := time.NewTicker(ns);
select {
case v := <- chan_target:
do_something_with_v;
case <- ticker.C:
handle_timeout;
}
Robado de Davies Liu .
Una pila basada en la otra respuesta, pero que usa una división 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()
}
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");
}
Declaraciones diferidas
Una instrucción "diferir" invoca una función cuya ejecución se aplaza hasta el momento en que la función circundante retorna.
DeferStmt = "diferir" Expresión.
La expresión debe ser una llamada a función o método. Cada vez que se ejecuta la instrucción "diferir", 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 de retorno, 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:
defer
es ahora también la forma idiomática de manejar el panic
de una manera exception-like :
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)
}
Llamando al código c de go
Es posible acceder al nivel inferior de go mediante el uso del tiempo de ejecución c.
Las funciones C están en la forma
void package·function(...)
(tenga en cuenta que el separador de puntos es un carácter unicode) donde los argumentos pueden ser básicos, vaya a tipos, sectores, cadenas, etc. Para devolver una llamada de valor
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 todavía debe declarar la función en un archivo go, y que usted mismo tendrá que ocuparse de la memoria. No estoy seguro si es posible llamar a bibliotecas externas usando esto, puede ser mejor usar cgo.
Consulte $ GOROOT / src / pkg / runtime para ver ejemplos usados en el tiempo de ejecución.
Consulte también esta respuesta para vincular el código de C ++ con go.
Parámetros de resultados nombrados
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 instrucción return 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 breve y claro: son documentación. Si nombramos los resultados de nextInt, resulta obvio cuál devuelto int es cuál.
func nextInt(b []byte, pos int) (value, nextPos int) {
Debido a que los resultados nombrados se inicializan y se vinculan a un retorno 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;
}
/*
* 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))
}
const ever = true;
for ever {
//infinite loop
}
for {
v := <-ch;
if closed(ch) { break }
fmt.Println(v)
}
Dado que range comprueba automáticamente si hay un canal cerrado, podemos acortarlo a esto:
for v := range ch {
fmt.Println(v)
}