Hola, 世界

Bienvenido al tour por el lenguaje de programación Go.

El tour está dividido en tres secciones: Conceptos básicos, métodos e intefaces, y concurrencia.

A lo largo del tour hay una serie de ejercicios para que los completes.

El tour es interactivo. Haz click en Ejecutar (o presiona Shift-Enter) para compilar y ejecutar el programa en un servidor remoto.

El resultado se mostrará debajo del código.

Estos programas de ejemplo muestran los diferentes aspectos de Go. Se supone que los programas del tour han de ser los puntos de partida de tus propios experimentos.

Modifica el programa y ejecútalo de nuevo.

Cuando estés listo para continuar, haz click en Siguiente o presiona la tecla PageDown.

package main

import "fmt"

func main() {
    fmt.Println("Hola, 世界")
}

Go en otros idiomas

El tour está disponible en otros idiomas:

(Si quieres traducir el tour a otro idioma, descarga el código de https://code.google.com/p/go-tour, traduce tour.article y lánzalo a producción en App Engine.)

Haz click en "siguiente" o pulsa PageDown para continuar.

Go offline

Este tour está disponible también como un programa independiente que puedes usar sin acceder a internet.

Este programa es rápido, ya que compila y ejecuta el código de los ejemplos en tu propio ordenador. También incluye ejercicos adicionales que no se encuentran disponibles en la versión online.

Para ejecutar el tour localmente primero debes instalar Go, a continuación go get para instalar gotour (en inglés):

go get code.google.com/p/go-tour/gotour

o si lo prefieres en castellano:

go get github.com/rcostu/go-tour-es/gotoures

y ejecuta el fichero gotour (o gotoures en castellano).

De lo contrario, haz click en "siguiente" o presiona la tecla PageDown para continuar.

(Puedes volver a estas instrucciones en cualquier momento haciendo click en "índice".)

Paquetes

Todo programa en Go contiene paquetes.

Los programas comienzan su ejecución en el paquete main.

Este programa usa los paquetes con rutas de importación "fmt" y "math".

Por convención, el nombre del paquete es el mismo que el último elemento de la ruta de importación.

package main

import (
    "fmt"
    "math"
)

func main() {
    fmt.Println("Feliz día", math.Pi)
}

Importación

Éste código agrupa las importaciones entre paréntesis de forma "factorizada". Tambien puedes realizar multiples importaciones de la siguiente forma:

import "fmt"
import "math"

pero es común usar la forma factorizada para eliminar código innecesario.

package main

import (
    "fmt"
    "math"
)

func main() {
    fmt.Printf("Ahora tienes %g problemas.",
        math.Nextafter(2, 3))
}

Identificadores exportados

Tras importar un paquete, puedes hacer referencia a los identificadores que exporta.

En Go, un identificador es exportado si empieza por una mayúscula.

Foo es un identificador exportado, al igual que FOO. El identificador foo no es exportado.

Ejecuta el código. Después sustituye math.pi por math.Pi e intentalo de nuevo.

package main

import (
    "fmt"
    "math"
)

func main() {
    fmt.Println(math.pi)
}

Funciones

Una función puede tener cero o más argumentos.

En este ejemplo, add posee dos parámetros de tipo int.

Observa que el tipo de indica después del nombre de la variable.

(Para más información acerca de por qué los tipos se miestran así, échale un vistazo a ésta entrada en inglés.)

package main

import "fmt"

func add(x int, y int) int {
    return x + y
}

func main() {
    fmt.Println(add(42, 13))
}

Funciones (continuación)

Cuando dos o más parámetros consecutivos de la función son del mismo tipo, puedes omitir el tipo de todos menos del último.

En el ejemplo, acortamos

x int, y int

a

x, y int
package main

import "fmt"

func add(x, y int) int {
    return x + y
}

func main() {
    fmt.Println(add(42, 13))
}

Múltiples resultados

Una función puede devolver varios resultados.

Esta función devuelve dos cadenas.

package main

import "fmt"

func swap(x, y string) (string, string) {
    return y, x
}

func main() {
    a, b := swap("hola", "mundo")
    fmt.Println(a, b)
}

Resultados nombrados

Las funciones tienen parametros; en Go los resultados pueden ser nombrados y actuar como variables; se les denomina "variables de retorno"

Si las variables de retorno tienen un nombre, una sentencia return sin argumentos devuelve el valor actual de dichas variables.

package main

import "fmt"

func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return
}

func main() {
    fmt.Println(split(17))
}

Variables

La sentencia var declara una lista de variables; como en la lista de argumentos de las funciones, el tipo se indica al final.

package main

import "fmt"

var x, y, z int
var c, python, java bool

func main() {
    fmt.Println(x, y, z, c, python, java)
}

Variables inicializadas

La declaracion de variables permite inicializaciones, una por variable.

Si se inicializa una variable, el tipo puede omitirse; la variable adoptará el tipo del valor con el que ha sido inicializada.

package main

import "fmt"

var x, y, z int = 1, 2, 3
var c, python, java = true, false, "¡no!"

func main() {
    fmt.Println(x, y, z, c, python, java)
}

Declaración implícita de Variables

Dentro de una función, puede utilizarse la sentencia de asignación := en lugar de la declaración var.

(Fuera de una función, todas las declaraciones de variables comienzan con la palabra clave var y el operando := no está disponible.)

package main

import "fmt"

func main() {
    var x, y, z int = 1, 2, 3
    c, python, java := true, false, "¡no!"

    fmt.Println(x, y, z, c, python, java)
}

Tipos básicos

Los tipos básicos en Go son:

bool

string

int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr

byte //alias para uint8

rune // alias para int32
     // Representa un punto Unicode

float32 float64

complex64 complex128
package main

import (
    "fmt"
    "math/cmplx"
)

var (
    ToBe   bool       = false
    MaxInt uint64     = 1<<64 - 1
    z      complex128 = cmplx.Sqrt(-5 + 12i)
)

func main() {
    const f = "%T(%v)\n"
    fmt.Printf(f, ToBe, ToBe)
    fmt.Printf(f, MaxInt, MaxInt)
    fmt.Printf(f, z, z)
}

Constantes

Las constantes se declaran como las variables, pero con la palabra reservada const.

Las constantes pueden ser cadenas, booleanas, o numéricas.

package main

import "fmt"

const Pi = 3.14

func main() {
    const World = "世界"
    fmt.Println("Hola", World)
    fmt.Println("Feliz día de", Pi)

    const Truth = true
    fmt.Println("¿Go mola?", Truth)
}

Constantes Numéricas

Las constantes numéricas son valores de alta precisión.

Una constante sin un tipo definido tiene el tipo necesitado según el contexto en el que se declara.

Intenta también imprimir el valor needInt(Big).

package main

import "fmt"

const (
    Big   = 1 << 100
    Small = Big >> 99
)

func needInt(x int) int { return x*10 + 1 }
func needFloat(x float64) float64 {
    return x * 0.1
}

func main() {
    fmt.Println(needInt(Small))
    fmt.Println(needFloat(Small))
    fmt.Println(needFloat(Big))
}

For

Go tiene sólo un operando para definir los bucles, los bucles for.

El bucle for básico es muy parecido al que se utiliza en C o Java, salvo que los ( ) desaparecen (ni siquiera son opcionales) y las llaves { } son obligatorias.

package main

import "fmt"

func main() {
    sum := 0
    for i := 0; i < 10; i++ {
        sum += i
    }
    fmt.Println(sum)
}

For (continuación)

Como en C o Java, puedes dejar las instrucciones de inicialización e incremento vacías.

package main

import "fmt"

func main() {
    sum := 1
    for ;sum < 1000; {
        sum += sum
    }
    fmt.Println(sum)
}

For es el "while" de Go

En este paso, puedes eliminar los puntos y coma ;: Un bucle while de C se transforma en un bucle for en Go.

package main

import "fmt"

func main() {
    sum := 1
    for sum < 1000 {
        sum += sum
    }
    fmt.Println(sum)
}

Eterno

Si omites la condición del bucle, es un bucle infinito de manera que un bucle infinito se escribe de manera compacta.

package main

func main() {
    for {
    }
}

If

La instrucción if es similar a la sentencia en C o Java, salvo que los paréntesis ( ) desaparecen (ni siquiera son opcionales) y las llaves { } son obligatorias.

(¿Te suena de algo?)

package main

import (
    "fmt"
    "math"
)

func sqrt(x float64) string {
    if x < 0 {
        return sqrt(-x) + "i"
    }
    return fmt.Sprint(math.Sqrt(x))
}

func main() {
    fmt.Println(sqrt(2), sqrt(-4))
}

If con instrucción inicial

Al igual que en la sentencia for, la sentencia if puede empezar con una instrucción de inicialización que se ejecutará antes de evaluar la condición.

Las variables declaradas por la instrucción de inicialización son únicamente visibles en el ámbito del if.

(Intenta usar v en la última sentencia return.)

package main

import (
    "fmt"
    "math"
)

func pow(x, n, lim float64) float64 {
    if v := math.Pow(x, n); v < lim {
        return v
    }
    return lim
}

func main() {
    fmt.Println(
        pow(3, 2, 10),
        pow(3, 3, 20),
    )
}

If y else

Las variables declaradas dentro de la instrucción de inicialización de un if son también visibles dentro de los bloques else.

package main

import (
    "fmt"
    "math"
)

func pow(x, n, lim float64) float64 {
    if v := math.Pow(x, n); v < lim {
        return v
    } else {
        fmt.Printf("%g >= %g\n", v, lim)
    }
    // No se puede usar v aquí
    return lim
}

func main() {
    fmt.Println(
        pow(3, 2, 10),
        pow(3, 3, 20),
    )
}

Ejercicio: Bucles y Funciones

Una forma sencilla de jugar con funciones y bucles es implementar la funcionalidad de la raíz cuadrada utilizando el método de Newton.

En este caso el método de Newton aproxima Sqrt(x) tomando un punto inicial z y repitiendo:

Para empezar, simplemente repite el cálculo 10 veces y mira cómo de cerca estás de la solución para distintos valores (1, 2, 3, ...).

Después cambia la condición del bucle para parar cuando el valor deje de cambiar (o solo cambie con un delta muy pequeño). Mira si esto ocurre con más o menos iteraciones. ¿Cómo estás de cerca comparado con math.Sqrt?

Pista: para declarar e inicializar un valor decimal dale un valor decimal o utiliza la conversión:

z := float64(0)
z := 0.0
package main

import (
    "fmt"
)

func Sqrt(x float64) float64 {
}

func main() {
    fmt.Println(Sqrt(2))
}

Estructuras y tipos

Una estructura (struct) es un registro de variables dentro de un mismo tipo.

(Y una declaración type declara un nuevo tipo de datos.)

package main

import "fmt"

type Vertex struct {
    X int
    Y int
}

func main() {
    fmt.Println(Vertex{1, 2})
}

Campos de una estructura

Los campos de una estructura son accesibles mediante el operador . (punto).

package main

import "fmt"

type Vertex struct {
    X int
    Y int
}

func main() {
    v := Vertex{1, 2}
    v.X = 4
    fmt.Println(v.X)
}

Punteros

Go posee punteros, pero no tiene aritmética de punteros (como C).

Los campos de las estructuras pueden accederse a través de un puntero a una estructura.

La indirección del puntero es transparente al programador.

package main

import "fmt"

type Vertex struct {
    X int
    Y int
}

func main() {
    p := Vertex{1, 2}
    q := &p
    q.X = 1e9
    fmt.Println(p)
}

Estructuras literales

Una estructura literal denota una nueva instancia de la estructura que muestra los valores de sus campos.

Puedes mostrar sólo un subconjunto de los campos utilizando la sintaxis Name:. (Y el orden de los campos nombrados es irrelevante.)

El prefijo especial & construye un puntero al espacio donde la nueva estructura se aloja.

package main

import "fmt"

type Vertex struct {
    X, Y int
}

var (
    p = Vertex{1, 2}  // Tiene tipo Vertex
    q = &Vertex{1, 2} // Tiene tipo *Vertex
    r = Vertex{X: 1}  // Y:0 es implicito
    s = Vertex{}      // X:0 e Y:0
)

func main() {
    fmt.Println(p, q, r, s)
}

La función `new`

La expresión new(T) aloja en memoria un valor T inicializado a 0 y retorna un puntero al mismo.

var t *T = new(T)

o

t := new(T)
package main

import "fmt"

type Vertex struct {
    X, Y int
}

func main() {
    v := new(Vertex)
    fmt.Println(v)
    v.X, v.Y = 11, 9
    fmt.Println(v)
}

Slices

Un slice apunta a un array de valores y posee un tamaño.

[]T es un slice con elementos de tipo T.

package main

import "fmt"

func main() {
    p := []int{2, 3, 5, 7, 11, 13}
    fmt.Println("p ==", p)

    for i := 0; i < len(p); i++ {
        fmt.Printf("p[%d] == %d\n",
            i, p[i])
    }
}

Slicing slices

Los slices pueden ser reasignados, creando un nuevo slice que apunte al mismo array.

La expresión

s[lo:hi]

es evaluada como un slice de elementos desde el elemento de índice lo hasta el elemento hi-1 inclusive. Por tanto

s[lo:lo]

es un slice vacío y

s[lo:lo+1]

tiene un elemento.

package main

import "fmt"

func main() {
    p := []int{2, 3, 5, 7, 11, 13}
    fmt.Println("p ==", p)
    fmt.Println("p[1:4] ==", p[1:4])

    // Un valor de inicio omitido implica 0
    fmt.Println("p[:3] ==", p[:3])

    // Un valor de fin omitido implica len(s)
    fmt.Println("p[4:] ==", p[4:])
}

Creando slices

Los slices son creados con la función make. Funciona alojando un array inicializado a 0 y retornando un slice que apunta a ese array:

a := make([]int, 5)  // len(a)=5

Los slices tienen un tamaño y una capacidad. La capacidad de un slice es el tamaño máximo que el slice puede crecer dentro del array al que apunta.

Para especificar una capacidad basta con pasar un tercer argumento a make:

b := make([]int, 0, 5)
// len(b)=0, cap(b)=5

Los slices pueden crecer reasignándose (por encima de su capacidad):

b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:]      // len(b)=4, cap(b)=4
package main

import "fmt"

func main() {
    a := make([]int, 5)
    printSlice("a", a)
    b := make([]int, 0, 5)
    printSlice("b", b)
    c := b[:2]
    printSlice("c", c)
    d := c[2:5]
    printSlice("d", d)
}

func printSlice(s string, x []int) {
    fmt.Printf("%s len=%d cap=%d %v\n",
        s, len(x), cap(x), x)
}

Slices nil

El valor por defecto de un slice es nil.

Un slice nil tiene un tamaño y una longitud de 0.

(Para más detalle por favor mira el artículo (en inglés) "Go Slices: usage and internals".)

package main

import "fmt"

func main() {
    var z []int
    fmt.Println(z, len(z), cap(z))
    if z == nil {
        fmt.Println("¡nulo!")
    }
}

Range

La forma range de un bucle for itera sobre elementos de un slice o un map.

package main

import "fmt"

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}

func main() {
    for i, v := range pow {
        fmt.Printf("2**%d = %d\n", i, v)
    }
}

Range (continuación)

Puedes obviar la clave o el valor asignándoselo a _.

Si sólo quieres el índice, descarta el , value integramente.

package main

import "fmt"

func main() {
    pow := make([]int, 10)
    for i := range pow {
        pow[i] = 1 << uint(i)
    }
    for _, value := range pow {
        fmt.Printf("%d\n", value)
    }
}

Ejercicio: Slices

Implementa la función Pic. Debería devolver un slice de tamaño dy, siendo cada uno de los elementos un slice de dx enteros sin signo de 8 bits. Cuando ejecutes el programa, mostrará tu dibujo, interpretando los números enteros como una escala de grises (bueno, escala de azules).

La elección de la imagen es de tu elección. Algunas funciones interesantes pueden ser x^y, (x+y)/2, y x*y.

(Necesitas usar un bucle para reservar memoria para cada []uint8 dentro de la matriz [][]uint8.)

(Usa uint8(intValue) para covertir entre tipos.)

package main

import "code.google.com/p/go-tour/pic"

func Pic(dx, dy int) [][]uint8 {
}

func main() {
    pic.Show(Pic)
}

Maps

Un map relaciona claves y valores.

Los Maps deben crearse con la función make (nunca con new) antes de su uso; el map nil está vacío y no puede ser asignado.

package main

import "fmt"

type Vertex struct {
    Lat, Long float64
}

var m map[string]Vertex

func main() {
    m = make(map[string]Vertex)
    m["Bell Labs"] = Vertex{
        40.68433, -74.39967,
    }
    fmt.Println(m["Bell Labs"])
}

Map literales

Los map literales son como las estructuras literales, pero las claves son obligatorias.

package main

import "fmt"

type Vertex struct {
    Lat, Long float64
}

var m = map[string]Vertex{
    "Bell Labs": Vertex{
        40.68433, -74.39967,
    },
    "Google": Vertex{
        37.42202, -122.08408,
    },
}

func main() {
    fmt.Println(m)
}

Map literales (continuación)

Si el tipo superior es un nombre de un tipo, puedes omitirlo de los elementos del literal.

package main

import "fmt"

type Vertex struct {
    Lat, Long float64
}

var m = map[string]Vertex{
    "Bell Labs": {40.68433, -74.39967},
    "Google":    {37.42202, -122.08408},
}

func main() {
    fmt.Println(m)
}

Operaciones con maps

Insertar o actualizar un elemento de un map m:

m[clave] = elem

Recuperar un elemento:

elem = m[clave]

Borrar un elemento:

delete(m, clave)

Comprobar si una clave está presente con una doble assignación:

elem, ok = m[clave]

Si clave existe en m, ok es true. De otro modo ok és false y elem tiene el valor inicial del tipo de los elementos del map.

De manera similar, cuando leemos de un map, si la clave no está el resultado es el valor inicial del tipo de los elementos del map.

package main

import "fmt"

func main() {
    m := make(map[string]int)

    m["Respuesta"] = 42
    fmt.Println("Valor:", m["Respuesta"])

    m["Respuesta"] = 48
    fmt.Println("Valor:", m["Respuesta"])

    delete(m, "Respuesta")
    fmt.Println("Valor:", m["Respuesta"])

    v, ok := m["Respuesta"]
    fmt.Println("Valor:", v, "¿Existe?", ok)
}

Ejercicio: Maps

Implementa ContadorPalabras. Debería devolver un map con el número de veces que una "palabra" aparece en la cadena s. La función wc.Test ejecuta un caso de prueba ejecutando la función implementada e imprime éxito o fallo.

Puedes encontrar ayuda en strings.Fields.

package main

import (
    "code.google.com/p/go-tour/wc"
)

func ContadorPalabras(s string) map[string]int {
    return map[string]int{"x": 1}
}

func main() {
    wc.Test(ContadorPalabras)
}

Funciones son valores

Las funciones también son valores.

package main

import (
    "fmt"
    "math"
)

func main() {
    hypot := func(x, y float64) float64 {
        return math.Sqrt(x*x + y*y)
    }

    fmt.Println(hypot(3, 4))
}

Funciones son clausuras

Y las funciones son clausuras completas.

La función adder retorna una clausura (o función anónima). Cada clausura está vinculada a su variable sum correspondiente.

package main

import "fmt"

func adder() func(int) int {
    sum := 0
    return func(x int) int {
        sum += x
        return sum
    }
}

func main() {
    pos, neg := adder(), adder()
    for i := 0; i < 10; i++ {
        fmt.Println(
            pos(i),
            neg(-2*i),
        )
    }
}

Ejercicio: La clausura de Fibonacci

Vamos a divertirnos un poco con las funciones.

Implementa una función de fibonacci que devuelva una función (o clausura) que devuelva los sucesivos números de fibonacci.

package main

import "fmt"

// fibonacci es una función que devuelve
// una función que devuelve un int.
func fibonacci() func() int {
}

func main() {
    f := fibonacci()
    for i := 0; i < 10; i++ {
        fmt.Println(f())
    }
}

Switch

Probablemente ya sabes cómo iba a ser la cláusula switch.

El cuerpo de un caso sale automáticamente de la cláusula switch, a menos que termine con una sentencia fallthrough, que provocaría que siguiera ejecutando el siguiente caso contemplado.

package main

import (
    "fmt"
    "runtime"
)

func main() {
    fmt.Print("Go runs on ")
    switch os := runtime.GOOS; os {
    case "darwin":
        fmt.Println("OS X.")
    case "linux":
        fmt.Println("Linux.")
    default:
        // freebsd, openbsd,
        // plan9, windows...
        fmt.Printf("%s.", os)
    }
}

Orden de evaluación de un switch

Los casos de un Switch evaluan los casos de arriba a abajo, parando cuando se encuentra un caso satisfactorio.

(Por ejemplo,

switch i {
case 0:
case f():
}

no ejecuta f si i==0.)

Nota: El temps a Go playground sempre comença el dimarts 2009-11-10 23:00:00 UTC, un valor amb significat es deixa com a exercici per al lector.

package main

import (
    "fmt"
    "time"
)

func main() {
    fmt.Println("¿Cuándo es Sábado?")
    today := time.Now().Weekday()
    switch time.Saturday {
    case today + 0:
        fmt.Println("Hoy.")
    case today + 1:
        fmt.Println("Mañana.")
    case today + 2:
        fmt.Println("Pasado mañana.")
    default:
        fmt.Println("Demasiado tarde.")
    }
}

Switch sin condición

Un switch sin condición es lo mismo que switch true.

Esta construcción puede ser una manera clara de escribir cadenas largas if-then-else.

package main

import (
    "fmt"
    "time"
)

func main() {
    t := time.Now()
    switch {
    case t.Hour() < 12:
        fmt.Println("¡Buenos días!")
    case t.Hour() < 17:
        fmt.Println("Buenas tardes.")
    default:
        fmt.Println("Buenas noches.")
    }
}

Ejercicio avanzado: Raices cúbicas complejas

Veamos cuál es el soporte de Go para números complejos mediante los tipos complex64 y complex128. Para raíces cuadradas el método de Newton se basa en repeticiones:

Busca la raíz cúbica de 2, para asegurarte que el algoritmo funciona. Existe una función Pow en el paquete math/cmplx.

package main

import "fmt"

func Cbrt(x complex128) complex128 {
}

func main() {
    fmt.Println(Cbrt(2))
}

Métodos e interfaces

Métodos

Go no tiene clases. De todas formas, puedes definir métodos para tipos struct.

El receptor del método aparece en su propia lista de argumentos entre la palabra reservada func y el nombre del método.

package main

import (
    "fmt"
    "math"
)

type Vertex struct {
    X, Y float64
}

func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
    v := &Vertex{3, 4}
    fmt.Println(v.Abs())
}

Métodos (continuación)

De hecho, puedes definir un método para cualquier tipo que definas en tu paquete, no sólamente para estructuras.

No puedes definir un método de un tipo de otro paquete o de un tipo básico.

package main

import (
    "fmt"
    "math"
)

type MyFloat float64

func (f MyFloat) Abs() float64 {
    if f < 0 {
        return float64(-f)
    }
    return float64(f)
}

func main() {
    f := MyFloat(-math.Sqrt2)
    fmt.Println(f.Abs())
}

Métodos con punteros a receptores

Los métodos pueden asociarse con un tipo o un puntero a un tipo declarado.

Acabamos de ver dos métodos Abs. Uno para el puntero a un vértice`*Vertex` y otro para el tipo MyFloat.

Hay dos razones para usar un receptor de tipo puntero. Primero, para evitar copiar el valor en cada llamada al método (más eficiente si el tipo usado es una estructura grande). Segundo, de tal forma que el método pueda modificar el valor a la que apunta el receptor.

Intenta cambiar las declaraciones de los métodos Abs y Scale para usar Vertex como el receptor, en lugar de *Vertex.

El método Scale no tiene efecto cuando v es un vértice`Vertex`. Scale cambia v. Cuando`v` es un tipo (no un puntero) el método ve una copia del vértice Vertex y no puede mutar el valor original.

Abs funciona de cualquier forma. Sólo lee v. No importa si está leyendo el valor original (a través del puntero) o una copia del valor.

package main

import (
    "fmt"
    "math"
)

type Vertex struct {
    X, Y float64
}

func (v *Vertex) Scale(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}

func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
    v := &Vertex{3, 4}
    v.Scale(5)
    fmt.Println(v, v.Abs())
}

Interfaces

Una interfaz es un tipo de datos definido como un conjunto de métodos.

Un tipo interface puede contener cualquier tipo que implemente esos métodos.

package main

import (
    "fmt"
    "math"
)

type Abser interface {
    Abs() float64
}

func main() {
    var a Abser
    f := MyFloat(-math.Sqrt2)
    v := Vertex{3, 4}

    a = f  // a MyFloat implementa Abser
    a = &v // a *Vertex implementa Abser
    a = v  // a Vertex, NO implementa Abser

    fmt.Println(a.Abs())
}

type MyFloat float64

func (f MyFloat) Abs() float64 {
    if f < 0 {
        return float64(-f)
    }
    return float64(f)
}

type Vertex struct {
    X, Y float64
}

func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

Las interfaces se implementan implícitamente

Un tipo implementa una interfaz simplemente implementando los métodos.

No hay declaración explícita.

Las interfaces implícitas desacoplan la implementación de paquetes entre los paquetes que definen las interfaces: ninguno depende de otro.

También favorece la definición de interfaces precisas, porque no tienes que encontrar todas las implementaciones y etiquetarlas con el nuevo nombre de la interfaz.

El paquete io define Reader y Writer; Tú no tienes que hacerlo.

package main

import (
    "fmt"
    "os"
)

type Reader interface {
    Read(b []byte) (n int, err error)
}

type Writer interface {
    Write(b []byte) (n int, err error)
}

type ReadWriter interface {
    Reader
    Writer
}

func main() {
    var w Writer

    // os.Stdout implementa Writer
    w = os.Stdout

    fmt.Fprintf(w, "hola, writer\n")
}

Errores

Un error es cualquier cosa que se defina así mismo como una cadena de error. La idea está en la interfaz predefinida error, con su único método, Error, que devuelve una cadena de caracteres:

type error interface {
    Error() string
}

Las funciones de impresión del paquete fmt sabe cómo llamar al método cuando se le pide imprimir un error.

package main

import (
    "fmt"
    "time"
)

type MyError struct {
    When time.Time
    What string
}

func (e *MyError) Error() string {
    return fmt.Sprintf("A las %v, %s",
        e.When, e.What)
}

func run() error {
    return &MyError{
        time.Now(),
        "ha fallado",
    }
}

func main() {
    if err := run(); err != nil {
        fmt.Println(err)
    }
}

Ejercicio: Errores

Copia tu función Sqrt de ejercicios anteriores y modifícala para que devuelva un valor de tipo error.

Sqrt debe devolver un error no nulo cuando se le pasa un número negativo, dado que no soporta números complejos.

Crea un nuevo tipo

type ErrNegativeSqrt float64

y hazlo del tipo error implementando un método

func (e ErrNegativeSqrt) Error() string

de forma que ErrNegativeSqrt(-2).String() devuelva "no se puede calcular la raíz de un número negativo: -2".

Nota: una llamada a fmt.Sprint(e) dentro del método Error hará que el programa entre en un bucle infinito. Puedes evitarlo convirtiendo e: fmt.Sprint(float64(e)). ¿Por qué?

Cambia tu función Sqrt para devolver un valor ErrNegativeSqrt cuando se le pase un número negativo.

package main

import (
    "fmt"
)

func Sqrt(f float64) (float64, error) {
    return 0, nil
}

func main() {
    fmt.Println(Sqrt(2))
    fmt.Println(Sqrt(-2))
}

Servidores Web

El paquete http sirve peticiones HTTP usando cualquier valor que implemente http.Handler:

package http

type Handler interface {
ServeHTTP(
    w ResponseWriter, r *Request)
}

En este ejemplo, el tipo Hello implementa http.Handler.

Visita http://localhost:4000/ para ver la bienvenida.

Nota: Este ejemplo sólo se puede ejecutar a través del tour web local. Para intentar implementar servidores web puedes Instalar Go.

package main

import (
    "fmt"
    "net/http"
)

type Hello struct{}

func (h Hello) ServeHTTP(
    w http.ResponseWriter,
    r *http.Request) {
    fmt.Fprint(w, "¡Hola!")
}

func main() {
    var h Hello
    http.ListenAndServe("localhost:4000", h)
}

Ejercicio: Manejadores HTTP

Implementa los siguientes tipos y define métodos ServeHTTP para ellos. Asígnalos distintas rutas de tu servidor web.

type String string

type Struct struct {
    Greeting string
    Punct    string
    Who      string
}

Por ejemplo, deberías poder asignárselas usando:

http.Handle("/string", String("I'm a frayed knot."))
http.Handle("/struct", &Struct{"Hola", ":", "Gophers!"})
package main

import (
    "net/http"
)

func main() {
    // Tu http.Handle llama aquí
    http.ListenAndServe("localhost:4000", nil)
}

Imágenes

El paquete image define la interfaz Image:

package image

type Image interface {
    ColorModel() color.Model
    Bounds() Rectangle
    At(x, y int) color.Color
}

(Mira la documentación para más detalles.)

color.Color y color.Model también son interfaces, pero las ignoraremos usando las implementaciones predefinidas image.RGBA y image.RGBAModel.

package main

import (
    "fmt"
    "image"
)

func main() {
    m := image.NewRGBA(image.Rect(0, 0, 100, 100))
    fmt.Println(m.Bounds())
    fmt.Println(m.At(0, 0).RGBA())
}

Ejercicio: Imágenes

¿Recuerdas el generador de imágenes que escribiste antes? Vamos a escribir otro, pero esta vez devolverá una implementación de image.Image en lugar de un slice.

Define tu propio tipo Image, implementa los métodos necesarios, y llama a pic.ShowImage.

Bounds deberían devolver un image.Rectangle, como `image.Rect(0, 0, w, h)`.

ColorModel debería devolver color.RGBAModel.

At debería devolver un color; el valor v del último generador corresponde a `color.RGBA{v, v, 255, 255}`.

package main

import (
    "code.google.com/p/go-tour/pic"
    "image"
)

type Image struct{}

func main() {
    m := Image{}
    pic.ShowImage(m)
}

Ejercicio: Lector Rot13

Un patrón común es un io.Reader que encapsula otro io.Reader, modificando el flujo de datos de alguna forma.

Por ejemplo, la función gzip.NewReader recibe un io.Reader (un flujo de datos en formato gzip) y devuelve un *gzip.Decompressor que también implementa io.Reader (un flujo de datos descomprimidos).

Implementa un rot13Reader que implemente io.Reader y lea de un io.Reader, modificando el flujo de datos aplicándole el algoritmo de cifrado ROT13 a todos los caracteres alfabéticos.

El tipo rot13Reader está completo. Implementa su método Read para que implemente la interfaz io.Reader.

package main

import (
    "io"
    "os"
    "strings"
)

type rot13Reader struct {
    r io.Reader
}

func main() {
    s := strings.NewReader(
        "¡Unf qrfpvsenqb ry póqvtb!")
        //"Lbh penpxrq gur pbqr!")
    r := rot13Reader{s}
    io.Copy(os.Stdout, &r)
}

Concurrencia

Gorutinas

Una gorutina es un hilo ligero manejado por el runtime de Go.

go f(x, y, z)

comienza una nueva gorutina ejecutando

f(x, y, z)

La evaluación de f, x, y, y z ocurre en la gorutina actual y la ejecución de f sucede en una nueva gorutina.

Las gorutinas ejecutan dentro del mismo espacio de direcciones, así que el acceso a memoria compartida debe ser síncronizado. El paquete sync provee de llamadas útiles, aunque no las necesitarás demasiado ya que Go provee otro tipo de primitivas. (Mira la siguiente transparencia.)

package main

import (
    "fmt"
    "time"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        time.Sleep(100 * time.Millisecond)
        fmt.Println(s)
    }
}

func main() {
    go say("mundo")
    say("hola")
}

Canales

Los canales son un tipo de datos a través de los cuales puedes enviar o recibir valores con el operador <-.

ch <- v    // Envía v al canal ch.
v := <-ch  // Recibe del canal ch y
           // asigna el valor a v.

(Los datos fluyen en la dirección de la "flecha".)

De la misma forma que los maps y slices, los canales deben crearse antes de usarlos:

ch := make(chan int)

Por defecto, los envíos y recepciones se bloquean hasta que el otro extremo está listo. Esto permite que las gorutinas se sincronicen sin cerrojos explícitos o variables condicionales.

package main

import "fmt"

func sum(a []int, c chan int) {
    sum := 0
    for _, v := range a {
        sum += v
    }
    c <- sum // envía sum a c
}

func main() {
    a := []int{7, 2, 8, -9, 4, 0}

    c := make(chan int)
    go sum(a[:len(a)/2], c)
    go sum(a[len(a)/2:], c)
    x, y := <-c, <-c // recibe de c

    fmt.Println(x, y, x+y)
}

Canales con buffer

Los canales pueden tener un buffer. Se puede indicar el tamaño del buffer pasando un segundo argumento a make para inicializar un canal con buffer de recepción:

ch := make(chan int, 100)

Los envíos a un canal con buffer se bloquean sólo si el buffer está lleno. Las recepciones se bloquean si el buffer está vacío.

Modifica el ejemplo para llenar el buffer y observa qué ocurre.

package main

import "fmt"

func main() {
    c := make(chan int, 2)
    c <- 1
    c <- 2
    fmt.Println(<-c)
    fmt.Println(<-c)
}

Range y Close

Un hilo que envía datos puede cerrar un canal para indicar que ya no se van a enviar más datos. Los receptores pueden comprobar si un canal ha sido cerrado asignando un segundo parámetro en la expresión de recepción:

v, ok := <-ch

ok es false si no hay más valores que recibir y el canal está cerrado.

El bucle `for i := range c` recibe valores de un canal de forma repetida hasta que éste es cerrado.

Nota: Sólo el hilo que envía datos debería cerrar un canal, nunca el receptor. Los envíos a un canal cerrado generarán una excepción.

Otra nota: Los canales no son como ficheros; normalmente no necesitas cerrarlos. Cerrar un canal sólo es necesario cuando el receptor debe ser avisado que no va a recibir más datos.

package main

import (
    "fmt"
)

func fibonacci(n int, c chan int) {
    x, y := 0, 1
    for i := 0; i < n; i++ {
        c <- x
        x, y = y, x+y
    }
    close(c)
}

func main() {
    c := make(chan int, 10)
    go fibonacci(cap(c), c)
    for i := range c {
        fmt.Println(i)
    }
}

Select

La cláusula select permite a una gorutina esperar diversas operaciones de comunicación.

Un select se bloquea hasta que uno de sus casos puede ser ejecutado, ejecutando el caso que ha satisfecho su condición. Elige uno al azar si hay varios listos.

package main

import "fmt"

func fibonacci(c, quit chan int) {
    x, y := 0, 1
    for {
        select {
        case c <- x:
            x, y = y, x+y
        case <-quit:
            fmt.Println("sale")
            return
        }
    }
}

func main() {
    c := make(chan int)
    quit := make(chan int)
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println(<-c)
        }
        quit <- 0
    }()
    fibonacci(c, quit)
}

Select por defecto

El caso default en una cláusula select ejecuta si no hay otro caso listo.

Utiliza un caso default para intentar enviar o recibir de forma no bloqueante:

select {
case i := <-c:
    // use i
default:
    // receiving from c would block
}

Nota: Este ejemplo no ejecutará en la versión web porque el entorno posee una sandbox que no tiene el concepto de tiempo. Quizá quieras instalar Go para ver este ejemplo en acción.

package main

import (
    "fmt"
    "time"
)

func main() {
    tick := time.Tick(1e8)
    boom := time.After(5e8)
    for {
        select {
        case <-tick:
            fmt.Println("tick.")
        case <-boom:
            fmt.Println("BOOM!")
            return
        default:
            fmt.Println("    .")
            time.Sleep(5e7)
        }
    }
}

Ejercicio: Árboles binarios equivalentes

Hay muchos árboles binarios con la misma secuencia de valores almacenadas en las hojas. Por ejemplo, aquí hay dos árboles binarios almacenando la secuencia 1, 1, 2, 3, 5, 8, 13.

Una función para comprobar que dos árboles binarios almacenan la misma secuencia es bastante compleja en la mayoría de los lenguajes de programación. Usaremos la concurrencia de Go y los canales para escribir una solución sencilla.

Este ejemplo usa el paquete tree, que define el tipo:

type Tree struct {
    Left  *Tree
    Value int
    Right *Tree
}

Ejercicio: Árboles binarios equivalentes

1. Implementa la función Walk.

2. Testea la función Walk.

La función tree.New(k) construye un árbol binario de estructura aleatoria con los valores k, 2k, 3k, ..., 10k.

Crea un nuevo canal ch y lanza la función Walk:

go Walk(tree.New(1), ch)

Después lee e imprime 10 valores del canal. Deberían ser los números 1, 2, 3, ..., 10.

3. Implementa la función Same usando la función Walk para determinar si t1 y t2 almacenan los mismos valores.

4. Testea la función Same.

`Same(tree.New(1), tree.New(1))` debería devolver true, y `Same(tree.New(1), tree.New(2))` debería devolver false.

package main

import "code.google.com/p/go-tour/tree"

// Walk lee el árbol t enviando todos los valores
// del árbol al canal ch.
func Walk(t *tree.Tree, ch chan int)

// Same determina si los árboles
// t1 and t2 contienen los mismos valores.
func Same(t1, t2 *tree.Tree) bool

func main() {
}

Ejercicio: Web Crawler

En este ejercicio usarás las características de concurrencia de Go para paralelizar un web crawler.

Modifica la función Crawl para obtener las URLs en paralelo sin obtener la misma URL dos veces.

package main

import (
    "fmt"
)

type Fetcher interface {
    // Fetch devuelve el cuerpo de una URL
    // un slice con las URLs encontradas en esa página.
    Fetch(url string) (body string, urls []string, err error)
}

// Crawl usa fetcher para recorrer recursivamente
// las páginas empezando por url, hasta un máximo de depth.
func Crawl(url string, depth int, fetcher Fetcher) {
    // TODO: Obtener URLs en paralelo.
    // TODO: No obtengas la misma URL dos veces.
    // Esta implementación tampoco lo hace:
    if depth <= 0 {
        return
    }
    body, urls, err := fetcher.Fetch(url)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Printf("trobat: %s %q\n", url, body)
    for _, u := range urls {
        Crawl(u, depth-1, fetcher)
    }
    return
}

func main() {
    Crawl("http://golang.org/", 4, fetcher)
}

// fakeFetcher es un Fetcher que devuelve resultados repetidos.
type fakeFetcher map[string]*fakeResult

type fakeResult struct {
    body string
    urls []string
}

func (f *fakeFetcher) Fetch(url string) (string, []string, error) {
    if res, ok := (*f)[url]; ok {
        return res.body, res.urls, nil
    }
    return "", nil, fmt.Errorf("not found: %s", url)
}

// fetcher es un fakeFetcher ya relleno.
var fetcher = &fakeFetcher{
    "http://golang.org/": &fakeResult{
        "The Go Programming Language",
        []string{
            "http://golang.org/pkg/",
            "http://golang.org/cmd/",
        },
    },
    "http://golang.org/pkg/": &fakeResult{
        "Packages",
        []string{
            "http://golang.org/",
            "http://golang.org/cmd/",
            "http://golang.org/pkg/fmt/",
            "http://golang.org/pkg/os/",
        },
    },
    "http://golang.org/pkg/fmt/": &fakeResult{
        "Package fmt",
        []string{
            "http://golang.org/",
            "http://golang.org/pkg/",
        },
    },
    "http://golang.org/pkg/os/": &fakeResult{
        "Package os",
        []string{
            "http://golang.org/",
            "http://golang.org/pkg/",
        },
    },
}

Por dónde continuar...

Puedes comenzar instalando Go o descargándote el SDK de Go App Engine SDK.

Una vez que lo tengas ejecutando en tu ordenador, la Documentación de Go es un buen lugar para continuar Contiene referencias a tutoriales, videos, y mucho más.

Para aprender como organizar y trabajar con código Go, mira este screencast o lee Cómo escribir código Go.

Si necesitas ayuda con la biblioteca estándar echa un vistazo a la API de los paquetes. Para ayuda con el lenguaje, puedes asombrarte con la facilidad de lectura de la especificación del lenguaje.

Si estás interesado en escribir aplicaciones web, échale un vistazo a la Wiki Codelab.

Si quieres explorar más a fondo el modelo de concurrencia de Go, échale un vistazo al documento Comparte memoria comunicándote.

La revisión de código Funciones de primera classe en Go da una interesante perspectiva sobre los tipos que tenen las funcions de Go.

El documento Funciones de primera clase de Go otorga una interesante perspectiva de los tipos de funciones en Go.

El Blog de Go tiene un gran archivo de artículos informativos de Go.

Visita golang.org.