![]() |
|
Golang Tutorial #2 - numbers and maps, conditions, structs, and types and interfaces - Printable Version +- Sinisterly (https://sinister.ly) +-- Forum: Coding (https://sinister.ly/Forum-Coding) +--- Forum: Coding (https://sinister.ly/Forum-Coding--71) +--- Thread: Golang Tutorial #2 - numbers and maps, conditions, structs, and types and interfaces (/Thread-Golang-Tutorial-2-numbers-and-maps-conditions-structs-and-types-and-interfaces) |
Golang Tutorial #2 - numbers and maps, conditions, structs, and types and interfaces - Inori - 04-20-2016 Continuing on with the series from yesterday, this is part 2 of the basic(ish) Go tutorials. Part 1 can be found here. With no extra introduction, here's today's first topic - ints and floats Ints and Floats Unlike python, which has embedded, dynamic typesetting, Go requires the user to supply bit length as well as values for anything that isn't just "int" or "float" (i.e., unsigned, over the default max [which I think is 32 bits]). These types, along with their maximum values and descriptions are listed below (from https://golang.org/ ). Code: uint8 the set of all unsigned 8-bit integers (0 to 255)
uint16 the set of all unsigned 16-bit integers (0 to 65535)
uint32 the set of all unsigned 32-bit integers (0 to 4294967295)
uint64 the set of all unsigned 64-bit integers (0 to 18446744073709551615)
int8 the set of all signed 8-bit integers (-128 to 127)
int16 the set of all signed 16-bit integers (-32768 to 32767)
int32 the set of all signed 32-bit integers (-2147483648 to 2147483647)
int64 the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)
float32 the set of all IEEE-754 32-bit floating-point numbers
float64 the set of all IEEE-754 64-bit floating-point numbers
complex64 the set of all complex numbers with float32 real and imaginary parts
complex128 the set of all complex numbers with float64 real and imaginary parts
byte alias for uint8
rune alias for int32Maps A map in Go is the same as a HashMap in Java, a dictionary in Python, a table in Lua, or a Hash in Ruby. Maps are flexible, easy to use, and don't have an allocated memory value (even when set with make(). It doesn't throw an error, but everything starts acting a bit odd). Map examples: Code: // inline, structured map creation
myMap0:=map[string]int{
"foo": 1,
"bar": 2, //<-- not a typo, you need the last comma
}
// "indexed" map creation
myMap1:=map[string]int{}
myMap1["foo"]=1
myMap1["bar"]=2
// map-ception
myMap2:=map[string]map[string]int{}
// already initialized as map[string]int, so no use of :=
myMap2["foo"]=map[string]int{
"test": 1,
"hello": 2,
}
myMap2["bar"]=map[string]int{
"abc": 3,
"def": 5,
}Conditionals Conditional control flow and logical operation in Go is similar to that of C, Java, Javascript, or almost any braced language (minus the parentheses). They're all fairly simple, so I'll just go over them below. Logical operators: Code: || - or - one or both of the conditions are true
&& - and - both variables are true
! - not - reverses the boolean value of a conditionConditionals: Code: // variables
a,b,c:=true,true,false
// if/else if/else block
if a&&c{
// true and false = false, so this code isn't run
}else if !a||c{
// not true (false) or false = false, so this isn't run, either
}else if a||b{
// true or true = true, so this code is run
}else{
// if nothing else worked, run this
}A quick final tip with anything conditional: some functions return errors as well as their desired value, so you can use semicolons to get the function returns, discard the error, and evaluate the return in the same line. Code: // boo, more code (underscore as variable means discard the value)
boolValue,_:=multiReturnFunction() // lets say this returns bool, error
if boolValue{
// <insert code here>
}
// yay, refactors!
if boolValue,_:=multiReturnFunction();boolValue{
// <insert code here>
}Types Types in Go are extremely easy to make and use using interfaces (which we'll discuss next) or by using pre-defined types. Examples of user-defined types are below. Code: // string container type. This is actually useful later
type stringContainer string
// int slice container
type intSlice []intI mentioned that container types are useful, because you can't use type association on non-user defined types (this includes types not defined in the current package). If, say, you wanted an associated repeat method for strings, you would associate with the string container type. Code: package main
import "fmt"
type stringContainer string
// associate with stringContainer, accept an int as an argument, and return a string called res
func(s stringContainer) repeatString(times int) (res string){
// add s to res "times" times
for i:=1;i<=times;i++{
res=res+string(s)
}
// return res
return
}
func main(){
str:=stringContainer("foo")
fmt.Println(str.repeatString(3))
}Structs Structs in Go work similar to those in Ruby (except without symbols, which is nice[?]). Below is a basic example. Code: package main
import(
"fmt"
"math/rand"
)
type structEx struct{
id int
randomVal int
}
func main(){
// set a, b, and c to structEx structs
// inline struct initialization
a:=structEx{0,rand.Intn(10)}
// attribute init
b:=structEx{}
b.id=1
b.randomVal=rand.Intn(10)
// inline attribute init
c:=structEx{id: 2,randomVal: rand.Intn(10)}
//print a and b
fmt.Println("id","\t","val")
fmt.Println(a.id,"\t",a.randomVal)
fmt.Println(b.id,"\t",b.randomVal)
fmt.Println(c.id,"\t",c.randomVal)
}Structs, when used in conjunction with types, can be used to bypass interface type errors (you can't have functions for interface types) Code: package main
import "fmt"
// raw object type (can't define functions)
type object interface{}
// struct container for interfaces
type container struct{data interface{}}
// function to extract data from a container struct
func(c container) extract() interface{}{
return c.data
}
func main(){
// set a to an object within a container
a:=container{object("test")}
// print the extracted data
fmt.Println(a.extract())
}Interfaces An interface, in its simplest description, is a "named collection of method signatures" (https://gobyexample.com ). What this means (and what you'll see looking pretty much anywhere) is that an interface is more or less a conditional that accepts values who's type matches at least all of the method signatures. A few examples of interfaces are below. Empty interface container: Code: package main
import(
"fmt"
"reflect"
)
type object interface{}
func main(){
i:=10 // set i to int
obj:=object(i) // set obj to an object instance of i
// print value and type
fmt.Println(i,reflect.TypeOf(i))
fmt.Println(obj,reflect.TypeOf(obj))
}From running this, we can see that an interface retains the original type (when reflect.TypeOf() is called on obj, it still returns int. Here's a more complicated, structured example: Code: package main
import "fmt"
/*
a type, sliceSum, accepts values of
types with a sum() function that
returns a float32 value
*/
type sliceSum interface{
sum() float32
}
// container types for float32 and int slices
type floatSliceCont []float32
type intSliceCont []int
// sum function for float slice container
func(c floatSliceCont) sum() (total float32){
for _,i:=range c{total+=i}
return
}
// sum function for int slice container
func(c intSliceCont) sum() (total float32){
for _,i:=range c{total+=float32(i)}
return
}
func main(){
// set a and b to float and int slice containers
a:=floatSliceCont{3.14,2.71,9.9}
b:=intSliceCont{10,15,25}
// set sumA and sumB to sliceSum instances of a and b
sumA:=sliceSum(a)
sumB:=sliceSum(b)
// print the sums of sumA and sumB
fmt.Println(sumA.sum(),sumB.sum())
}End note: I didn't cover packages in this tutorial, because that in itself is a broad topic and there's already a lot in this thread, so I'll cover it next time. |