提交 f94c6fce 编写于 作者: M ma0

aligned and formatted chapter 2 based on...

aligned and formatted chapter 2 based on https://github.com/Jackgris/build-web-application-with-golang_ES
上级 fcbfc56c
# 2.2 Principios de Go
En esta sección, vamos a aprender como definir constantes, variables con tipos básicos y algunos conocimientos mas de la programación en Go.
## Definición de variables
Tenemos muchas formas diferentes de definir variables con la sintaxis de Go.
Utilizando la palabra reservada `var` es la forma básica de definir una variable, observe como en Go se coloca el tipo de variable luego del nombre de la misma.
```
// definición de una variables de nombre “variableName” y tipo "type"
var variableName type
```
Definición de múltiples variables.
```
// definimos tres variables donde su tipo es "type"
var vname1, vname2, vname3 type
```
Definición de una variable con un valor inicial.
```
// definición de una variable de nombre “variableName”, tipo "type" y valor "value"
var variableName type = value
```
Definición de múltiples variables con valores iniciales.
```
/*
Definición de tres variables de tipo "type", y sus valores iniciales.
vname1 de valor v1, vname2 de valor v2, vname3 de valor v3
*/
var vname1, vname2, vname3 type = v1, v2, v3
```
¿Crees que es muy tedioso utilizar las anteriores formas para definir variables? Entonces no te preocupes, porque el equipo de desarrollo de Go también se encontró con este problema.
Por lo que si deseas definir una variable con valores iniciales, podemos omitir el tipo de cada una,
por lo que el código quedaría algo así:
```
/*
Definición de tres variables de tipo "type", y sus valores iniciales.
vname1 de valor v1,vname2 de valor v2,vname3 de valor v3
*/
var vname1, vname2, vname3 = v1, v2, v3
```
Bueno, se que aun no es lo suficientemente sencillo. Vamos a ver como lo mejoramos.
```
/*
Definición de tres variables de tipo "type", y sus valores iniciales.
vname1 de valor v1,vname2 de valor v2,vname3 de valor v3
*/
vname1, vname2, vname3 := v1, v2, v3
```
Ahora se ve mucho mejor. Use `:=` para reemplazar a `var` y `type`, esto se llama declaración breve. Pero espere, esto tiene una limitante, esta forma de declaración solo puede ser utilizada dentro de una función. Si intenta utilizarla fuera del cuerpo de una función va a recibir un error de compilación. Por lo que normalmente utilizamos `var` para definir variables globales, y podemos utilizar la declaración breve en `var()`.
`_` (guión bajo) es un nombre especial de variable, cualquier valor que le sea otorgado será ignorado. Por ejemplo, le otorgamos `35` a `b`, y descartamos el `34`.( ***Este ejemplo nos muestra como esto funciona. Parece inútil aquí porque normalmente lo utilizamos cuando tomamos los valores de retorno de una función.*** )
```
_, b := 34, 35
```
Si no utilizas alguna de las variables declaradas en el programa, el compilador les lanzara un error. Intente compilar el siguiente código, vea que sucede.
```
package main
func main() {
var i int
}
```
## Constantes
Las llamadas constantes son los valores que se determinan en el momento de compilación, y que no se pueden modificar durante la ejecución. En Go, podemos utilizar números, booleans o string como tipos de constantes.
Definimos una constante de la siguiente manera.
```
const nombreConstante = valor
// podemos asignar el tipo de una constante si es necesario
const Pi float32 = 3.1415926
```
Otros ejemplos.
```
const Pi = 3.1415926
const i = 10000
const MaxThread = 10
const prefix = "astaxie_"
```
## Tipos básicos
### Booleanos
En Go, podemos usar `bool` para definir variables de tipo booleanas, su valor puede ser unicamente `true` o `false`, y `false` sera el valor por defecto. ( ***No puede convertir el tipos variables entre números y booleanos!*** )
```
// ejemplo de código
var isActive bool // variable global
var enabled, disabled = true, false // omitimos el tipo de las variables
func test() {
var available bool // variable local
valid := false // definición breve de una variable
available = true // asignación de un valor a una variable
}
```
### Tipos Numéricos
Tipos enteros incluyendo los enteros con signo y sin signo. Para esto Go tiene `int` y `uint` al mismo tiempo, ellos tienen la misma longitud, pero la longitud específica va a depender de su sistema operativo. Ellos utilizan 32-bit en sistemas operativos de 32-bit, y 64-bit en sistemas operativos de 64-bit. Go también tiene tipos que tienen una longitud específica incluyendo `rune`, `int8`, `int16`, `int32`, `int64`, `byte`, `uint8`, `uint16`, `uint32`, `uint64`. Tenga en cuenta que `rune` es el alias de `int32` y `byte` es el alias para `uint8`.
Algo importante que debe saber es que no puede asignar valores entre estos tipos, esta operación va a causar un error de compilación.
```
var a int8
var b int32
c := a + b
```
Aunque int32 tiene una longitud mayor que uint8, y tiene la misma longitud que int, pero no podrá asignar valores entre ellos. ( ***aquí c afirmará ser de tipo `int`*** )
Los tipos Float tienen `float32` y `float64`, y no tiene tipo llamado `float`, este último solo es el tipo por defecto cuando se utiliza la declaración breve.
¿Eso es todo? ¡No! Go también tiene número complejos. `complex128` (con una parte de 64-bit reales y otra parte de 64-bit imaginarios) es el tipo por defecto, si necesita un tipo mas pequeño, hay uno llamado `complex64` (con una parte de 32-bit reales y otra de 32-bit imaginarios). Su forma es `RE+IMi`, donde `RE` es la parte real e `IM` es la parte imaginaria, el último `i` es el número imaginario. Este es un ejemplo de número complejo.
```
var c complex64 = 5+5i
//salida: (5+5i)
fmt.Printf("Value is: %v", c)
```
### String
Acabamos de hablar sobre que Go utiliza el juego de caracteres de UTF-8. Las Strings son representadas mediante comillas dobles `""` o comillas simples ``` `` ```.
```
// ejemplo de código
var frenchHello string // forma básica de definir una string
var emptyString string = "" // definimos una string con un valor vacío
func test() {
no, yes, maybe := "no", "yes", "maybe" // declaración breve
japaneseHello := "Ohaiou"
frenchHello = "Bonjour" // forma básica para asignar un valor
}
```
Es imposible cambiar el valor de una string por su índice, va a obtener un error cuando compile el siguiente código.
```
var s string = "hello"
s[0] = 'c'
```
Pero, y si ¿realmente deseo cambiar el valor de un solo carácter de una string? Intente utilizar el siguiente código.
```
s := "hello"
c := []byte(s) // convertimos una string a un tipo []byte
c[0] = 'c'
s2 := string(c) // volvemos a convertirlo a un tipo string
fmt.Printf("%s\n", s2)
```
Podemos utilizar el operador `+` para combinar dos strings.
```
s := "hello,"
m := " world"
a := s + m
fmt.Printf("%s\n", a)
```
y también.
```
s := "hello"
s = "c" + s[1:] // no puede cambiar los valores de una string por índice pero puede obtener sus valores.
fmt.Printf("%s\n", s)
```
Que pasa si ¿quiero tener una string de varias líneas?
```
m := `hello
world`
```
capturará todos los caracteres en una string.
### Tipos de Errores
Go tiene un tipo de `error` para hacer frente a los mensajes de error. También tiene un paquete llamado `errors` para manejar los errores.
```
err := errors.New("emit macho dwarf: elf header corrupted")
if err != nil {
fmt.Print(err)
}
```
### Estructura de datos fundamental
La siguiente imagen viene de un artículo sobre [Estructuras de datos en Go](http://research.swtch.com/godata) en [El Blog de Russ Cox](http://research.swtch.com/). Como puede ver, Go nos da bloques de memoria para almacenar datos.
![](images/2.2.basic.png?raw=true)
Imagen 2.1 Go estructura de datos fundamental
## Algunas habilidades
### Definir por grupo
Si desea definir múltiples constantes, variables o importaciones de paquetes, podemos utilizar formularios de grupos.
Forma básica.
```
import "fmt"
import "os"
const i = 100
const pi = 3.1415
const prefix = "Go_"
var i int
var pi float32
var prefix string
```
Forma de grupo.
```
import(
"fmt"
"os"
)
const(
i = 100
pi = 3.1415
prefix = "Go_"
)
var(
i int
pi float32
prefix string
)
```
A menos que asigne el valor de la constante `iota`, el primer valor de la constante en el grupo `const()` va a ser `0`. Si a las siguientes constantes no se les asignan valores explícitamente, sus valores serán el mismo que el último. Si el último valor de la constante es `iota`, los valores de las siguientes constantes que no son asignadas también serán `iota`.
### Enumerar con iota
Go tiene la palabra reservada `iota`, esta palabra reservada va a crear un `enum`, que va a comenzar con `0`, y se va a ir incrementando en `1`.
```
const(
x = iota // x == 0
y = iota // y == 1
z = iota // z == 2
w // Si no hay una expresión después del nombre de la contasnte, se usa la última expresión,
//entonces aquí nos dice que w = iota implícitamente. Por lo tanto w == 3, e y y x los dos, podrían omitir "= iota" de la misma forma.
)
const v = iota // cuando se vuelve a utilizar la palabra reservada `const` seguida de la asignación iota nuevamente, este reinicia su valor a `0`, por esto v = 0.
const (
e, f, g = iota, iota, iota // e=0,f=0,g=0 mismos valores de iota en una línea.
)
```
### Algunas reglas
La razón por la que Go es conciso es porque tiene algunos comportamientos por defecto.
- Cualquier variables que comience con mayúscula se va exportar como pública, de otra manera será una variable privada.
- Se usa la misma regla para funciones y constantes, no existen las palabras reservadas `public` o `private` en Go.
## array, slice, map
### array
`array` es un array obviamente, lo definimos de la siguiente forma.
var arr [n]type
en `[n]type`, `n` es la longitud del array, `type` es el tipo de elementos que contiene. Como en otros lenguajes, utilizamos `[]` para obtener o establecer los elementos en el array.
```
var arr [10]int // un array de tipo int
arr[0] = 42 // los array arrancan su índice en 0
arr[1] = 13 // asignamos un valor a un elemento
fmt.Printf("El primer elemento es %d\n", arr[0])
// obtenemos el valor del elemento, este nos devolvera 42
fmt.Printf("El último elemento es %d\n", arr[9])
//este nos devolverá el valor por defecto del elemento número 10 en este array, que es 0 en este caso.
```
Debido a que la longitud es una parte del tipo del array, `[3]int` y `[4]int` son diferentes tipos, por lo que no podemos cambiar la longitud del arrays. Cuando utilizamos arrays como argumentos, las funciones reciben copias en lugar de referencias a ellos! Si lo que busca es utilizar referencias, es posible que desee utilizar `slice` de las que vamos a hablar más adelante.
Es posible usar `:=` cuando definimos arrays.
```
a := [3]int{1, 2, 3} // define un array de int con 3 elementos
b := [10]int{1, 2, 3}
// define array de int con 10 elementos, y los tres primeros son asignados
// el resto utiliza el valor por defecto 0.
c := [...]int{4, 5, 6} // usamos `…` para reemplazar el número de la longitud, Go la va a calcular por usted.
```
Es posible que desee utilizar arrays como elementos de un arrays, vamos a ver como hacerlo.
```
// definimos un array de dos dimensiones con dos elementos, y cada elemento tiene cuatro elementos.
doubleArray := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}}
// podemos escribirlo de una forma más corta.
easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}
```
Array estructuras de datos fundamentales.
![](images/2.2.array.png?raw=true)
Imagen 2.2 Relación de mapeo de array multi-dimensionales
### slice
En muchas situaciones, un array no es una buena elección. Por ejemplo, nosotros no sabemos cual va a ser el largo del array cuando lo definimos, entonces vamos a necesitar un "array dinámico". Este es llamado `slice` en Go.
Un `slice` no es realmente un `array dinámico`, es un tipo referenciado. `slice` apunta a un `array` subyacente, su declaración es similar a los `array`, pero no necesitan una longitud.
```
// al igual que como definimos un array, pero no le pasamos la longitud esta vez
var fslice []int
```
Entonces definimos un `slice`, e inicializamos sus datos.
```
slice := []byte {'a', 'b', 'c', 'd'}
```
Un `slice` puede ser re-definido desde otro slice o array existente. Un `slice` usa `array[i:j]` para tomar sus valores, donde `i` es
el comienzo del indice y `j` es el final del indice, note que `array[j]` no será un, ahora la longitud
del slice es `j-i`.
```
// definimos un slice con 10 elementos donde el tipo es byte
var ar = [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
// definimos dos slices de tipo []byte
var a, b []byte
// a apunta a los elementos del tercero al quinto elemento en el array ar.
a = ar[2:5]
// ahora tiene los elementos ar[2],ar[3] y ar[4]
// b es otro slice del array ar
b = ar[3:5]
// ahora b tiene los elementos ar[3] y ar[4]
```
Note la diferencia entre `slice` y `array` cuando los definimos. Nosotros usamos `[…]` para que Go
calcule la longitud pero usamos`[]` para definir unicamente slice.
Su estructura de datos subyacente.
![](images/2.2.slice.png?raw=true)
Imagen 2.3 Similitud entre slice y array
slice tiene algunas operaciones utiles.
- `slice` comienza en 0, `ar[:n]` es igual a `ar[0:n]`
- El segundo indice sera la longitud del `slice` si lo omitimos, `ar[n:]` será igual a `ar[n:len(ar)]`.
- Se puede usar `ar[:]` para tomar todo el array, la razón de esto se explica en las dos anteriores explicaciones.
Más ejemplos acerca de `slice`
```
// definimos un array
var array = [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
// definimos dos slices
var aSlice, bSlice []byte
// Algunas operaciones útiles
aSlice = array[:3] // es igual a aSlice = array[0:3] aSlice tiene los elementos a,b,c
aSlice = array[5:] // es igual a aSlice = array[5:10] aSlice tiene los elementos f,g,h,i,j
aSlice = array[:] // es igual a aSlice = array[0:10] aSlice tiene todos los elementos
// slice desde slice
aSlice = array[3:7] // aSlice tiene los elementos d,e,f,g,len=4,cap=7
bSlice = aSlice[1:3] // bSlice contiene aSlice[1], aSlice[2], entonces este tendrá los elementos e,f
bSlice = aSlice[:3] // bSlice contiene aSlice[0], aSlice[1], aSlice[2], entonces este tiene d,e,f
bSlice = aSlice[0:5] // bSlice se puede expandir, ahora bSlice contiene d,e,f,g,h
bSlice = aSlice[:] // bSlice tiene los mismos elementos que aSlice, que son d,e,f,g
```
`slice` es un tipo de referencia, por lo que si uno se modifica entonces afectará al resto.
Por ejemplo, con los elementos de anteriores `aSlice` y `bSlice`, si se modifica el valor de algún elemento en `aSlice`,
`bSlice` será modificado también.
`slice` por definición es como una estructura, este contiene tres partes.
- Un puntero que apunta donde comienza el `slice`.
- la longitud del `slice`.
- Capacidad, la longitud de donde comienza el indice hacia donde termina el indice del `slice`.
```
Array_a := [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
Slice_a := Array_a[2:5]
```
Los fundamentos de la estructura de datos del código anterior es el siguiente.
![](images/2.2.slice2.png?raw=true)
Imagen 2.4 Array información del slice
Hay algunas funciones integradas para las slice.
- `len` nos devuelve la longitud del `slice`.
- `cap` nos devuelve la longitud máxima del `slice`
- `append` añade uno o mas elementos al `slice`, y nos devuelve el `slice` .
- `copy` copia elementos de un slice hacia otro, y nos devuelve el número de elementos que fueron copiados.
Atención: `append` va a cambiar el array al que apunta el `slice`, y afectará a los otros que apuntan al mismo array.
Ademas si no tiene una longitud suficiente para el slice (`(cap-len) == 0`), `append` nos va a devolver un nuevo array para este slice, en este punto,
los otros slices van a apuntan al anterior array nos serán afectados.
### map
`map` es como un diccionario en Python, lo usamos de la siguiente forma `map[claveTipo]tipoValor` para definirlo.
Vamos a ver algo de código, para configurar o tomar un valor de un `map` es como en un `slice`, usamos la `key` para ello, pero el indice en un `slice` puede ser
solo de tipo int, y en un `map` podemos usar mucho mas que eso, `int`, `string`, o lo que quieras. Ademas, todos ellos
pueden utilizar `==` y `!=` para comparar valores.
```
// usamos string tipo de clave (key), int como el tipo de valor, y debemos usar `make` para inicializarlo.
var numbers map[string] int
// otra forma de definir un map
numbers := make(map[string]int)
numbers["one"] = 1 // asignamos el valor para la clave
numbers["ten"] = 10
numbers["three"] = 3
fmt.Println("El tercer número es: ", numbers["three"]) // tomamos el valor
// Esto imprime: El tercer número es: 3
```
Algunas cosas a tener en cuenta cuando usamos map.
- `map` esta y es desordenado, cada vez que imprimamos `map` vamos a obtener diferentes resultados. Es imposible obtener un valor por índice, debe usar la clave.
- `map` no tiene una longitud fija, es un tipo de referencia al igual que los `slice`.
- `len` también funciona en `map`, este devuelve el número de claves que tiene el map.
- Es muy sencillo modificar el valor de un elemento del `map`, simplemente usamos `numbers["one"]=11` para cambiar el valor que contiene la clave one a `11`.
Se puede usar la forma `clave:valor` para inicializar los valores del map, y `map` tiene internamente los métodos para verificar si la clave existe.
Utilice `delete` para borrar un elemento del `map`.
```
// Inicialice un map
rating := map[string]float32 {"C":5, "Go":4.5, "Python":4.5, "C++":2 }
// map nos devuelve dos valores. El segundo valor ,ok es false, si la clave no
//existe true de otra forma.
csharpRating, ok := rating["C#"]
if ok {
fmt.Println("C# se encuentra en el map y su ranking es ", csharpRating)
} else {
fmt.Println("No tenemos un ranking asociado con C# en este map")
}
delete(rating, "C") // borramos el elemento con la clave "c"
```
Como se dijo anteriormente, `map` es un tipo por referencia, si dos `map`s apuntan a los mismos datos,
cualquier cambio van a afectar a ambos.
```
m := make(map[string]string)
m["Hello"] = "Bonjour"
m1 := m
m1["Hello"] = "Salut" // ahora el valor de m["hello"] es Salut
```
### make, new
`make` realiza la asignación de memoria para construir las estructuras como los `map`, `slice`, o `channel`, `new`
es para la reserva de memoria de cada tipo.
`new(T)` reservamos la memoria para el valor vacío del tipo `T`'s, devuelve la dirección de memoria, que es el valor del tipo `*T`. En los términos de Go,
este devuelve un puntero, que apunta al valor vacío del tipo `T`.
`new` devuelve punteros.
La función incorporada `make(T, args)` tiene un diferente efecto que `new(T)`, `make` puede ser usado para `slice`, `map`,
y `channel`, y nos devuelve un valor inicializado con valor inicial del tipo `T`. La razón para hacer esto es porque estos tres tipos
por debajo deben ser inicializados antes que los punteros a ellos. Por ejemplo, un `slice` contiene punteros que por debajo apuntan a
un `array`, longitud y capacidad. Antes de que esta información sea inicializada, un `slice` es `nil`, entonces para un `slice`, `map`,
`channel`, `make` inicializa los datos que tienen por debajo, y asigna algunos valores configurables.
`make` devuelve valores distinto de cero.
La siguiente imagen nos muestra como son diferentes `new` y `make`.
![](images/2.2.makenew.png?raw=true)
Imagen 2.5 Reserva de memoria de fondo de make y new
En cuando a valor cero, no significa valor vacío. Es el valor de las variables cuando no son asignados de forma manual, usualmente es 0, esta es la lista de algunos de estos valores.
```
int 0
int8 0
int32 0
int64 0
uint 0x0
rune 0 // el tipo actual de rune es int32
byte 0x0 // el tipo actual de byte es uint8
float32 0 // la longitud es 4 byte
float64 0 // la longitud es 8 byte
bool false
string ""
```
## Enlaces
- [Indice](preface.md)
- Sección anterior: ["Hello, Go"](02.1.md)
- Siguiente sección: [Sentencias de control y funciones](02.3.md)
# 2.3 Sentencias de control y funciones
En esta sección, vamos a hablar sobre sentencias de control de flujo y operaciones con funciones en Go.
## Sentencias de control
El mayor invento en los lenguajes de programación es el control de flujo. Gracias a ellos, podemos utilizar algunas sentencias sencillas de control para representar lógicas complejas. Tenemos tres categorías, condicionales, controles de ciclos y saltos incondicionales.
### if
`if` es la más común de las palabras reservadas en sus programas. Si se cumplen las condiciones entonces realiza algo, si no realiza otra cosa o nada.
`if` no necesita paréntesis en Go.
```
if x > 10 {
fmt.Println("x es mayor que 10")
} else {
fmt.Println("x es menor que 10")
}
```
Lo más útil de `if` en Go es que puede tener una instrucción de inicialización antes de la sentencia de condición. El alcance o ámbito de las variables de inicialización que nosotros definimos en esta condición es unicamente dentro del bloque del `if`.
```
// inicializamos x, entonces verificamos si x es mayor que 10
if x := computedValue(); x > 10 {
fmt.Println("x es mayor que 10")
} else {
fmt.Println("x es menor que 10")
}
// el siguiente código no va a compilar
fmt.Println(x)
```
Utilice if-else para múltiples condiciones.
```
if entero == 3 {
fmt.Println("entero es igual a 3")
} else if entero < 3 {
fmt.Println("entero es menor que 3")
} else {
fmt.Println("entero es mayor que 3")
}
```
### goto
Go la palabra reservada `goto`, sea cuidadoso cuando la usa. `goto` tiene un salto hacia el `label` hacia el cuerpo de el mismo bloque de código.
```
func myFunc() {
i := 0
Here: // label termina con ":"
fmt.Println(i)
i++
goto Here // salta hacia el label "Here"
}
```
El nombre del label diferencia entre mayúsculas y minúsculas.
### for
`for` es la lógica de control mas poderosa en Go, puede leer los datos en los bucles y realizar operaciones iterativas, al igual que un típico `while`.
```
for expression1; expression2; expression3 {
//...
}
```
`expression1`, `expression2` y `expression3` son obviamente todas expresiones, donde `expression1` y `expression3` son definiciones de variables o valores de retornos de funciones, y `expression2` es una sentencia condicional. `expression1` se ejecutara siempre antes de cada bucle, y `expression3` después.
Un ejemplo es mas útil que cientos de palabras.
```
package main
import "fmt"
func main(){
sum := 0;
for index:=0; index < 10 ; index++ {
sum += index
}
fmt.Println("la suma es igual a ", sum)
}
// Printsum es igual a 45
```
A veces necesitamos asignaciones múltiples, Go tiene el operador `,`, así que podemos usar la asignación paralela como `i, j = i + 1, j - 1`.
Si no son necesarios, podemos omitir a `expression1` y `expression3`.
```
sum := 1
for ; sum < 1000; {
sum += sum
}
```
Podemos omitir también el `;`. Se siente familiar? Si, es un `while`.
```
sum := 1
for sum < 1000 {
sum += sum
}
```
Hay dos operaciones importantes en los bucles que son `break` y `continue`. `break` salta afuera del bucle, y `continue` salta el bucle actual y continua en el siguiente. Si usted anida bucles, utilice `break` para saltar al bucle que esta junto.
```
for index := 10; index>0; index-- {
if index == 5{
break // o continue
}
fmt.Println(index)
}
// break imprime 10、9、8、7、6
// continue imprime 10、9、8、7、6、4、3、2、1
`for` podría leer los datos desde un `slice` o un `map` cuando es utilizado con `range`.
for k,v:=range map {
fmt.Println("map's key:",k)
fmt.Println("map's val:",v)
}
```
Como Go soporta el retorno de valores múltiples y nos da errores de compilación cuando utiliza valores que no fueron definidos, por eso puede necesitar utilizar `_` para descartar algunos valores de retorno.
```
for _, v := range map{
fmt.Println("map's val:", v)
}
```
### switch
A veces puede pensar que esta utilizando demasiados valores `if-else` para implementar alguna lógica, también puede pensar que no se ve bien y que no sea correcto para mantener a futuro. Ahora es tiempo para utilizar `switch` para resolver este problema.
```
switch sExpr {
case expr1:
algunas instrucciones
case expr2:
algunas otras instrucciones
case expr3:
algunas otras instrucciones
default:
otro código
}
```
El tipo de `sExpr`, `expr1`, `expr2`, y `expr3` debe ser el mismo. `switch` es muy flexible, las condiciones no necesitan ser constantes, es ejecutado de arriba hacia abajo hasta que se cumpla alguna condición. Si no hay ninguna declaración después de la palabra reservada `switch`, entonces este se compara con `true`.
```
i := 10
switch i {
case 1:
fmt.Println("i es igual a 1")
case 2, 3, 4:
fmt.Println("i es igual a 2, 3 o 4")
case 10:
fmt.Println("i es igual a 10")
default:
fmt.Println("Todo lo que yo se, es que i es un entero")
}
```
En la quinta linea, pusimos muchos valores en un solo `case`, y no necesitamos utilizar `break` en el final del cuerpo de un `case`. Saltara fuera del cuerpo de switch una vez que coincida con algún case y ejecute las instrucciones dentro del mismo. Si usted busca que siga comparando con otros cases, va a necesitar utilizar la palabra reservada `fallthrough`.
```
integer := 6
switch integer {
case 4:
fmt.Println("integer <= 4")
fallthrough
case 5:
fmt.Println("integer <= 5")
fallthrough
case 6:
fmt.Println("integer <= 6")
fallthrough
case 7:
fmt.Println("integer <= 7")
fallthrough
case 8:
fmt.Println("integer <= 8")
fallthrough
default:
fmt.Println("default case")
}
```
Este programa va a imprimir la siguiente información.
```
integer <= 6
integer <= 7
integer <= 8
default case
```
## Funciones
Utilizamos la palabra reservada `func` para definir funciones.
```
func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
// cuerpo de la función
// retorna múltiples valores
return value1, value2
}
```
Podemos obtener la siguiente información del ejemplo anterior.
- Utilizamos la palabra reservada `func` para definir la función llamada `funcName`.
- Funciones tienen cero, uno o mas de un argumento, el tipo del argumento después del nombre del mismo y separados por `,`.
- Las funciones pueden devolver múltiples valores.
- Tiene dos valores de retorno llamados `output1` y `output2`, se pueden omitir los nombre y utilizar unicamente los tipos.
- Si solo hay un valor de retorno y omite el nombre, no va a necesitar comas para retornar mas valores.
- Si la función no tiene valores de retorno, puede omitir la parte de retorno.
- Si la función tiene valores de retorno, va a necesitar utilizar `return` en alguna parte del cuerpo de la función.
Veamos un ejemplo práctico. (calcular el valor mínimo)
```
package main
import "fmt"
// devolvemos el valor mas grande entre a y b
func max(a, b int) int {
if a > b {
return a
}
return b
}
func main() {
x := 3
y := 4
z := 5
max_xy := max(x, y) // llama a la función max(x, y)
max_xz := max(x, z) // llama a la función max(x, z)
fmt.Printf("max(%d, %d) = %d\n", x, y, max_xy)
fmt.Printf("max(%d, %d) = %d\n", x, z, max_xz)
fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // llamamos a la función aquí
}
```
En el ejemplo anterior, tenemos dos argumentos en la función `max`, los de tipo `int`, por eso el primer tipo puede ser omitido, como `a, b int` en lugar de `a int, b int`. Se cumple la misma regla para mas argumentos. Nótese que `max` tiene solo un valor de retorno, por lo que solo escribimos el tipo de valor de retorno, esta es una forma corta.
### Retorno de múltiples valores
Una de las cosas en las que Go es mejor que C es que soporta el retorno de múltiples valores.
Vamos a utilizar el ejemplo anterior aquí.
```
package main
import "fmt"
// retorna el resultado de A + B y A * B
func SumAndProduct(A, B int) (int, int) {
return A+B, A*B
}
func main() {
x := 3
y := 4
xPLUSy, xTIMESy := SumAndProduct(x, y)
fmt.Printf("%d + %d = %d\n", x, y, xPLUSy)
fmt.Printf("%d * %d = %d\n", x, y, xTIMESy)
}
```
En el ejemplo anterior devolvemos dos valores sin nombre, y usted también puede nombrarlos. Si nombramos los valores de retornos, solo vamos a utilizar `return` para devolver para devolver bien los valores ya que se inicializan en la función automáticamente. Debe tener en cuenta que si sus funciones se van a utilizar fuera del paquete, lo que significa que los nombre de las funciones inician con mayúsculas, es mejor que escriba la sentencia mas completa para `return`; esto hace que es código sea mas legible.
```
func SumAndProduct(A, B int) (add int, Multiplied int) {
add = A+B
Multiplied = A*B
return
}
```
### Argumentos variables
Go soporta funciones con un número variable de argumentos. Estas funciones son llamadas "variadic", lo que significa que puede darle un numero incierto de argumentos a la función.
```
func myfunc(arg ...int) {}
```
`arg …int` le dice a Go que esta función tiene argumentos variables. Tenga en cuenta que estos argumentos son de tipo `int`. En el cuerpo de la función , `arg` será un `slice` de `int`.
```
for _, n := range arg {
fmt.Printf("Y el número es: %d\n", n)
}
```
### Pasando argumentos por valor y punteros
Cuando pasamos argumentos a una función a la cual llamamos, esa función actualmente toma una copia de nuestra variables, cualquier cambio que realicemos no va a afectar a la variable original.
Vamos a ver un ejemplo para probar lo que decimos.
```
package main
import "fmt"
// función sencilla para sumar 1 a 'a'
func add1(a int) int {
a = a+1 // cambiamos el valor de a
return a // devolvemos el nuevo valor de a
}
func main() {
x := 3
fmt.Println("x = ", x) // debe imprimir "x = 3"
x1 := add1(x) // llamamos a add1(x)
fmt.Println("x+1 = ", x1) // debe imprimir "x+1 = 4"
fmt.Println("x = ", x) // debe imprimir "x = 3"
}
```
¿Usted vio eso? Siempre que llamamos a `add1`, y `add1` le suma uno a `a`, el valor de `x` no sufre cambios.
El motivo de esto es muy sencillo: cuando llamamos a `add1`, nosotros obtenemos una copia de `x` para esto, no `x` en si mismo.
Ahora nos debemos preguntar, como puedo pasar el verdadero valor de `x` a la función.
Para eso necesitamos utilizar punteros. Sabemos que las variables son almacenadas en memoria, y todas ellas tienen una dirección de memoria, nosotros vamos a cambiar el valor de esa variables si cambiamos los valores en la dirección de memoria de esa variable. Por lo tanto la función `add1` tiene que saber la dirección de memoria de `x` para poder cambiar su valor. Por eso necesitamos pasar de la siguiente forma su valor `&x` a la función, y cambiar el tipo de argumento a uno de tipo puntero `*int`. Tiene que ser consciente de que pasamos una copia del puntero, no una copia del valor.
```
package main
import "fmt"
// función sencilla para sumar 1 a a
func add1(a *int) int {
*a = *a+1 // cambiamos el valor de a
return *a // devolvemos el nuevo valor de a
}
func main() {
x := 3
fmt.Println("x = ", x) // debe imprimir "x = 3"
x1 := add1(&x) // llamamos a add1(&x) pasando la dirección de memoria de x
fmt.Println("x+1 = ", x1) // debe imprimir "x+1 = 4"
fmt.Println("x = ", x) // debe imprimir "x = 4"
}
```
Ahora podemos cambiar el valor de `x` en la función. Porque usamos punteros? Cual es la ventaja?
- Usamos mas funciones que modifiquen una misma variable.
- Tiene un bajo costo utilizar direcciones de memoria (8 bytes), la copia no es una forma eficiente tanto en el tiempo como en el espacio para pasar variables.
- `string`, `slice`, `map` son tipos de referenciados, por eso ellos por defecto utilizan punteros cuando se pasan a funciones. (Atención: si necesitas cambiar el tamaño de un `slice`, vas a necesitar pasar el puntero de forma explicita)
### defer
Go tiene un muy buen diseño llamado `defer`, uno puede tener muchas declaraciones de `defer` en una función; ellos se ejecutan en orden inverso cuando el programa ejecuta el final de la función. Especialmente cuando el programa abre un recurso como un archivo, estos archivos tienen que ser cerrados antes de que la función devuelva un error. Vamos a ver algún ejemplo.
```
func ReadWrite() bool {
file.Open("file")
// realizamos alguna tarea
if failureX {
file.Close()
return false
}
if failureY {
file.Close()
return false
}
file.Close()
return true
}
```
Vimos la repetición de código en varias ocasiones, `defer` nos va a resolver muy bien este problema. No solo nos va a ayudar a realizar código limpio, y también lo va a hacer mas legible.
```
func ReadWrite() bool {
file.Open("file")
defer file.Close()
if failureX {
return false
}
if failureY {
return false
}
return true
}
```
Si hay mas de un `defer`, ellos se van a ejecutar en orden inverso. El siguiente ejemplo va a imprimir `4 3 2 1 0`.
```
for i := 0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
```
### Funciones como valores y tipos
Las funciones son también variables en Go, podemos usar un `type` para definirlas. Las funciones que tienen la misma firma pueden verso como del mismo tipo.
```
type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...])
```
Cual es la ventaja de esta característica? Entonces podemos pasar funciones como valores.
```
package main
import "fmt"
type testInt func(int) bool // definimos un función como un tipo de variable
func isOdd(integer int) bool {
if integer%2 == 0 {
return false
}
return true
}
func isEven(integer int) bool {
if integer%2 == 0 {
return true
}
return false
}
// pasamos la función `f` como un argumento de otra función
func filter(slice []int, f testInt) []int {
var result []int
for _, value := range slice {
if f(value) {
result = append(result, value)
}
}
return result
}
func main(){
slice := []int {1, 2, 3, 4, 5, 7}
fmt.Println("slice = ", slice)
odd := filter(slice, isOdd) // usamos la función como un valor
fmt.Println("Odd elements of slice are: ", odd)
even := filter(slice, isEven)
fmt.Println("Even elements of slice are: ", even)
}
```
Es muy útil cuando usamos interfaces. Como puede ver `testInt` es una variable que tiene como tipo una función, y devuelve valores y el argumento de `filter` es el mismo que el de `testInt`. Por lo tanto, tenemos una lógica mas compleja en nuestro programa, y hacemos nuestro código mas flexible.
### Panic y Recover
Go no tiene estructura `try-catch` como lo tiene Java. En vez de lanzar excepciones, Go usa `panic` y `recover` para hacer frente a los errores. Sin embargo, no debería usar mucho `panic`, aunque sea muy poderoso.
Panic es una función incorporada para romper el flujo normal del programa y entrar en un estado de pánico. Cuando la función `F` llama a `panic`, la función `F` no continuara ejecutándose, pero sus funciones `defer` siempre se ejecutaran. Entonces `F` vuelve a su punto de ruptura donde se causo el estado de pánico. El programa no va a finalizar hasta que todas las funciones retornen con `panic` hasta el primer nivel de esa `goroutine`. `panic` se puede producir con una llamada a `panic` en el programa, y algunos errores pueden causar una llamada a `panic` como un intento de acceso fuera de un array.
Recover es una función incorporada para recuperar una `goroutine` de un estado de pánico, solo el llamado a `recover` es útil en una función `defer` porque las funciones normales no van a ser ejecutadas cuando el programa se encuentre en un estado de pánico. Vamos a poder atrapar el valor de `panic` si el programa se encuentra en un estado de pánico, este nos va a devolver `nil` si el programa se encuentra en un estado normal.
El siguiente ejemplo nos muestra como utilizar `panic`.
```
var user = os.Getenv("USER")
func init() {
if user == "" {
panic("no hay valor para $USER")
}
}
```
El siguiente ejemplo nos muestra como verificar un `panic`.
```
func throwsPanic(f func()) (b bool) {
defer func() {
if x := recover(); x != nil {
b = true
}
}()
f() // si f causa un panic, este va a recuperase (recover)
return
}
```
### La función `main` y la función `init`
Go tiene dos retenciones (retention) que son llamadas `main` e `init`, donde `init` puede ser usada en todos los paquetes y `main` solo puede ser usada en el paquete `main`. Estas dos funciones no son capaces de tener argumentos o valores de retorno. A pesar de que podemos escribir muchas funciones `init` en un mismo paquete, yo recomiendo fuertemente que escriban solo una función `init` por cada paquete.
Los programas en Go van a llamar a `init()` y a `main()` automáticamente, así que no es necesario llamarlas. Para cada paquete, la función `init` es opcional, pero `package main` tiene una y solo una función `main`.
Los programas se inicializan y se ejecutan desde el paquete `main`, si el paquete `main` importa otros paquetes, ellos serán importados en tiempo de compilación. Si un paquete es importado muchas veces, este va a ser compilado solo una vez. Después de importar los paquetes, el programa va a inicializar las constantes y variables en los paquetes importados, luego va a ejecutar la función `init` si es que existe, y así sucesivamente. Después de que todos los paquetes fueran inicializados, el programa va a comenzar a inicializar las constantes y variables en el paquete `main`, entonces va a ejecutar la función `init` en el paquete si es que existe. La siguiente figura les va a mostrar el proceso.
![](images/2.3.init.png?raw=true)
Figure 2.6 Flujo de inicialización de un programa en Go
### import
Nosotros usamos `import` muy frecuentemente en los programas en Go como se muestra acá.
```
import(
"fmt"
)
```
Entonces nosotros podemos usar las funciones de ese paquete de la siguiente forma.
fmt.Println("Hola mundo")
`fmt` es parte de la librería estándar de Go , esta localizado en $GOROOT/pkg. Go utiliza las siguiente dos formas para paquete de terceros.
1. Path relativo
import "./model" // carga el paquete que se encuentra en el mismo directorio, yo no recomiendo utilizar esta forma.
2. Path absoluto
import "shorturl/model" // carga un paquete en el path "$GOPATH/pkg/shorturl/model"
Tenemos algunos operadores especiales para importar paquetes, y los principiantes normalmente se confunden con estos operadores.
1. EL operador punto .
A veces podemos ver personas que utilizan la siguiente forma para importar paquetes.
import(
. "fmt"
)
El operador punto significa que podemos omitir el nombre del paquete cuando llamamos a las funciones del mismo. En vez de esto `fmt.Printf("Hola mundo")` ahora podemos usar esto `Printf("Hola mundo")`.
2. El operador alias.
Este puede cambiar el nombre del paquete que vamos a importar cuando llamamos a las funciones del mismo.
import(
f "fmt"
)
En vez de esto `fmt.Printf("Hola mundo")` ahora podemos usar esto `f.Printf("Hola mundo")`.
3. El operador `_`.
Este es un operador un poco difícil de comprender si no tenemos una breve explicación.
import (
"database/sql"
_ "github.com/ziutek/mymysql/godrv"
)
El operador `_` en realidad significa que solo importamos ese paquete, y usamos la función `init` de ese paquete, y no estamos seguros si vamos a utilizar funciones de dicho paquete.
## Enlaces
- [Indice](preface.md)
- Sección anterior: [Principios de Go](02.2.md)
- Siguiente sección: [struct](02.4.md)
# 2.4 struct
## struct
Podemos definir en Go nuevos tipos de contenedores con otras propiedades o campos como en otros lenguajes de programación. Por ejemplo, podemos crear el tipo llamado `persona` para representar una persona, este tipo tiene nombre y edad. Podemos llamar estos tipos de tipos como `struct`.
```
type persona struct {
nombre string
edad int
}
```
Mira que fácil es definir un `struct`!
Tiene dos campos.
- `nombre` es una `string` usada para guardar el nombre de personas.
- `edad` es un `int` usado para guardar la de edad de personas.
Vamos a ver como usarlo.
```
type persona struct {
nombre string
edad int
}
```
var P persona // p es de tipo persona
P.nombre = "Astaxie" // asigna "Astaxie" al campo 'nombre' de p
P.edad = 25 // asigna 25 al campo 'edad' de p
fmt.Printf("El nombre de la persona es %s\n", P.name) // accedemos al campo 'nombre' de p
Tenemos tres formas mas de definir un struct.
- Asignando un valor inicial en forma ordenada
P := persona{"Tom", 25}
- Usando el formato `campo:valor` para inicializarlo sin orden
P := persona{edad:24, nombre:"Bob"}
- Definimos una struct anónima, y la inicializamos
P := struct{nombre string; edad int}{"Amy",18}
Vamos a ver un ejemplo completo.
```
package main
import "fmt"
// definimos un tipo nuevo
type persona struct {
nombre string
edad int
}
// comparamos la edad de dos personas, y devolvemos la mas vieja con la
// diferencia, struct es pasado por valor
func Older(p1, p2 persona) (persona, int) {
if p1.edad>p2.edad {
return p1, p1.edad-p2.edad
}
return p2, p2.edad-p1.edad
}
func main() {
var tom persona
// inicialización
tom.nombre, tom.edad = "Tom", 18
// inicializamos los dos valores con el formato "campo:valor"
bob := persona{edad:25, nombre:"Bob"}
// inicializamos los dos valores en orden
paul := persona{"Paul", 43}
tb_Older, tb_diff := Older(tom, bob)
tp_Older, tp_diff := Older(tom, paul)
bp_Older, bp_diff := Older(bob, paul)
fmt.Printf("De %s y %s, %s es mas viejo por %d años\n", tom.nombre, bob.nombre , tb_Older.nombre , tb_diff)
fmt.Printf("De %s y %s, %s es mas viejo por %d años\n", tom.nombre, paul.nombre, tp_Older.nombre, tp_diff)
fmt.Printf("De %s y %s, %s es mas viejo por %d años\n", bob.nombre, paul.nombre, bp_Older.nombre, bp_diff)
}
```
### Campos incrustados en un struct
Solo les mostré como definir struct con campos que tienen nombre y tipo. De hecho, Go soporta campos sin nombre pero si con tipo, vamos a llamar a estos campos incrustados.
Cuando el campo incrustado es un struct, todos los campos de ese struct serán campos del nuevo struct de forma implícita.
Vamos a ver un ejemplo.
```
package main
import "fmt"
type Human struct {
name string
age int
weight int
}
type Student struct {
Human // campo incrustado, esto significa que el struct Student va a incluir los campos que tiene Human.
speciality string
}
func main() {
// inicializamos a student
mark := Student{Human{"Mark", 25, 120}, "Computer Science"}
// campos accesibles
fmt.Println("Su nombre es ", mark.name)
fmt.Println("Su edad es ", mark.age)
fmt.Println("Su peso es ", mark.weight)
fmt.Println("Su especialidad es ", mark.speciality)
// modificamos un campo
mark.speciality = "AI"
fmt.Println("Mark cambio su especialidad")
fmt.Println("Su especialidad es ", mark.speciality)
// modificamos su edad
fmt.Println("Mark esta mas viejo")
mark.age = 46
fmt.Println("Su edad es ", mark.age)
// modificamos su peso
fmt.Println("Mark ya no es mas un atleta")
mark.weight += 60
fmt.Println("Su peso es ", mark.weight)
}
```
![](images/2.4.student_struct.png?raw=true)
Figure 2.7 Herencia en Student y Human
Vemos que accedemos a la edad y nombre en Student de la misma forma que lo hacemos con Human. Así es como funcionan los campos incrustados. Es muy útil “cool”, no lo es? Espera, hay algo todavía mas “cool”! Puede utilizar a Student para acceder a los campos incrustados de Human!
mark.Human = Human{"Marcus", 55, 220}
mark.Human.age -= 1
Todos los tipos pueden ser utilizados como campos incrustados.
```
package main
import "fmt"
type Skills []string
type Human struct {
name string
age int
weight int
}
type Student struct {
Human // struct como un campo incrustado
Skills // string como un campo incrustado
int // usamos un tipo embebido como un campo incrustado
speciality string
}
func main() {
// inicializamos al Student Jane
jane := Student{Human:Human{"Jane", 35, 100}, speciality:"Biology"}
// accedemos a sus campos
fmt.Println("Su nombre es ", jane.name)
fmt.Println("Su edad es , jane.age)
fmt.Println("Su peso es ", jane.weight)
fmt.Println("Su especialidad es ", jane.speciality)
// modificamos el valor del campo skill
jane.Skills = []string{"anatomy"}
fmt.Println("Sus habilidades son ", jane.Skills)
fmt.Println("Ella adquirió don habilidades mas ")
jane.Skills = append(jane.Skills, "physics", "golang")
fmt.Println("Sus habilidades ahora son ", jane.Skills)
// modificamos un campo embebido
jane.int = 3
fmt.Println("Su numero preferido es ", jane.int)
}
```
En el ejemplo anterior, podemos ver que a todos los tipos se les pueden incrustar campos y podemos utilizar funciones para operarlos.
Hay un problema mas, si Human tiene un campo llamado `phone` y Student tiene otro campo llamado con el mismo nombre, que deberíamos hacer?
Go utiliza una forma muy sencilla para resolverlo. Los campos exteriores consiguen accesos superiores, lo que significa, es que cuando se accede a `student.phone`, obtendremos el campo llamado phone en student, no en el struct de Human. Esta característica se puede ver simplemente como una `sobrecarga` de campos.
```
package main
import "fmt"
type Human struct {
name string
age int
phone string // Human tiene el campo phone
}
type Employee struct {
Human // campo embebido Human
speciality string
phone string // phone en employee
}
func main() {
Bob := Employee{Human{"Bob", 34, "777-444-XXXX"}, "Designer", "333-222"}
fmt.Println("El teléfono del trabajo de Bob es:", Bob.phone)
// accedemos al campo phone en Human
fmt.Println("El teléfono personal de Bob es:", Bob.Human.phone)
}
```
## Enlaces
- [Indice](preface.md)
- Sección anterior: [Sentencias de control y funciones](02.3.md)
- Siguiente sección: [Orientado a objetos](02.5.md)
# Orientado a objetos
En las ultimas dos secciones hablamos de funciones y struct, alguna vez pensaste en usar funciones como campos de un struct? En esta sección, voy a realizar una introducción a otro tipo de funciones , que vamos a llamar métodos (`method`).
## Métodos
Supongamos que definimos un struct del estilo rectángulo(rectangle), y usted busca calcular el área, por lo general vamos a usar el siguiente código para lograr este objetivo.
```
package main
import "fmt"
type Rectangle struct {
width, height float64
}
func area(r Rectangle) float64 {
return r.width*r.height
}
func main() {
r1 := Rectangle{12, 2}
r2 := Rectangle{9, 4}
fmt.Println("El área de r1 es: ", area(r1))
fmt.Println("El área de r2 is: ", area(r2))
}
```
En el ejemplo de arriba podemos calcular el área de un rectángulo, usamos la función llamada `area`, pero no es un método de un struct “rectangle” (como un método en un clase en un lenguaje orientado a objeto clásico). La función y el struct como se ve son dos cosas independientes.
Hasta ahora eso no es un problema. Pero que pasa si quiere calcular el área de un circulo, cuadrado, pentágono, u otra figura, vamos a tener que agregar mas funciones con nombres muy similares.
![](images/2.5.rect_func_without_receiver.png?raw=true)
Figure 2.8 Relación entre funciones y struct
Obviamente, no es “cool”. Además el área debería ser propiedad del circulo o el rectángulo.
Por estas razones, tenemos conceptos sobre `métodos`. `métodos` es afiliado a un tipo, tienen la misma sintaxis que una función excepto por una palabra más después de la palabra reservada `func` que es llamada receptor (`receiver`) que es el cuerpo principal de ese método.
Utilice el mismo ejemplo, `Rectangle.area()` pertenece al rectángulo, no como una función periférica. Mas específicamente, `length`, `width` y `area()` todos pertenecen a rectángulo.
Como dice Rob Pike.
"Un método es una función con un primer argumento implícito, llamado receiver."
Sintaxis de un método.
```
func (r ReceiverType) funcName(parameters) (results)
```
Cambiemos el ejemplo anterior para utilizar métodos.
```
package main
import (
"fmt"
"math"
)
type Rectangle struct {
width, height float64
}
type Circle struct {
radius float64
}
func (r Rectangle) area() float64 {
return r.width*r.height
}
func (c Circle) area() float64 {
return c.radius * c.radius * math.Pi
}
func main() {
r1 := Rectangle{12, 2}
r2 := Rectangle{9, 4}
c1 := Circle{10}
c2 := Circle{25}
fmt.Println("El área de r1 es: ", r1.area())
fmt.Println("El área de r2 es: ", r2.area())
fmt.Println("El área de c1 es: ", c1.area())
fmt.Println("El área de c2 es: ", c2.area())
}
```
Notas para el uso de métodos.
- Si el nombre de un método es el mismo, pero no tiene el mismo receptor, ellos no son iguales.
- Los métodos son capaces de acceder a los campos en los receptores.
- Usamos el `.` para llamar al método en el struct, al igual que cuando llamamos a un campo.
![](images/2.5.shapes_func_with_receiver_cp.png?raw=true)
Figure 2.9 Los métodos son diferentes en diferentes struct
En el ejemplo anterior, el método area() es respectivamente perteneciente a Rectangle y a Circle, por lo que los receptores son Rectangle y Circle.
Una cosa es importante notar, que un método con una línea de puntos significa que el receptor se pasa por valor, no por referencia. La diferencia entre ellos es que el método podría cambiar el valor del receptor cuando el mismo es pasado por referencia, y este toma una copia del receptor cuando es pasado por valor.
¿El receptor puede ser unicamente un struct? Por supuesto que no, cualquier tipo podría ser el receptor en un método. Puede ser un poco confuso cuando hablamos de tipos personalizados(“customized type”), struct es un tipo especial de tipo modificado, hay varios tipos personalizado.
Utilice el siguiente formato para definir un tipo personalizado.
```
type typeName typeLiteral
```
Un ejemplo sobre tipos personalizados.
```
type ages int
type money float32
type months map[string]int
m := months {
"January":31,
"February":28,
...
"December":31,
}
```
Espero que ahora sepa como utilizar los tipos personalizados. Es similar a `typedef` en C, nosotros usamos `ages` para sustituir a `int` en el ejemplo anterior.
Volvamos a los `métodos`.
Puede utilizar tantos métodos en tipos personalizados como desees.
```
package main
import "fmt"
const(
WHITE = iota
BLACK
BLUE
RED
YELLOW
)
type Color byte
type Box struct {
width, height, depth float64
color Color
}
type BoxList []Box //un slice de boxes
func (b Box) Volume() float64 {
return b.width * b.height * b.depth
}
func (b *Box) SetColor(c Color) {
b.color = c
}
func (bl BoxList) BiggestsColor() Color {
v := 0.00
k := Color(WHITE)
for _, b := range bl {
if b.Volume() > v {
v = b.Volume()
k = b.color
}
}
return k
}
func (bl BoxList) PaintItBlack() {
for i, _ := range bl {
bl[i].SetColor(BLACK)
}
}
func (c Color) String() string {
strings := []string {"WHITE", "BLACK", "BLUE", "RED", "YELLOW"}
return strings[c]
}
func main() {
boxes := BoxList {
Box{4, 4, 4, RED},
Box{10, 10, 1, YELLOW},
Box{1, 1, 20, BLACK},
Box{10, 10, 1, BLUE},
Box{10, 30, 1, WHITE},
Box{20, 20, 20, YELLOW},
}
fmt.Printf("Tenemos %d boxes en nuestro conjunto\n", len(boxes))
fmt.Println("El volumen de la primera es ", boxes[0].Volume(), "cm³")
fmt.Println("El color de la última es",boxes[len(boxes)-1].color.String())
fmt.Println("La más grande es", boxes.BiggestsColor().String())
fmt.Println("Vamos a pintarlas a todas de negro")
boxes.PaintItBlack()
fmt.Println("El color de la segunda es", boxes[1].color.String())
fmt.Println("Obviamente, ahora, la más grande es", boxes.BiggestsColor().String())
}
```
Vamos a definir algunas constantes y tipos personalizados.
- Usamos `Color` como un alias de `byte`.
- Definimos un struct `Box` que tiene los campos height, width, length y color.
- Definimos un struct `BoxList` que tiene `Box` como sus campos.
Entonces vamos a definir algunos métodos para personalizar nuestros tipos.
- Volume() usa a Box como su receptor, devuelve el volumen de Box.
- SetColor(c Color) cambiar el color de las Box.
- BiggestsColor() devuelve el colo que tiene la de mayor volumen.
- PaintItBlack() configuramos el color para todas las Box en BoxList a negro.
- String() usamos Color como su receptor, devolvemos un string con formato de el nombre del color.
Es mucho mas claro cuando usamos palabras para describir nuestro requerimientos? Usualmente escribimos nuestros requerimientos antes de comenzar a programar.
### Utilizar punteros como un receptor
Vamos a mirar el método `SetColor`, su receptor es un puntero de tipo Box. Si, podes usar `*Box` como receptor. Porque usaríamos un puntero acá? Porque buscamos cambiar el color de Box en este método, si no usamos un puntero, solo cambiaría el valor de la copia de Box.
Si vemos el receptor como el primer argumento de los métodos, no es difícil de entender como funcionan estos.
Podría decir que deberíamos usar `*b.Color=c` en vez de `b.Color=c` en el método SetColor(). Cualquiera de los dos estaría bien, porque Go lo sabe interpretar. ¿Ahora crees que Go es mas fascinante?
También podría decir que deberíamos usar `(&bl[i]).SetColor(BLACK)` en `PaintItBlack` porque le pasamos un puntero a `SetColor`. Una vez más, cualquiera de los dos esta bien, porque Go sabe interpretarlo correctamente!
### Herencia de métodos
Aprendimos sobre la herencia de campos en la última sección, también tenemos la herencia de métodos en Go. Así que si un campo anónimo tiene métodos, el struct que contiene el campo tendrá todos los métodos del mismo.
```
package main
import "fmt"
type Human struct {
name string
age int
phone string
}
type Student struct {
Human // campo anónimo
school string
}
type Employee struct {
Human
company string
}
// definimos un método en Human
func (h *Human) SayHi() {
fmt.Printf("Hola, Yo soy %s puedes llamarme al %s\n", h.name, h.phone)
}
func main() {
mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
mark.SayHi()
sam.SayHi()
}
```
### Sobrecarga de métodos
Si queremos que Employee tenga su propio método llamado `SayHi`, podemos definir el método con el mismo nombre que el de Employee, y así ocultaremos `SayHi` de Human cuando lo llamemos.
```
package main
import "fmt"
type Human struct {
name string
age int
phone string
}
type Student struct {
Human
school string
}
type Employee struct {
Human
company string
}
func (h *Human) SayHi() {
fmt.Printf("Hola, Yo soy %s podes llamarme al %s\n", h.name, h.phone)
}
func (e *Employee) SayHi() {
fmt.Printf("Hola, Yo soy %s, trabajo en %s. Podes llamarme al %s\n", e.name,
e.company, e.phone) //Si, lo podes cortar en dos líneas.
}
func main() {
mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
mark.SayHi()
sam.SayHi()
}
```
Ahora usted es capaz de escribir programas que utilicen el paradigma Orientado a Objetos, los métodos utilizan la regla de que los que tienen nombre que se inician con mayúsculas van a ser públicos o privados en caso contrario.
## Enlaces
- [Indice](preface.md)
- Sección anterior: [struct](02.4.md)
- Siguiente sección: [interfaces](02.6.md)
# 2.6 Interfaces
## Interfaces
Una de las características de diseño mas sutiles en Go son las interfaces. Después de leer esta sección, probablemente quedara impresionado por su implementación.
### Que es una interfaz?
En pocas palabras, una interfaz es un conjunto de métodos, que se usan para definir un conjunto de acciones.
Al igual que en los ejemplos de las secciones anteriores, ambos, Student y Employee pueden llamar a `SayHi()`, pero ellos no hacen lo mismo.
Vamos a trabajar un poco mas, vamos a agregarle a ellos el método `Sing()`, y también vamos a agregar `BorrowMoney()` a Student y `SpendSalary()` a Employee.
Ahora Student tiene tres métodos llamados `SayHi()`, `Sing()`, `BorrowMoney()`, y Employee tiene `SayHi()`, `Sing()` y `SpendSalary()`.
Esta combinación de métodos es llamada interfaz, y es implementada por Student y Employee. Entonces Student y Employee implementan la interfaz: `SayHi()`, `Sing()`. Al mismo tiempo, Employee no implementa la interfaz: `SayHi()`, `Sing()`, `BorrowMoney()`, y Student no implementa la interfaz: `SayHi()`, `Sing()`, `SpendSalary()`. Esto es porque Employee no tiene el método `BorrowMoney()` y Student no tiene el método `SpendSalary()`.
### Tipo de Interfaces
Una interfaz define un grupo de métodos, entonces si un tipo implementa todos los métodos entonces nosotros decimos que ese tipo implementa la interfaz.
```
type Human struct {
name string
age int
phone string
}
type Student struct {
Human
school string
loan float32
}
type Employee struct {
Human
company string
money float32
}
func (h *Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func (h *Human) Sing(lyrics string) {
fmt.Println("La la, la la la, la la la la la...", lyrics)
}
func (h *Human) Guzzle(beerStein string) {
fmt.Println("Guzzle Guzzle Guzzle...", beerStein)
}
// Employee sobrescribe Sayhi
func (e *Employee) SayHi() {
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
e.company, e.phone) //Si acá podes cortarlo en 2 líneas.
}
func (s *Student) BorrowMoney(amount float32) {
s.loan += amount // (otra vez...)
}
func (e *Employee) SpendSalary(amount float32) {
e.money -= amount
}
// definimos algunas interfaces
type Men interface {
SayHi()
Sing(lyrics string)
Guzzle(beerStein string)
}
type YoungChap interface {
SayHi()
Sing(song string)
BorrowMoney(amount float32)
}
type ElderlyGent interface {
SayHi()
Sing(song string)
SpendSalary(amount float32)
}
```
Sabemos que una interfaz puede ser implementada por cualquier tipo, y un tipo puede implementar muchas interfaces al mismo tiempo.
Tenga en cuenta que todos los tipos implementan la interfaz vacía `interface{}` porque esta no tiene ningún método y todos los tipos tienen cero métodos por defecto.
### Valor de una interfaz
Entonces, que tipo de valores se puede poner en una interfaz? Si definimos una variable como tipo interfaz, cualquier tipo que implemente la interfaz puede asignarse a esta variable.
Al igual que en el ejemplo anterior, si definimos una variable m como la interfaz Men, entonces cualquier Student, Human o Employee puede ser asignado a m. Entonces podríamos tener una lista de Men, y cualquier tipo que implemente la interfaz Men puede agregarse a esa lista (slice). Sin embargo sea consciente que una lista de interfaces no tiene el mismo comportamiento que una lista de otros tipos.
```
package main
import "fmt"
type Human struct {
name string
age int
phone string
}
type Student struct {
Human
school string
loan float32
}
type Employee struct {
Human
company string
money float32
}
func (h Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func (h Human) Sing(lyrics string) {
fmt.Println("La la la la...", lyrics)
}
func (e Employee) SayHi() {
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
e.company, e.phone) //Si acá podes cortarlo en 2 líneas.
}
// La interfaz Men es implementada por Human, Student y Employee
type Men interface {
SayHi()
Sing(lyrics string)
}
func main() {
mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}
paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100}
sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}
Tom := Employee{Human{"Sam", 36, "444-222-XXX"}, "Things Ltd.", 5000}
// definimos la interfaz i
var i Men
//i podemos guardar un Student
i = mike
fmt.Println("This is Mike, a Student:")
i.SayHi()
i.Sing("November rain")
//i podemos guardar un Employee
i = Tom
fmt.Println("This is Tom, an Employee:")
i.SayHi()
i.Sing("Born to be wild")
// lista de Men
fmt.Println("Let's use a slice of Men and see what happens")
x := make([]Men, 3)
// estos tres elementos son de diferentes tipos pero todos ellos implementan la interfaz Men
x[0], x[1], x[2] = paul, sam, mike
for _, value := range x {
value.SayHi()
}
}
```
Una interfaz es un conjunto de métodos abstractos, y puede ser implementada por tipos que no son interfaces. Por lo tanto esta no puede implementarse a si misma.
### Interfaz vacía
Una interfaz vacía es una interfaz que no contiene ningún método, entonces todos los tipos implementan una interfaz vacía. Es muy útil cuando buscamos guardar todos los tipos en el mismo lugar, y es similar a void* en C.
```
// definimos una interfaz vacía
var a interface{}
var i int = 5
s := "Hello world"
// a puede guardar un valor de cualquier tipo
a = i
a = s
```
Si una función usa una interfaz vacía como su tipo de argumento, esta puede aceptar cualquier tipo; si una función usa una interfaz vacía como el tipo de valor de retorno, esta puede devolver cualquier tipo.
### Argumentos de métodos de una interfaz
Cualquier variable puede usarse en una interfaz, entonces podemos pensar sobre como podemos usar esta característica para pasar cualquier tipo de variable a una función.
Por ejemplo, usamos mucho fmt.Println, pero alguna vez notaste que este acepta cualquier tipo de argumento? Si vemos el código fuente de fmt que es libre, podemos ver la siguiente definición.
```
type Stringer interface {
String() string
}
```
Esto significa que cualquier tipo que implemente la interfaz Stringer puede ser pasada a fmt.Println como argumento. Vamos a probarlo.
```
package main
import (
"fmt"
"strconv"
)
type Human struct {
name string
age int
phone string
}
// Human implementa fmt.Stringer
func (h Human) String() string {
return "Name:" + h.name + ", Age:" + strconv.Itoa(h.age) + " years, Contact:" + h.phone
}
func main() {
Bob := Human{"Bob", 39, "000-7777-XXX"}
fmt.Println("This Human is : ", Bob)
}
```
Volviendo atrás al ejemplo de Box, podemos ver que Color también implementa la interfaz Stringer, por lo que somos capaces de personalizar el formato de impresión. Si no implementamos esa interfaz, fmt.Println imprimirá el tipo con el formato por defecto.
```
fmt.Println("The biggest one is", boxes.BiggestsColor().String())
fmt.Println("The biggest one is", boxes.BiggestsColor())
```
Atención: Si el tipo implementa la interfaz `error`, fmt va a llamar a `error()`, entonces en este punto usted no tendrá que implementar Stringer.
### Tipos de variables en un interfaz
Si una variable es del tipo que implementa una interfaz, sabemos que cualquier otro tipo que implemente la misma interfaz puede ser asignada a esta variable. La pregunta es ¿Como podemos saber cual es el tipo específico almacenado en la interfaz? Tenemos dos formas, que le voy a comentar a continuación.
- Patrón de afirmación Comma-ok
Go tiene la sintaxis `value, ok := element.(T)`. Esto comprueba si la variable es del tipo que se espero, donde value es el valor de la variable, y ok es un valor de tipo booleano, element es la variable interfaz y T es el tipo que se afirma tener.
Si el elemento es del tipo que esperamos, ok será true, en otro caso será false.
Veamos un ejemplo para verlo con más claridad.
```
package main
import (
"fmt"
"strconv"
)
type Element interface{}
type List []Element
type Person struct {
name string
age int
}
func (p Person) String() string {
return "(Nombre: " + p.name + " - edad: " + strconv.Itoa(p.age) + " años)"
}
func main() {
list := make(List, 3)
list[0] = 1 // un int
list[1] = "Hola" // una string
list[2] = Person{"Dennis", 70}
for index, element := range list {
if value, ok := element.(int); ok {
fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
} else if value, ok := element.(string); ok {
fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
} else if value, ok := element.(Person); ok {
fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
} else {
fmt.Println("list[%d] is of a different type", index)
}
}
}
```
Es bastante sencillo usar este patrón, pero si tenemos que verificar varios tipos, es mejor que usemos un `switch`.
- Verificación con un switch
Vamos a ver el uso de `switch` para reescribir el ejemplo anterior.
```
package main
import (
"fmt"
"strconv"
)
type Element interface{}
type List []Element
type Person struct {
name string
age int
}
func (p Person) String() string {
return "(Nombre: " + p.name + " - edad: " + strconv.Itoa(p.age) + " años)"
}
func main() {
list := make(List, 3)
list[0] = 1 //un int
list[1] = "Hello" //una string
list[2] = Person{"Dennis", 70}
for index, element := range list {
switch value := element.(type) {
case int:
fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
case string:
fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
case Person:
fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
default:
fmt.Println("list[%d] is of a different type", index)
}
}
}
```
Una cosa que deberíamos recordar es que `element.(type)` no puede ser usado fuera del cuerpo del `switch`, lo que significa que en este caso no puedes usar el patrón `comma-ok`.
### Interfaces embebidas
La como más atractiva es que Go tiene mucha sintaxis para la lógica embebida, como campos anónimos en un struct. No es para sorprenderse, que podamos usar interfaces también como campos anónimos, pero vamos a llamarlas `Interfaces embebidas`. Aquí, vamos a seguir las mismas reglas que para los campos anónimos. Más específicamente, si una interfaz tiene otra interfaz como una interfaz embebida, esta tendrá todos los métodos que la clase embebida tiene.
Podemos ver el archivo fuente `container/heap` que tiene una definición como la siguiente.
```
type Interface interface {
sort.Interface // embebida sort.Interface
Push(x interface{}) //el método Push para empujar elementos a la pila
Pop() interface{} //el elemento Pop que saca elementos de la pila
}
Podemos ver que `sort.Interface` es una interfaz embebida, por lo que la interfaz anterior tiene tres métodos que son explícitos de `sort.Interface`.
type Interface interface {
// Len es el número de elementos en la colección.
Len() int
// Less devuelve si el elemento de índice i se debe ordenar
// antes que el elemento con índice j.
Less(i, j int) bool
// Swap intercambia los elementos con índices i y j.
Swap(i, j int)
}
Otro ejemplo es el de `io.ReadWriter` en el paquete `io`.
// io.ReadWriter
type ReadWriter interface {
Reader
Writer
}
```
### Reflexión
La reflexión en Go es usada para obtener información en tiempo de ejecución. Vamos a usar el paquete `reflect`, y este articulo oficial [articulo](http://golang.org/doc/articles/laws_of_reflection.html) explica como funciona la reflexión en Go.
Tenemos que realizar tres pasos para usar reflect. Primero, necesitamos convertir una interfaz en un tipo reflect types (reflect.Type o reflect.Value, depende en que situación nos encontremos).
```
t := reflect.TypeOf(i) // tomamos la meta-data de el tipo i, y usamos t para tomar todos los elementos
v := reflect.ValueOf(i) // tomamos el valor actual de el tipo i, y usamos v para cambiar este valor
```
Después de eso, necesitamos convertir el tipo reflect de el valor que tomamos a el tipo que necesitamos.
```
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())
fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
fmt.Println("value:", v.Float())
```
Finalmente, si buscamos cambiar el valor que vino del tipo reflects,necesitamos hacerlo modificable. Como hablamos antes, esta es una diferencia entre pasar por valor o por referencia. El siguiente código no compilará.
```
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1)
```
En lugar de eso, debemos usar el siguiente código para cambiar el valor de los tipos reflect.
```
var x float64 = 3.4
p := reflect.ValueOf(&x)
v := p.Elem()
v.SetFloat(7.1)
```
Acabo de hablar sobre los conocimientos básicos de sobre el uso de reflexión (reflection), debería practicar más para entenderlo mejor.
## Enlaces
- [Indice](preface.md)
- Sección anterior: [Orientado a objetos](02.5.md)
- Siguiente sección: [Concurrencia](02.7.md)
# Concurrencia
Se dice que Go es el lenguaje C del siglo 21. Pienso que hay dos razones para eso: la primera, Go es un lenguaje simple; segundo, la concurrencia es un tema candente en el mundo de hoy, y Go soporta esta característica a nivel de lenguaje.
## goroutine
goroutines y concurrencia están integradas en el diseño del núcleo de Go. Ellas son similares a los hilos pero trabajan de forma diferente. Más de una docena de goroutines a lo mejor por debajo solo tienen 5 o 6 hilos. Go también nos da soporte completo para compartir memoria entre sus goroutines. Una goroutine usualmente usa 4~5 KB de memoria en la pila. Por lo tanto, no es difícil ejecutar miles de goroutines en una sola computadora. Una goroutine es mas liviana, más eficiente, y más conveniente que los hilos del sistema.
Las goroutines corren en el administrador de procesos en tiempo de ejecución en Go. Usamos la palabra reservada `go` para crear una nueva goroutine, que por debajo es una función ( ***main() es una goroutine*** ).
```
go hello(a, b, c)
```
Vamos a ver un ejemplo.
```
package main
import (
"fmt"
"runtime"
)
func say(s string) {
for i := 0; i < 5; i++ {
runtime.Gosched()
fmt.Println(s)
}
}
func main() {
go say("world") // creamos una nueva goroutine
say("hello") // actual goroutine
}
```
Salida:
```
hello
world
hello
world
hello
world
hello
world
hello
```
Podemos ver que es muy fácil usar concurrencia en Go usando la palabra reservada `go`. En el ejemplo anterior, estas dos goroutines comparten algo de memoria, pero sería mejor si utilizáramos la receta de diseño: No utilice datos compartidos para comunicarse, use comunicación para compartir datos.
runtime.Gosched() le dice al CPU que ejecute otras goroutines, y que en algún punto vuelva.
El manejador de tareas solo usa un hilo para correr todas la goroutines, lo que significa que solo implementa la concurrencia. Si buscas utilizar mas núcleos del CPU para usar mas procesos en paralelo, tenemos que llamar a runtime.GOMAXPROCS(n) para configurar el numero de núcleos que deseamos usar. Si `n<1`, esto no va a cambiar nada. Esta función se puede quitar en el futuro, para ver mas detalles sobre el procesamiento en paralelo y la concurrencia vea el siguiente [articulo](http://concur.rspace.googlecode.com/hg/talk/concur.html#landing-slide).
## Canales
goroutines son ejecutadas en el mismo espacio de direcciones de memoria, por lo que se tiene que mantener sincronizadas si buscas acceder a la memoria compartida. ¿Como nos comunicamos entre diferentes goroutines? Go utiliza un muy buen mecanismo de comunicación llamado `canales`(channel). Los `canales` son como dos tuberías (o pipes) en la shell de Unix: usando canales para enviar o recibir los datos. El unico tipo de datos que se puede usar en los canales es el tipo `channel` y la palabra reservada para eso es `chan`. Tenga en cuenta que para crear un nuevo `channel` debemos usar la palabra reservada `make`.
```
ci := make(chan int)
cs := make(chan string)
cf := make(chan interface{})
```
Los canales usan el operador `<-` para enviar o recibir datos.
```
ch <- v // enviamos v al canal ch.
v := <-ch // recibimos datos de ch, y lo asignamos a v
```
Vemos esto en un ejemplo.
```
package main
import "fmt"
func sum(a []int, c chan int) {
total := 0
for _, v := range a {
total += v
}
c <- total // enviamos total 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 // recibimos de c
fmt.Println(x, y, x + y)
}
```
Enviando y recibimos los datos por defecto en bloques, por lo que es mucho mas fácil usar goroutines sincrónicas. Lo que quiero decir, es que el bloque en la goroutine no va a continuar cuando reciba datos de un canal vacío (`value := <-ch`), hasta que otras goroutines envíen datos a este canal. Por otro lado, la goroutine por otro lado no enviara datos al canal (`ch<-5`) hasta que no reciba datos.
## Buffered channels
Anteriormente hice una introducción sobre canales non-buffered channels (non-buffered channels), y Go también tiene 'buffered channels' que pueden guardar mas de un elemento. Por ejemplo, `ch := make(chan bool, 4)`, aca creamos un canal que puede guardar 4 elementos booleanos. Por lo tanto con este canal, somos capaces de enviar 4 elementos sin el bloqueo, pero la goroutine se bloqueará cuando intente enviar un quito elemento y la goroutine no lo recibirá.
```
ch := make(chan type, n)
n == 0 ! non-buffer(block)
n > 0 ! buffer(non-block until n elements in the channel)
```
Puedes probar con este código en tu computadora y cambiar algunos valores.
```
package main
import "fmt"
func main() {
c := make(chan int, 2) // si cambia 2 por 1 tendrá un error en tiempo de ejecución, pero 3 estará bien
c <- 1
c <- 2
fmt.Println(<-c)
fmt.Println(<-c)
}
```
## Range y Close
Podemos usar range para para hacer funcionar los 'buffer channels' como una lista y un map.
```
package main
import (
"fmt"
)
func fibonacci(n int, c chan int) {
x, y := 1, 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)
}
}
```
`for i := range c` no parara de leer información de el canal hasta que el canal se alla cerrado. Vamos a usar la palabra reservada `close` para cerrar el canal en el ejemplo anterior. Es imposible enviar o recibir datos de un canal cerrado, puede usar `v, ok := <-ch` para verificar si el canal esta cerrado. Si `ok` devuelve false, esto significa que no hay datos en ese canal y este fue cerrado.
Recuerde cerrar siempre los canales productores, no los consumidores, o sera muy fácil obtener un estado de pánico o 'panic status'.
Otra cosa que deber tener que recordar es que los canales son diferentes a los archivos, y no debe cerrarlos con frecuencia, a menos que este seguro que es canal esta completamente sin uso, o desea salir del bloque donde usa 'range'.
## Select
En los ejemplos anteriores, nosotros usamos solo un canal, pero ¿como podemos lidiar con mas de un canal? Go tiene la palabra reservada llamada `select` para escuchar muchos canales.
`select` de forma predeterminada es bloqueante, y este continua la ejecución solo cuando un canal tiene datos o recibió datos. Si varios canales están listos para usarse al mismo tiempo, select elegirá cual ejecutar al azar.
```
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 1, 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)
}
`select` también tiene `default`, al igual que el `switch`. Cuando todos los canales no están listos para ser usados, ejecuta el default (no espera mas por el canal).
select {
case i := <-c:
// usa i
default:
// se ejecuta cuando c esta bloqueado
}
```
## Timeout
A veces la goroutine esta bloqueada, ¿pero como podemos evitar que esto, mientras tanto nos bloquee el programa? Podemos configurar para esto un timeout en el select.
```
func main() {
c := make(chan int)
o := make(chan bool)
go func() {
for {
select {
case v := <- c:
println(v)
case <- time.After(5 * time.Second):
println("timeout")
o <- true
break
}
}
}()
<- o
}
```
## Goroutine en tiempo de ejecución (o runtime)
El paquete `runtime` tiene algunas funciones para hacer frente a las goroutines.
- `runtime.Goexit()`
Sale de la actual goroutine, pero las funciones defer son ejecutadas como de costumbre.
- `runtime.Gosched()`
Permite que el manejador de tareas ejecute otras goroutines, y en algún momento vuelve allí.
- `runtime.NumCPU() int`
Devuelve el numero de núcleos del CPU
- `runtime.NumGoroutine() int`
Devuelve el numero de goroutines
- `runtime.GOMAXPROCS(n int) int`
Configura cuantos núcleos del CPU queremos usar
## Enlaces
- [Indice](preface.md)
- Sección anterior: [interfaces](02.6.md)
- Siguiente sección: [Resumen](02.8.md)
# 2.8 Resumen
En este capitulo principalmente introducimos 25 palabras reservadas de Go. Vamos a repasar cuales son y que hacen.
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
- `var` y `const` son usadas para definir variables y constantes.
- `package` e `import` son para el uso de paquetes.
- `func` es usada para definir funciones y métodos.
- `return` es usada para devolver valores en funciones o métodos.
- `defer` es usada para definir funciones defer.
- `go` es usada para comenzar a correr una nueva goroutine.
- `select` es usada para elegir entre múltiples canales de comunicación.
- `interface` es usada para definir una interfaz.
- `struct` es usada para definir tipos especiales personalizados.
- `break`, `case`, `continue`, `for`, `fallthrough`, `else`, `if`, `switch`, `goto`, `default` fueron introducidos en la sección 2.3.
- `chan` es usada para crear tipos de canales para comunicarse con las goroutines.
- `type` es usada para definir tipos personalizados.
- `map` es usada para definir un map que es como una tabla hash en otros lenguajes.
- `range` es usada para leer datos desde un `slice`, `map` o `channel`.
Si entendes como usar estas 25 palabras reservadas, usted ya a aprendido mucho sobre Go.
## Enlaces
- [Indices](preface.md)
- Sección anterior: [Concurrencia](02.7.md)
- Siguiente sección: [Conocimientos básicos sobre la Web](03.0.md)
- 1.[Go environment configuration](01.0.md)
- 1.1. [Installation](01.1.md)
- 1.2. [$GOPATH and workspace](01.2.md)
- 1.3. [Go commands](01.3.md)
- 1.4. [Go development tools](01.4.md)
- 1.5. [Summary](01.5.md)
- 2.[Go basic knowledge](02.0.md)
- 2.1. ["Hello, Go"](02.1.md)
- 2.2. [Go foundation](02.2.md)
- 2.3. [Control statements and functions](02.3.md)
- 2.4. [struct](02.4.md)
- 2.5. [Object-oriented](02.5.md)
- 2.6. [interface](02.6.md)
- 2.7. [Concurrency](02.7.md)
- 2.8. [Summary](02.8.md)
- 3.[Web foundation](03.0.md)
- 3.1. [Web working principles](03.1.md)
- 3.2. [Build a simple web server](03.2.md)
- 3.3. [How Go works with web](03.3.md)
- 3.4. [Get into http package](03.4.md)
- 3.5. [Summary](03.5.md)
- 4.[User form](04.0.md)
- 4.1. [Process form inputs](04.1.md)
- 4.2. [Verification of inputs](04.2.md)
- 4.3. [Cross site scripting](04.3.md)
- 4.4. [Duplicate submissions](04.4.md)
- 4.5. [File upload](04.5.md)
- 4.6. [Summary](04.6.md)
- 5.[Database](05.0.md)
- 5.1. [database/sql interface](05.1.md)
- 5.2. [MySQL](05.2.md)
- 5.3. [SQLite](05.3.md)
- 5.4. [PostgreSQL](05.4.md)
- 5.5. [Develop ORM based on beedb](05.5.md)
- 5.6. [NoSQL database](05.6.md)
- 5.7. [Summary](05.7.md)
- 6.[Data storage and session](06.0.md)
- 6.1. [Session and cookies](06.1.md)
- 6.2. [How to use session in Go](06.2.md)
- 6.3. [Session storage](06.3.md)
- 6.4. [Prevent hijack of session](06.4.md)
- 6.5. [Summary](06.5.md)
- 7.[Text files](07.0.md)
- 7.1. [XML](07.1.md)
- 7.2. [JSON](07.2.md)
- 7.3. [Regexp](07.3.md)
- 7.4. [Templates](07.4.md)
- 7.5. [Files](07.5.md)
- 7.6. [Strings](07.6.md)
- 7.7. [Summary](07.7.md)
- 8.[Web services](08.0.md)
- 8.1. [Sockets](08.1.md)
- 8.2. [WebSocket](08.2.md)
- 8.3. [REST](08.3.md)
- 8.4. [RPC](08.4.md)
- 8.5. [Summary](08.5.md)
- 9.[Security and encryption](09.0.md)
- 9.1. [CSRF attacks](09.1.md)
- 9.2. [Filter inputs](09.2.md)
- 9.3. [XSS attacks](09.3.md)
- 9.4. [SQL injection](09.4.md)
- 9.5. [Password storage](09.5.md)
- 9.6. [Encrypt and decrypt data](09.6.md)
- 9.7. [Summary](09.7.md)
- 10.[Internationalization and localization](10.0.md)
- 10.1 [Time zone](10.1.md)
- 10.2 [Localized resources](10.2.md)
- 10.3 [International sites](10.3.md)
- 10.4 [Summary](10.4.md)
- 11.[Error handling, debugging and testing](11.0.md)
- 11.1. [Error handling](11.1.md)
- 11.2. [Debugging by using GDB](11.2.md)
- 11.3. [Write test cases](11.3.md)
- 11.4. [Summary](11.4.md)
- 12.[Deployment and maintenance](12.0.md)
- 12.1. [Logs](12.1.md)
- 12.2. [Errors and crashes](12.2.md)
- 12.3. [Deployment](12.3.md)
- 12.4. [Backup and recovery](12.4.md)
- 12.5. [Summary](12.5.md)
- 13.[Build a web framework](13.0.md)
- 13.1. [Project program](13.1.md)
- 13.2. [Customized routers](13.2.md)
- 13.3. [Design controllers](13.3.md)
- 13.4. [Logs and configurations](13.4.md)
- 13.5. [Add, delete and update blogs](13.5.md)
- 13.6. [Summary](13.6.md)
- 14.[Develop web framework](14.0.md)
- 14.1. [Static files](14.1.md)
- 14.2. [Session](14.2.md)
- 14.3. [Form](14.3.md)
- 14.4. [User validation](14.4.md)
- 14.5. [Multi-language support](14.5.md)
- 14.6. [pprof](14.6.md)
- 14.7. [Summary](14.7.md)
- Appendix A [References](ref.md)
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册