Go (o Golang) es un lenguaje de programación moderno, creado en 2007 por Robert Griesemer, Rob Pike y Ken Thompson y lanzado en 2009. Trata de aprender de los errores de otros lenguajes de programación más antiguos, tomando lo mejor de cada uno y eliminando las limitaciones o problemas que estos nos presentan.
Go nace de la necesidad de crear un lenguaje donde:
En los lenguajes de programación tradicionales, era necesario escoger entre una rápida velocidad de ejecución, rápida velocidad de compilación o sencillez en la lectura y escritura del lenguaje . En la filosofía Go, estos tres elementos son posibles al mismo tiempo.
package main
Los archivos Go casi siempre tienen declaraciones import
import "fmt"
import (
"fmt"
"math/rand"
)
fmt.Println("Hola Mundo")
fmt.Println("go" + "lang")
fmt.Println("1.0/2.0 =", 1.0/2.0)
fmt.Println("4 < 7 =", 4 < 7)
fmt.Println(true && false)
fmt.Println("Mi numero favorito es:", rand.Intn(10))
Hola Mundo golang 1.0/2.0 = 0.5 4 < 7 = true false Mi numero favorito es: 1
25 <nil>
Nota : Go distingue entre mayúsculas y minúsculas
En Go, las variables son declaradas explícitamente, var se usa para declarar una o más variables. Se puede declarar múltiples variables en una línea.
Go soporta constantes de tipo carácter, cadena, booleano y valores numéricos.
import "reflect"
const s string = "constant"
fmt.Println(s, reflect.TypeOf(s))
var a string = "Prueba"
fmt.Println(a, reflect.TypeOf(a))
var b, c int = 9, 10
fmt.Println(b,reflect.TypeOf(b), c, reflect.TypeOf(c))
var d = true
fmt.Println(d, reflect.TypeOf(d))
var e int
fmt.Println(e, reflect.TypeOf(e))
constant string Prueba string 9 int 10 int true bool 0 int
6 <nil>
Nota:
Las variables tienen por defecto 0 o false en el caso del bool
- bool -----> true, false
- int8 -----> -128....127
- int16 -----> -32768….32767
- int32 -----> -2147483648...2147483647
- int64 -----> -9223372034854775808….9223372036854775807
- float32 -----> Real (punto flotante) de 32 bits
- float64 -----> Real (punto flotante) de 64 bits
- complex64 ----->números complejos con partes real e imaginaria de 32 bits
- complex128 ----->números complejos con partes real e imaginaria de 64 bits
- uint8 -----> 0...255
- uint16 ----->0....65535
- uint32 ----->0...4294967295
- uint64 -----> 0….18446744073709551615
- byte -----> igual que uint8
- rune -----> igual que int32
- uint -----> igual que uint32
- int -----> igual que int32
- uintptr -----> entero suficientemente para almacenar un puntero
- string -----> array inmutable de bits que representa caracteres
Operadores | Significado | Tipo de dato |
---|---|---|
&& | Y | logicos |
|| | O | logicos |
! | NO | logicos |
+ | Suma | enteros, float, complex,string |
- | Resta | enteros, float, complex |
* | Multiplicacion | enteros, float, complex |
/ | Division | enteros, float, complex |
% | Modulo | enteros |
& | Y(bit a bit) | enteros |
| | O(bit a bit) | enteros |
^ | XOR(bit a bit) | enteros |
&^ | Y NO(limpiar bits) | enteros |
<< | Desplazamiento a izquierda | enteros |
>> | Desplazamiento a derecha | enteros |
== | Igualdad | Todo tipo de dato |
!= | Diferencia | Todo tipo de dato |
> | Mayor que | Todo tipo de dato |
< | Menor que | Todo tipo de dato |
>= | Mayor o igual que | Todo tipo de dato |
<= | Menor o igual que | Todo tipo de dato |
i := 1
for i <= 3 { //El tipo más básico, con una condición sencilla.
fmt.Println(i)
i = i + 1
}
for j := 7; j <= 9; j++ { //El clásico ciclo for con estructura inicializar/condición/después.
fmt.Println(j)
}
for { //sin ninguna condición iterará repetidamente hasta que se use break para salir del ciclo o return para regresar un valor de la función que lo contiene.
fmt.Println("loop")
break
}
1 2 3 7 8 9 loop
La derivación con if y else en Go se hace de manera directa.
if 7%2 == 0 { // Ejemplo básico.
fmt.Println("7 es par")
} else {
fmt.Println("7 es impar")
}
if 8%4 == 0 { // Puedes utilizar un `if` sin un else.
fmt.Println("8 es divisible entre 4")
}
// Los condicionales pueden ser precedidos por una declaración; cualquier
// variable declarada en dicha declaración estará disponible en todas las derivaciones.
if num := 9; num < 0 {
fmt.Println(num, "es negativo")
} else if num < 10 {
fmt.Println(num, "tiene 1 digito")
} else {
fmt.Println(num, "tiene multiples digitos")
}
7 es impar 8 es divisible entre 4 9 tiene 1 digito
Nota : Go no necesita los paréntesis alrededor de las condiciones en Go, pero las llaves {} si son obligatorias.
Al igual que en el if es posible definir una variable al inicio. En cada caso el break es usado automáticamente.
import (
"time"
)
fmt.Println("¿Cuándo es Sábado?")
today := time.Now().Weekday()
switch time.Saturday {
case today + 0:
fmt.Println("Hoy!")
case today + 1:
fmt.Println("Mañana!")
case today + 2:
fmt.Println("En dos días!")
case today + 3:
fmt.Println("En tres días!")
default:
fmt.Println("Todavía falta mucho!")
}
¿Cuándo es Sábado? En tres días!
var alumnos [4]string
alumnos[0] = "Ana"
alumnos[1] = "Brenda"
alumnos[2] = "Carmen"
alumnos[3] = "David"
fmt.Println(alumnos[0],alumnos[3])
fmt.Println(alumnos)
primos := [6]int{2, 3, 5, 7, 11, 13}
fmt.Println(primos)
Ana David [Ana Brenda Carmen David] [2 3 5 7 11 13]
16 <nil>
Un arreglo tiene un tamaño fijo, mientras que una slice (porciones) son secciones de arreglos, estas no almacenan datos, solo hacen referencia a secciones especificas de un arreglo. “[] T” es una slice con elementos de tipo T.
alumnos[1:4]
[Brenda Carmen David]
Los Mapas son el tipo de datos asociativo de Go. (también conocidos como Diccionarios o Hashes en otros lenguajes).
m := make(map[string]int) // Para crear un mapa vacío, se utiliza `make`:`make(map[key-type]val-type)`.
// Se pueden establecer los pares de llaves/valores utilizando la sintaxis típica `name[key] = val`.
m["k1"] = 7
m["k2"] = 13
// Si se presenta el mapa con e.g. `Println` muestra todos sus pares de llaves/valores.
fmt.Println("map:", m)
// Se obtiene el valor de una llave con la sintaxis `name[key]`.
v1 := m["k1"]
fmt.Println("v1: ", v1)
// La función `len` regresa el número de pares llave/valor cuando
// se utiliza con un mapa.
fmt.Println("len:", len(m))
// La función `delete` elimina pares llave/valor de un mapa.
delete(m, "k2")
fmt.Println("map:", m)
// También puedes declarar e inicializar un mapa nuevo
// en una sola línea con la sintaxis:
n := map[string]int{"foo": 1, "bar": 2}
fmt.Println("map:", n)
map: map[k1:7 k2:13] v1: 7 len: 2 map: map[k1:7] map: map[foo:1 bar:2]
22 <nil>
Las Funciones son una parte escencial de Go.
Estructura de una función
package main
import "fmt"
func plus(a int, b int) int {
return a + b
}
func main() {
res := plus(1, 2)
fmt.Println("1+2 =", res)
}
func suma(a int, b int) /*Funcion que recibe dos valores `int` */ int {
//regresa la suma de los mismos como `int`.
return a + b
}
resultado := suma(1, 2)
fmt.Println("1+2 =", resultado)
1+2 = 3
8 <nil>
Nota:
Go soporta apuntadores, lo cual nos permite pasar referencias de valores y datos entre las funciones de nuestro programa.
i, j := 10, 27
p := &i // apunta a i
fmt.Println(*p) // lee i atraves del puntero
*p = 21 // asigna i a traves del puntero
fmt.Println(i) // ve el nuevo valor de i
p = &j // apunta a j
*p = *p / 3 // divide j a traves del puntero
fmt.Println(j) // ve el nuevo valor de j
10 21 9
2 <nil>
Los structs en go son colecciones de campos con tipos específicos. Son útiles para agrupar datos y formar registros.
type persona struct { //El struct persona tiene los campos nombre y edad.
nombre string
edad int
}
// Esta sintaxis crea una instancia de un struct.
fmt.Println(persona{"Bob", 20})
// Puedes nombrar los campos cuando inicializas un struct.
fmt.Println(persona{nombre: "Alice", edad: 30})
// Los campos omitidos serán de valor cero.
fmt.Println(persona{nombre: "Fred"})
// El prefijo `&` devuelve el apuntador a un struct.
fmt.Println(&persona{nombre: "Ann", edad: 40})
// Puedes acceder a los campos del struct con un punto.
s := persona{nombre: "Sean", edad: 50}
fmt.Println(s.nombre)
// También puedes usar puntos con apuntadores a struct -
// los apuntadores son automáticamente dereferenciados.
sp := &s
fmt.Println(sp.edad)
{Bob 20} {Alice 30} {Fred 0} &{Ann 40} Sean 50
3 <nil>
type rect struct {
width, height int
}
func (r *rect) area() int { //Este método area tiene un tipo receptor *rect.
return r.width * r.height
}
func (r rect) perim() int { //Los métodos pueden ser definidos para receptores de tipo apuntador o por valor.
return 2*r.width + 2*r.height
}
r := rect{width: 10, height: 5}
fmt.Println("area: ", r.area())
fmt.Println("perim:", r.perim())
rp := &r
fmt.Println("area: ", rp.area())
fmt.Println("perim:", rp.perim())
area: 50 perim: 30 area: 50 perim: 30
Nota: El método consta de:
Una interface es una plantilla de métodos y es utilizado para proporcionar modularidad a Go
import "math"
type geometrica interface {//Aquí podemos ver la interfaz básica de una figura geométrica.
area() float64
perim() float64
}
type cuadro struct {
ancho, altura float64
}
type circulo struct {
radio float64
}
func (s cuadro) area() float64 {
return s.ancho * s.altura
}
func (s cuadro) perim() float64 {
return 2*s.ancho + 2*s.altura
}
func (c circulo) area() float64 {
return math.Pi * c.radio * c.radio
}
func (c circulo) perim() float64 {
return 2 * math.Pi * c.radio
}
func medida(g geometrica) {
fmt.Println(g)
fmt.Println(g.area())
fmt.Println(g.perim())
}
s := cuadro{ancho: 3, altura: 4}
c := circulo{radio: 5}
medida(s)
medida(c)
{3 4} 12 14 {5} 78.53981633974483 31.41592653589793
import "time"
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello")
}
package main
import (
"fmt"
"os"
"time"
)
func main() {
start := time.Now()
f, err := os.Create("mi_archivo.txt")
if err != nil {
panic(err)
}
final := 100000
for i := 0; i <= final; i++ {
_, err = f.WriteString(fmt.Sprintf("%06x\n", i))
if err != nil {
panic(err)
}
}
elapsed := time.Since(start)
fmt.Println(elapsed)
}
344.893278ms
000000
000001
000002
000003
000004
000005
000006
000007
000008
000009
00000a
00000b
00000c
00000d
00000e
...
package main
import (
"fmt"
"os"
"time"
)
func calcNums(start, end int, f *os.File, doneCh chan struct{}){
for i := start; i <= end; i++ {
_, err := f.WriteString(fmt.Sprintf("%06x\n", i))
if err != nil {
panic(err)
}
}
doneCh <- struct{}{}
}
func main() {
start := time.Now()
f, err := os.Create("mi_archivo_concurrente.txt")
if err != nil {
panic(err)
}
numGoRoutines := 10
doneCh := make(chan struct{})
final := 100000
for i := 0; i <= final; i = i + (final/numGoRoutines) + 1 {
paso := i + (final/numGoRoutines)
if paso > final {
paso = final
}
fmt.Printf("ejecutando %d %d\n", i, paso)
go calcNums(i, paso, f, doneCh)
}
doneNum := 0
for doneNum < numGoRoutines {
<- doneCh
fmt.Println("Terminé")
doneNum++
}
fmt.Println("Todos han finalizado")
elapsed := time.Since(start)
fmt.Println(elapsed)
}
ejecutando 0 10000
ejecutando 10001 20001
ejecutando 20002 30002
ejecutando 30003 40003
ejecutando 40004 50004
ejecutando 50005 60005
ejecutando 60006 70006
ejecutando 70007 80007
ejecutando 80008 90008
ejecutando 90009 100000
Terminé
Terminé
Terminé
Terminé
Terminé
Terminé
Terminé
Terminé
Terminé
Terminé
Todos han finalizado
443.013137ms
011177
011178
011179
01117a
01117b
01117c
01117d
01117e
01117f
011180
011181
011182
011183
011184
011185
...
package main
import (
"fmt"
"os"
"time"
"strings"
)
func calcNums(start, end int, resultCh chan string, doneCh chan struct{}){
var sBuilder strings.Builder
for i := start; i <= end; i++ {
fmt.Fprint(&sBuilder, fmt.Sprintf("%06x\n", i))
}
resultCh <- sBuilder.String()
doneCh <- struct{}{}
}
func main() {
start := time.Now()
f, err := os.Create("mi_archivo_concurrente_mejorado.txt")
if err != nil {
panic(err)
}
outCh := make(chan string)
doneWrite := make(chan struct{})
//Writer
go func() {
for s := range outCh {
_, err := f.WriteString(s)
if err != nil {
panic(err)
}
}
doneWrite <- struct{}{}
}()
numGoRoutines := 10
doneCh := make(chan struct{})
final := 100000
for i := 0; i <= final; i = i + (final/numGoRoutines) + 1 {
paso := i + (final/numGoRoutines)
if paso > final {
paso = final
}
fmt.Printf("ejecutando %d %d\n", i, paso)
go calcNums(i, paso, outCh, doneCh)
}
doneNum := 0
for doneNum < numGoRoutines {
<- doneCh
fmt.Println("Terminé")
doneNum++
}
close(outCh)
<-doneWrite
fmt.Println("Todos han finalizado")
elapsed := time.Since(start)
fmt.Println(elapsed)
}
ejecutando 0 10000
ejecutando 10001 20001
ejecutando 20002 30002
ejecutando 30003 40003
ejecutando 40004 50004
ejecutando 50005 60005
ejecutando 60006 70006
ejecutando 70007 80007
ejecutando 80008 90008
ejecutando 90009 100000
Terminé
Terminé
Terminé
Terminé
Terminé
Terminé
Terminé
Terminé
Terminé
Terminé
Todos han finalizado
104.021407ms
004e22
004e23
004e24
004e25
004e26
004e27
004e28
004e29
004e2a
004e2b
004e2c
004e2d
004e2e
004e2f
004e30
...