Hola, 世界

Bienvenido a la visita guiada del lenguaje de programación Go.

La visita está dividida en tres partes. Al final de cada parte hay una serie de ejercicios para hacer.

El tour es interactivo. Si aprietas el botón Ejecutar ahora (o presionas Shift-Enter) compilarás y ejecutarás el programa en un servidor remoto. tu ordenador. El resultado se muestra justo debajo del código.

Los programas que aparecen en la visita muestran diferentes aspectos de Go, pero también te pueden servir de base para tus propios experimentos.

Edita el programa y vuélvelo a ejecutar.

Cuando estés preparado para continuar, aprieta el botón Siguiente o presiona la tecla AvPág.

package main

import "fmt"

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

Go local

La visita guiada también está disponible en otros idiomas:

(Si quieres traducir la visita guiada a otro idioma, clona el código fuente de https://code.google.com/p/go-tour, traduce el fichero static/index.html, e instálala en App Engine siguiendo las instrucciones que hay en appengine/README.)

Aprieta el botón "Siguiente" o presiona AvPág para continuar.

Go offline

Esta visita guiada también está disponible como un programa suelto que puedes usar cuando no tengas acceso a Internet.

Esa versión es más rápida, porque compila y ejecuta los ejemplos en tu propio ordenador. También incluye ejercicios extra, que no están en esta versión online.

Para ejecutar la visita guiada en tu ordenador, primero instala Go, y luego usa go get para instalar gotour

go get code.google.com/p/go-tour/gotour
y finalmente lanza el ejecutable gotour.

Si no, aprieta el botón "Siguiente" or aprieta AvPág para continuar. (Puedes volver a estas instrucciones luego apretando el botón del "Índice", que cuelga de la parte superior como un punto de libro.)

Introducción

Paquetes

Todo programa en Go está hecho de paquetes.

Los programas se empiezan a ejecutar en el paquete main.

Este programa utiliza los paquetes con las rutas "fmt" y "math".

La convención es que el nombre del paquete es el mismo que el último elemento de su ruta.

package main

import (
	"fmt"
	"math"
)

func main() {
	fmt.Println("Happy", math.Pi, "Day")
}

Importar paquetes

Este código agrupa los paquetes que importa en una única sentencia "factorizada". Aunque también puedes importar los paquetes con sentencias separadas:

import "fmt"
import "math"
pero es típico usar la forma "factorizada" para simplificar.
package main

import (
	"fmt"
	"math"
)

func main() {
	fmt.Printf("Now you have %g problems.",
		math.Nextafter(2, 3))
}

Nombres exportados

Una vez importado un paquete, puedes referirte a los nombres que exporta.

En Go, un nombre es exportado si su primera letra es mayúscula.

Por ejemplo, suponiendo un paquete ficticio, Foo se exportaría, y también FOO, pero foo no.

Intenta ejecutar el código. Luego renombra math.pi a math.Pi e intenta 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 tiene dos parámetros de tipo int.

Fíjate que el tipo va después del nombre de la variable.

(Si quieres más información sobre porqué los tipos tienen la apariencia que tienen, consulta el artículo sobre la sintaxis de declaraciones en Go.)

package main

import "fmt"

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

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

Más funciones

Cuando dos parámetros o más tienen el mismo tipo, puedes omitirlo en todos excepto el último.

En este ejemplo, hemos acortado

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 más de un resultado.

Esta función devuelve dos strings, o cadenas de caracteres.

package main

import "fmt"

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

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

Resultados con nombre

En Go, además de poder nombrar los parámetros de una función, también se pueden nombrar los resultados.

Si se nombran los resultados y se utilizan en la función, la instrucción return devolverá sus valores en ese momento.

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 instrucción var declara una lista de variables; tal como en los parámetros de una función, el tipo va 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)
}

Inicialización de variables

Una declaración de variables puede incluir valores iniciales, uno por variable.

Si éstos estan presentes, se puede omitir el tipo y la variable tendrá el tipo del valor inicial utilizado.

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

Declaraciones de variables cortas

Dentro de una función, la asignación especial := se puede utilizar para ahorrarse la declaración.

(Fuera de las funciones, todas las sentencias empiezan con una palabra reservada y la asignación especial := no existe.)

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

Constantes

Las constantes se declaran como las variables, poniendo el prefijo const, que es una palabra reservada.

Las constantes pueden ser caracteres, cadenas de caracteres, Booleanos, o valores numéricos.

package main

import "fmt"

const Pi = 3.14

func main() {
	const World = "世界"
	fmt.Println("Hello", World)
	fmt.Println("Happy", Pi, "Day")

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

Constantes Numéricas

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

Una constante sin tipo adopta el tipo que el contexto requiere.

Intenta imprimir needInt(Big), también.

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

Para hacer bucles, Go solo tiene el for.

El for básico tiene el mismo aspecto que el de C o Java, solo que los paréntesis () no hacen falta (no son ni 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)
}

Más For

Como en C o Java, puedes dejar vacías las sentencias pre y post.

package main

import "fmt"

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

El For es el "while"

Llegados a ese punto se pueden obviar los puntos-y-coma: el while de C se deletrea for en Go.

package main

import "fmt"

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

Bucle infinito

Si se omite la condición en un bucle, éste dará vueltas sin parar, así el bucle infinito tiene una forma compacta.

package main

func main() {
	for {
	}
}

If

La sentencia if tiene el mismo aspecto que el de C o Java, solo que los paréntesis () no hacen falta (no son ni 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 corta

Como el for, el if puede empezar con una instrucción corta que se ejecuta antes de la condición.

El alcance de las variables que se declaran en esa instrucción se limita al código del if.

(Intenta usar v en el return del final.)

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 en la instrucción corta de un if también son visibles cualquier bloque 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)
	}
	// can't use v here, though
	return lim
}

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

Tipos básicos

Los tipos básicos de Go son

bool

string

int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr

byte // alias for uint8

rune // alias for int32
     // represents a Unicode code point

float32 float64

complex64 complex128
package main

import (
	"math/cmplx"
	"fmt"
)

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

Estructuras

Un struct es una colección de campos.

(Y una declaración que empieza con type declara un tipo.)

package main

import "fmt"

type Vertex struct {
	X int
	Y int
}

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

Campos de estructuras

Los campos se acceden usando el 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 tiene punteros, pero no tiene aritmética de punteros.

Si tienes un puntero a una estructura, los campos se acceden igual, la indirección a través del puntero es transparente.

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

Literales para estructuras

Los literales para estructuras denotan un valor nuevo reservado en memoria con los valores de los campos indicados por orden.

Se puede especificar un subconjunto de los campos usando la notación Campo:. (Y entonces el orden no importa.)

El prefijo especial & obtiene un puntero a la nueva estructura.

package main

import "fmt"

type Vertex struct {
	X, Y int
}

var (
	p = Vertex{1, 2}  // has type Vertex
	q = &Vertex{1, 2} // has type *Vertex
	r = Vertex{X: 1}  // Y:0 is implicit
	s = Vertex{}      // X:0 and Y:0
)

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

La función new

La expresión new(T) devuelve un puntero a un nuevo valor de tipo T inicializado a 0.

var t *T = new(T)

or

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

Maps

Un map asocia un valor a cada llave.

Antes de usar un map, hay que crearlo con make (en vez de con new); y el map nil por definición está vacío y no se le puede añadir nada.

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

Literales de map

Los literales de map son como los de las estructuras, pero las llaves son necesarias (en el ejemplo, "Bell Labs" y "Google").

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

Más literales de map

Si el tipo del nivel superior es simplemente un nombre de tipo, se puede omitir en 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)
}

Alterar un map

Insertar o actualizar un elemento en un map m:

m[key] = elem

Obtener un elemento:

elem = m[key]

Borrar un elemento:

delete(m, key)

Determinar si una llave está presente con una asignación múltiple:

elem, ok = m[key]

Si la llave key está en m, ok será true, Si no, ok será false y elem será el valor cero del tipo de los elementos.

Análogamente, al obtener un elemento, si la llave no está presente el resultado es el valor cero del tipo de los elementos.

package main

import "fmt"

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

	m["Answer"] = 42
	fmt.Println("The value:", m["Answer"])

	m["Answer"] = 48
	fmt.Println("The value:", m["Answer"])

	delete(m, "Answer")
	fmt.Println("The value:", m["Answer"])

	v, ok := m["Answer"]
	fmt.Println("The value:", v, "Present?", ok)
}

Porciones

Un slice (porción) es un puntero a una tabla de valores e incluye una longitud.

[]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])
	}
}

Porciones de porciones

Al obtener una porción de un slice el resultado es otro slice que apunta al mismo array que el original.

El resultado de evaluar la expresión

s[lo:hi]

es un slice con los elementos desde lo hasta hi-1, ambos incluidos. Por tanto

s[lo:lo]
está 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])

	// missing low index implies 0
	fmt.Println("p[:3] ==", p[:3])

	// missing high index implies len(s)
	fmt.Println("p[4:] ==", p[4:])
}

Crear porciones

Los slices se crean con la función make. Ésta reserva memoria para una tabla inicializada a cero y devuelve un slice que apunta a esa tabla:

a := make([]int, 5)  // len(a)=5
Los slices tienen una longitud y una capacidad. La capacidad es el tamaño máximo que puede abarcar el slice en la tabla subyacente.

Para especificar una capacidad, se puede pasar un tercer argumento a make:

b := make([]int, 0, 5) // len(b)=0, cap(b)=5
Un slice se puede alargar, con límite la capacidad, obteniendo una porción más larga:

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

La porción cero

El valor cero de un slice es nil.

Una porción así tiene longitud y capacidad 0.

(Para saber más sobre slices, lee el artículo "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("nil!")
	}
}

Valores de tipo función

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

Clausuras de función

Y las funciones son clausuras completas (full closures).

La función adder devuelve una clausura. Cada clausura está ligada a su propia variable sum.

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

Intervalos

El for tiene una versión usando range que itera 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)
	}
}

Más intervalos

Para ignorar el índice o el valor, puedes asignar a _.

Si solo quieres el índice, puedes obviar “, value” por completo.

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

Switch

Probablemente ya sabías que pinta iba a tener el switch.

Por defecto, al final de un case se sale del switch, a menos que haya una instrucción fallthrough.

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

Switch: orden de evaluación

El switch evalúa los casos de arriba a abajo, parando cuando un caso es cierto.

(Por ejemplo,

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

no llamará a f si i == 0.)

package main

import (
	"fmt"
	"time"
)

func main() {
	fmt.Println("When's Saturday?")
	today := time.Now().Weekday()
	switch time.Saturday {
	case today+0:
		fmt.Println("Today.")
	case today+1:
		fmt.Println("Tomorrow.")
	case today+2:
		fmt.Println("In two days.")
	default:
		fmt.Println("Too far away.")
	}
}

Switch sin condición

Cuando un switch no tiene condición es equivalente a switch true.

Esta versión permite escribir cadenas de ifs muy largas.

package main

import (
	"fmt"
	"time"
)

func main() {
	t := time.Now()
	switch {
	case t.Hour() < 12:
	    fmt.Println("Good morning!")
	case t.Hour() < 17:
	    fmt.Println("Good afternoon.")
	default:
	    fmt.Println("Good evening.")
	}
}

Ejercicio: Bucles y funciones

Para jugar un poco con funciones y bucles, implementa la raíz cuadrada usando el método de Newton.

En este caso, el método de Newton consiste en aproximar Sqrt(x), escogiendo un punto inicial z y luego repitiendo:

Newton's method

Para empezar, repite el cálculo 10 veces y mira cuánto te aproximas al valor real para diferentes valores (1, 2, 3, ...).

Luego, cambia la condición del bucle para que se detenga cuando el valor no cambie (o cambie menos que un valor delta muy pequeño). Comprueba si eso da más o menos iteraciones. Cuánto te aproximas a la función math.Sqrt?

Pista: para declarar e inicializar un valor real, utiliza un literal con sintaxis de número real o utiliza una conversión:

z := float64(1)
z := 1.0
package main

import (
	"fmt"
)

func Sqrt(x float64) float64 {
}

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

Ejercicio: Maps

Implementa WordCount. Debería devolver un map de contadores para cada “palabra” en la cadena de caracteres s. La función wc.Text correrá una batería de tests sobre la función proporcionada y dará un veredicto.

Quizás encuentres útil la función strings.Fields.

package main

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

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

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

Ejercicio: Porciones

Implementa Pic. Debería devolver un slice de longitud dy, donde cada elemento es a su vez un slice de dx elementos de tipo entero sin signo de 8 bits. Cuando ejecutes el programa, mostrará tu imagen, interpretando los enteros como un nivel de gris (bueno, de azul).

La imagen se deja a tu elección. Hay funciones interesantes como x^y, (x+y)/2, y x*y.

(Necesitarás un bucle para reservar memoria para cada []uint8 dentro de [][]uint8.)

(Utiliza uint8(valorEntero) para hacer la conversión a uint8.)

package main

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

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

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

Ejercicio: Clausura de Fibonacci

Divirtámonos con las funciones.

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

package main

import "fmt"

// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
}

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

Ejercicio avanzado: Raíz cúbica de números complejos

Exploremos los tipos complex64 y complex128, que permiten trabajar con números complejos y forman parte del lenguaje. El método de Newton aplicado a la raíz cúbica implica repetir:

Newton's method

Para comprobar que el algoritmo funciona, busca primero la raíz cúbica de 2. Hay una función Pow para potencias 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 e Interfaces

Métodos

Go no tiene clases. De todas maneras, puedes definir métodos para estructuras.

El receptor del método aparece en su propia lista de parámetros entre la palabra 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ás métodos

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

Aunque no podrás definir un método para un tipo de otro paquete, o para 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 receptores de tipo puntero

Los métodos se pueden asociar a un tipo, o a un puntero a un tipo.

Acabamos de ver dos métodos Abs. El de esta página se define para el tipo *Vertex (un puntero a Vertex) y en el tema anterior había otro para el tipo MyFloat.

Hay dos razones para utilizar un puntero como tipo receptor del método. La primera es evitar la copia del valor receptor en las llamadas al método (más eficiente si el tipo es una estructura grande). La segunda es que así el método puede modificar el valor del receptor.

Intenta cambiar las declaraciones de los métodos Abs y Scale de forma que usen Vertex como receptor, en vez de *Vertex.

El método Scale no tiene efecto cuando v es un Vertex. Como que Scale cambia el valor de v, si v no es un puntero, el método verá solamente una copia del Vertex y no el valor original.

Abs funciona en los dos casos, porque solo lee v, y entonces no importa si lee el valor original o una copia.

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

Un conjunto de métodos define un interfaz.

Una variable de tipo interfaz puede contener valores de 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 implements Abser
	a = &v // a *Vertex implements Abser
	a = v  // a Vertex, does NOT
	       // implement 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)
}

Los Interfaces se satisfacen implícitamente

Un tipo implementa un interfaz si implementa sus métodos.

No hay una declaración explícita de intenciones.

El hecho de que los interfaces sean implícitos permite el desacoplamiento entre los paquetes que implementan un interfaz y los que lo definen: no existen dependencias entre ellos.

También fomenta la definición de interfaces precisos, porque no hay que encontrar cada implementación y cambiarla al nuevo nombre del interfaz.

El paquete io define Reader y Writer; así no tienes que hacerlo tú.

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 implements Writer
	w = os.Stdout

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

Errores

Un error es un valor que se puede describir a sí mismo usando una cadena de caracteres. Esta es la idea detrás del tipo error predefinido en Go. Es un interfaz con un sólo método que devuelve una cadena de caracteres:

type error interface {
	Error() string
}

Las funciones de salida del paquete fmt llaman automáticamente a ese método cuando reciben un error.

package main

import (
	"fmt"
	"time"
)

type MyError struct {
	When time.Time
	What string
}

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

func run() error {
	return &MyError{
		time.Now(),
		"it didn't work",
	}
}

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

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 el ejemplo, el tipo Hello implementa http.Handler.

Visita http://localhost:4000/ para ver el saludo. Nota: Este ejemplo no funciona en la versión web de la visita guiada. Para poder probar estos ejemplos deberías instalar Go.

package main

import (
	"fmt"
	"net/http"
)

type Hello struct{}

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

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

Imágenes

El paquete image define el interfaz Image:

package image

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

(Consulta la documentación para conocer todos los detalles.)

Además, color.Color y color.Model son interfaces, pero ignoraremos eso al usar las implementaciones predefinidas color.RGBA y color.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: Errores

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

Sqrt debería devolver un valor de error distinto de nil cuando recibe un número negativo, ya que no está preparada para números complejos.

Crea un nuevo tipo

type ErrNegativeSqrt float64

y haz que sea un tipo de error poniéndole un método

func (e ErrNegativeSqrt) Error() string

tal que la llamada ErrNegativeSqrt(-2).Error() devuelva "cannot Sqrt negative number: -2".

Nota: una llamada a fmt.Print(e) en el código del método Error mete al programa en un bucle infinito. Puedes evitarlo convirtiendo e primero: fmt.Print(float64(e)). Porqué?

Cambia tu función Sqrt para que devuelva un valor de tipo ErrNegativeSqrt cuando reciba 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))
}

Ejercicio: Handlers HTTP

Implementa los tipos siguientes y define ServeHTTP para ellos. Regístralos para servir rutas específicas en tu servidor web.

type String string

type Struct struct {
	Greeting string
	Punct    string
	Who      string
}

Por ejemplo, deberías poder registrar handlers usando:

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

import (
	"net/http"
)

func main() {
	// your http.Handle calls here
	http.ListenAndServe("localhost:4000", nil)
}

Ejercicios: Imágenes

Recuerdas el generador de imágenes que escribiste anteriormente? Escribamos otro, pero esta vez devolverá un valor que implemente image.Image en vez de un slice.

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

Bounds debería 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 en el generador de imágenes anterior se corresponde con color.RGBA{v, v, 255, 255} en este.

package main

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

type Image struct{}

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

Ejercicio: Lector Rot13

Es típico hacer un io.Reader que envuelve a otro io.Reader, y modificar los datos de alguna manera.

Por ejemplo, la función gzip.NewReader recibe un io.Reader (una secuencia de bytes comprimidos con gzip) y devuelve un *gzip.Reader que también implementa io.Reader (la secuencia de bytes descomprimida).

Implementa un rot13Reader que implemente io.Reader y también lea de un io.Reader, modificando aplicándole a la secuencia de bytes el cifrado por sustitución ROT13 a los caracteres alfabéticos.

El tipo rot13Reader se proporciona. Conviértelo en un io.Reader implementando su método Read.

package main

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

type rot13Reader struct {
	r io.Reader
}

func main() {
	s := strings.NewReader(
		"Lbh penpxrq gur pbqr!")
	r := rot13Reader{s}
	io.Copy(os.Stdout, &r)
}
Concurrency

Concurrencia

Gorutinas

Una gorutina es un hilo de ejecución muy ligero (un lightweight thread) que gestiona el runtime de Go.

go f(x, y, z)

lanza una nueva gorutina ejecutando

f(x, y, z)

La evaluación de f, x, y, y z se hace en la gorutina actual y la ejecución de f en la nueva.

Las gorutinas se ejecutan en el mismo espacio de direcciones, y por tanto el acceso a la memoria compartida debe sincronizarse. El paquete sync ofrece primitivas útiles para esto, aunque es probable que no las necesites demasiado porque hay otras formas de sincronización (como se explica en el tema siguiente.)

package main

import (
	"fmt"
	"runtimetime"
)

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

func main() {
	go say("world")
	say("hello")
}

Canales

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

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

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

Como en los maps o slices, los canales deben crearse antes del primer uso:

ch := make(chan int)

Por defecto, los envíos y recepciones bloquean la gorutina hasta que haya otra gorutina lista al otro lado. Esto permite la sincronización sin necesidad de locks o variables especiales.

package main

import "fmt"

func sum(a []int, c chan int) {
	sum := 0
	for _, v := range a {
		sum += v
	}
	c <- sum  // send sum to 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  // receive from c

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

Canales con buffer

Los canales pueden tener un buffer. Para crear un canal con buffer solo hay que poner un segundo parámetro en la creación del canal:

ch := make(chan int, 100)

Los envíos a canales con buffer solo son bloqueantes si el buffer está lleno. Las recepciones se bloquean cuando el buffer está vacío.

Modifica el ejemplo para llenar el buffer por encima de su capacidad para ver qué ocurre.

package main

import "fmt"

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

Recorrer y cerrar

Una gorutina puede cerrar un canal (con close) para indicar que no va a enviar más valores por él. Los receptores pueden entonces determinar si el canal ha sido cerrado poniendo una segunda variable en la instrucción de recepción: después de

v, ok := <-ch

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

El bucle for i := range c recibe valores de un canal repetidamente hasta que éste sea cerrado.

Nota: Sólo la gorutina que envía debería cerrar el canal, nunca la receptora. Enviar en un canal cerrado produce un panic.

Otra nota: Los canales no son como ficheros; en general no hay que cerrarlos. Cerrar un canal sólo es necesario si el receptor debe saber que no habrá más valores, como ocurre en el bucle con range.

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 instrucción select permite a una gorutina gestionar a la vez varias operaciones de comunicación.

El select bloquea la gorutina actual hasta que alguno de los casos pueda continuar, y entonces ejecuta ese caso. Cuando muchos de los casos permiten continuar, se escoge uno al azar.

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

Selección por defecto

El caso default en un select se ejecuta cuando ninguno de los otros permite continuar.

Esto se puede utilizar para intentar una comunicación sin bloquear la ejecución.

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

Nota: Este ejemplo no se puede ejecutar en la versión web de la visita guiada porque el entorno del sandbox no tiene concepto de tiempo. Quizás quieras instalar Go para poder probarlo.

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 distintos com la misma secuencia de valores almacenada en las hojas. Por ejemplo, los siguientes árboles contienen la secuencia 1, 1, 2, 3, 5, 8, 13. binary trees

Una función que determine si dos árboles binarios contienen la misma secuencia es bastante complejo de implementar en la mayoría de lenguajes. Utilizaremos la concurrencia de Go y sus canales para escribir una solución simple.

Este ejemplo utiliza 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. Comprueba la función Walk.

La función tree.New(k) construye un árbol con estructura al azar que contiene los valores k, 2k, 3k, ..., 10k.

Crea un nuevo canal ch y lanza el walker:

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

Luego lee e imprime 10 valores sacados del canal. Deberían se los números 1, 2, 3, ..., 10.

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

4. Comprueba 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 "tourcode.google.com/p/go-tour/tree"

// Walk recorre la hojas del árbol t 
// enviando los valores por el canal ch.
func Walk(t *tree.Tree, ch chan int)

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

func main() {
}

Ejercicio: web crawler

En este ejercicio usarás la concurrencia de Go con el fin de paralelizar un web crawler (que obtiene recursivamente páginas web a partir de una URL).

Modifica la función Crawl para cargar las URLs en paralelo evitando repeticiones.

package main

import (
	"fmt"
)

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

// Crawl usa el 'fetcher' para obtener recursivamente las páginas
// empezando en cierta 'url', con cierta profundidad máxima 'depth'.
func Crawl(url string, depth int, fetcher Fetcher) {
   // TODO: Obtener URLs en paralelo.
	// TODO: Evitar repeticiones.
	// Esta implementación no hace ninguna de las dos cosas.
	if depth <= 0 {
		return
	}
	body, urls, err := fetcher.Fetch(url)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("found: %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 the devuelve resultados ficticios.
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 contiene los datos ficticios.
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/",
		},
	},
}

Y ahora qué...

Puedes empezar con Go instalándolo o descargando el SDK de Go App Engine.

Una vez tengas instalado Go en tu máquina, la La documentation de Go es un gran sitio para continuar empezar. Contiene materiales de referencia, tutoriales, vídeos y más cosas.

Para aprender a organizar y trabajar con código Go, mira este tutorial o lee Cómo escribir código Go (en inglés).

Si necesitas ayuda con la librería estándar, mira la referencia sobre paquetes. Para ayuda sobre el lenguaje en sí, quizás te sorprenda que la Especificación del Lenguaje is muy legible.

Para explorar el modelo de concurrencia de Go, consulta Share Memory by Communicating.

En First Class Functions in Go se da una perspectiva interesante sobre los tipos de función de Go.

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

Visita golang.org para más información.