sábado, 27 de julio de 2013

Lenguaje Go! Comenzando a ver las librerías del core (números)

Hola a todos, en este nuevo post sobre el lenguaje Go!, vamos a comenzar a ver parte de la librería del core de Go, en este caso vamos a arrancar por un tema que quizás no les guste a muchos, ya que vamos a ver como trabajar con números :P



Conversión entre números y strings

Este es un ejemplo:

package main

import "fmt"
import "strconv"

func main() {
var i int
fmt.Scanf("%d", &i)
str := strconv.FormatInt(int64(i), 10)
hex, _ := strconv.ParseInt(str, 16, 64)
fmt.Printf("%d\n", hex)
}

Hemos visto algunos ejemplo de conversión entre números y string usando el paquete “fmt”. Nosotros podríamos usar especificadores de formato numérico con funciones como Printf() ó Scanf() para leer y escribir números, pero eso generalmente es excesivo para una simple conversión.

El paquete “strconv” contiene las distintas funciones para realizar las conversiones de manera simple. Esto nos permite la conversión entre tipos numéricos y cadenas, en ambas direcciones, opcionalmente con una base de número especificado.
En el ejemplo se muestra una secuencia más bien complicada de conversiones. La primera dice que algunos caracteres de la entrada estándar, se interpretarán como un entero decimal. A continuación, se construye una cadena a partir de esto, y luego se analiza la cadena en base-16. Por último, se muestra el valor como un entero de base 10 de nuevo. Antes de Go 1.0, las funciones de este paquete tenían nombres muy confusos para diferentes bases numéricas. Ahora la amplia gama de funciones se ha sustituido por un conjunto más simple y más genérico.
El ejemplo utiliza “FormatInt()” para crear una cadena desde el número entero y “parseInt ()” para realizar la conversión inversa. Estamos ignorando el segundo valor devuelto por “strconv.ParseInt ()”. Este es un valor de error, y suponemos que la entrada es siempre válida. Si vamos a recibir la entrada por parte del usuario, entonces probablemente deberíamos comprobar el valor de error.

Usando enteros grandes

Ejemplo de código previo a la explicación :D

package main

import "math/big"
import "fmt"

func main() {
var n int
fmt.Printf("Cuantos números de la serie Fibonacci quiere calcular?")
fmt.Scanf("%d", &n)
last := big.NewInt(1)
current := big.NewInt(1)
for i := 0 ; (i < n) && (i < 2) ; i++ {
fmt.Printf("1\n")
}
for i := 2 ; i < n ; i++ {
last.Add(last, current)
tmp := last
last = current
current = tmp
fmt.Printf("%s\n", current.String())
}
}

El paquete “math/big” define dos tipos: uno para los números enteros de longitud arbitraria y una para los números racionales de tamaño arbitrario, representados como fracciones. Estos definen cada uno, un conjunto de operaciones, todos siguiendo las mismas generalidades, y de una forma ligeramente poco intuitiva.

Las operaciones tales como “Add”(sumar) son métodos de estos tipos. El receptor está configurado para guardar el resultado de realizar la operación con los dos operandos. Esto significa que a.Add (b, c)  en este paquete es equivalente a “a = b + c”. En el ejemplo se muestra cómo utilizar números de tipo “grandes enteros” para calcular la serie Fibonacci. Este programa crea dos grandes enteros, uno para cada uno de los dos últimos valores en la serie.
No hay tipos explícitos en este programa (a excepción del pequeño entero utilizado para el bucle). Esta es una de las cosas buenas de Go: que rara vez tienen que preocuparse acerca de los tipos, se puede confiar en la inferencia de tipos en todas partes. Los tres grandes enteros son todos punteros hacia las estructuras de los “big int”.
Una de las ventajas del recolector de basura es que no tenemos por que preocuparnos por esto. Las variables automáticamente se limpian si son puntero o tipos de estructuras.
Una implementación de “big int” de cualquier tipo tendrá que almacenar una cantidad arbitraria de datos, por lo que es más eficiente si los reutilizamos. Para calcular la secuencia, vamos a sumar los dos últimos valores juntos y luego en el bucle, vamos desechando el viejo último valor implícito en la adición y luego los intercambiamos.
La operación de intercambio es muy barata, ya que son tipos de punteros: sólo estamos cambiando la forma en que nos referimos a los dos objetos, no los estamos copiando (o pasando por valor).

Conversión entre números y punteros

Ejemplo de código:

package main

import "fmt"
import "unsafe"

func main() {
str := "Variable en Go"
addr := unsafe.Pointer(&str)
fmt.Printf("La dirección de str es %d\n", addr)
str2 := (*string)(addr)
fmt.Printf("Constructor de String para punteros: %s\n", *str2)
address := uintptr(addr)
address += 4
// Esto no tiene definido el enfoque!
str3 := (*string)(unsafe.Pointer(address))
fmt.Printf("Constructor de String para punteros: %s\n", *str3)
}

En el lenguaje “BCPL”, no había distinción entre los int y los tipos de punteros, solo había un solo tipo de palabra que almacenaría un valor que podría caber en un registro. Si has hecho un poco de aritmética con un valor tal, que fuera tratado como un int, y se eliminan las referencias, este será tratado como un puntero. C añade algo de tipificación explícita, por lo que los int y los punteros fueron tratados como tipos distintos, pero todavía se permite conversiones implícitas entre los punteros y punteros del tamaño de int, como también permite diversas formas de aritmética de punteros. En C, un puntero es un número que representa una dirección de memoria, con una pequeña cantidad de la verificación de tipos para que no hagamos algunas cosas estúpidas :D, que son posible hacer con los punteros.
En Go, punteros y los int son tipos totalmente distintos. La conversión entre ellos se admite a través del paquete de “unsafe”, como vemos en el ejemplo.

Nota:
Las operaciones no seguras no son siempre compatibles. Pasándose el parámetro -u a el compilador Go desactiva el chequeo. Esto es comúnmente utilizado en entornos como el  de “Google App Engine”. Lo primero que notará acerca de este ejemplo es que es complicado y detallado. Esto es intencional: hacer las cosas de bajo nivel con los punteros es muy raro tener que hacerlo, y ademas Go no quiere fomentar este tipo de comportamiento. En C es común que tengas que hacer aritmética de punteros, porque el lenguaje no proporciona una forma sensata de hacer lo que queres. En cambio Go, normalmente sólo es necesario acceder a punteros como int para interactuar con otros idiomas, o para algunas tareas muy bajo nivel. Abstracciones como “slices” evitan la necesidad en los casos comunes.

El paquete “unsafe” proporciona un tipo de puntero, lo que representa un puntero arbitrario. Este tiene algunas propiedades especiales. Se puede convertir en y desde una “uintptr”, dando un valor numérico que representa la dirección. También se puede lanzar desde y hacia cualquier otro tipo de puntero en Go. Esto evita por completo el mecanismo chequeo de tipo de Go, y eso no da garantía de que el puntero resultante sea válido.
A diferencia de C, Go se vuelve explícito cuando usted está haciendo cosas que pueden ser peligrosas con punteros.

Salida del programa de ejemplo:

La dirección de str es 833223893344
Constructor de String para punteros: Variable en Go
panic: runtime error: runtime: allocation size out of range

goroutine 1 [running]:
fmt.(*fmt).padString(0xc2000562c8, 0xe00000000, 0x4b253000000000)
/usr/local/go/src/pkg/fmt/format.go:129 +0x1ba
fmt.(*fmt).fmt_s(0xc2000562c8, 0xe00000000, 0x4b253000000000)
/usr/local/go/src/pkg/fmt/format.go:272 +0x5b
fmt.(*pp).fmtString(0xc200056270, 0xe00000000, 0x4b253000000000, 0x73)
/usr/local/go/src/pkg/fmt/print.go:537 +0xb3
fmt.(*pp).printField(0xc200056270, 0x4819c0, 0xc20003a180, 0x73, 0x0, ...)
/usr/local/go/src/pkg/fmt/print.go:799 +0x9b6
fmt.(*pp).doPrintf(0xc200056270, 0x4c24f0, 0x28, 0x7f8024da4f78, 0x1, ...)
/usr/local/go/src/pkg/fmt/print.go:1111 +0x1276
fmt.Fprintf(0xc200053180, 0xc200000008, 0x4c24f0, 0x28, 0x7f8024da4f78, ...)
/usr/local/go/src/pkg/fmt/print.go:214 +0x80
fmt.Printf(0x4c24f0, 0x28, 0x7f8024da4f78, 0x1, 0x1, ...)
/usr/local/go/src/pkg/fmt/print.go:223 +0x8c
main.main()
/home/jackgris/workspace/prueba.go:16 +0x224
exit status 2



Web Oficial

Ver por lo de la licencia

Espero que les haya gustado, esta entrada, como verán empezamos a ver algunas de las librerías que nos ofrece Go, en el próximo post sobre el lenguaje, probablemente veamos algo de patrones o seguiremos mirando algo más de la librería estándar del mismo. Nos vemos en la próxima ;)

Saludos a todos, Gabriel