Go Programming

Go Programming

May 31, 2020
go, programming

tags
Programming Computer Science

Related notes: Interesting Golang libraries

Packages #

In Go, programs start running in package main. Package names are defined by the last element of the import path: import math/rand has files which begin with the package rand. Packages consists of a bunch of .go files.

Package identifiers (functions, variables, struct and other data), may be used in other packages, with a few exceptions. Go allows only exported identifiers to be called after the package import. An exported identifier is any identifier which the first character starts in UPPER CASE. Any identifier which starts with a lower case letter is not exported.

Exported identifiers can be thought of public and private we see in other languages. This approach allows us to separate public API by using upper case character from private logic.

Private identifier within a package may be referenced within its package.

Functions #

A function can have 0 or more arguments. All arguments must be typed: x int, y int, when 2 or consecutives arguments share the same type, arguments may be defined as so: x, y int.

Functions can return 0 or more number of results: return x, y. Function return values may be named, and must be defined after the function declaraction and before the beginning {

import "fmt"

func foobar(x, y int) (z int){
	z = x * y
	return
}

func main(){
	x := foobar(3, 3) // should return 9
	fmt.Println(x)
}

9

The empty return will return the z value. Named return values should be used to document the meaning of the return values. Named return values should be used on short functions, as named values in large functions may become confusing.

Arrays #

An array can be defined by [x]T of n values of type T. In GO, we cannot defined an array size dynamically on run time, thus the following will not work:

import "fmt"

func array(size int) ([]int){
	var arr[size]int
	for i := 0; i < size; i++ {
		arr[i] = i * 2
	}
	return arr
}

func main(){
	fmt.Println(array(10))

}

But the following will work:

import "fmt"

func array() ([10]int){
	var arr[10]int
	for i := 0; i < 10; i++ {
		arr[i] = i * 2
	}
	return arr
}

func main(){
	fmt.Println(array())
}

[0 2 4 6 8 10 12 14 16 18]

Slices #

Slice expressions construct a substring or slice from a string, array, pointer to array, or slice.

Arrays are convenient but limiting. Sizes are fixed and no way of resizing. Go provides slices, which can be dynamically reallocated. Like Python, Go slices can be formed by specifying slices with a low and high bound:

import "fmt"

func array() (*[10]int){
	var arr[10]int
	for i := 0; i < 9; i++ {
		arr[i] = i * 2
	}
	return &arr
}

func main(){
	arr := array()
	fmt.Println(arr[0:5])
	fmt.Println(arr[0:2])

	/* https://golang.org/ref/spec#Address_operators */
	arr1 := array()[0:5]
	x := arr1[1:2]
	x[0] = -1
	fmt.Println(arr1, x)
}

Slices are references to arrays #

A slice describes a section of an underlying array, it does not store any data. If X is an array and Y a slice of x[0:1], changing index 0 will change all the value of slices that share the same underlying array.

import "fmt"

func main() {
	x := [6]int {1, 2, 3, 4, 5, 6}
	y := x[0:2]
	fmt.Println(y, x)
	y[0] = 10
	fmt.Println(y, x)
}

Length and capacity #

A slice has a capacity and length. The length is the number of elements the current slice has, and the capacity is the number of elements the underlying slice has counting from the first element of the slice

import "fmt"

func main() {
	slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	fmt.Printf("Slice: %v\n", slice)

	fmt.Printf("Cap of slice (%p): %d\n", slice, cap(slice))
	fmt.Printf("Len of slice (%p): %d\n", slice, len(slice))

	subslice := slice[0:5]

	fmt.Printf("Cap of slice (%p): %d\n", subslice, cap(subslice))
	fmt.Printf("Len of slice (%p): %d\n", subslice, len(subslice))

	/* extend subslice */
	extended := subslice[:10]

	fmt.Printf("Cap of slice (%p): %d\n", extended, cap(extended))
	fmt.Printf("Len of slice (%p): %d\n", extended, len(extended))

	shrink := subslice[:2]

	fmt.Printf("Cap of slice (%p): %d\n", shrink, cap(shrink))
	fmt.Printf("Len of slice (%p): %d\n", shrink, len(shrink))

	// drop first 2 items
	subslice = subslice[2:]

	fmt.Printf("Cap of slice (%p): %d\n", subslice, cap(subslice))
	fmt.Printf("Len of slice (%p): %d\n", subslice, len(subslice))

	fmt.Printf("slice: %v\n", slice)
	fmt.Printf("subslice: %v\n", subslice)
	fmt.Printf("extended: %v\n", extended)
	fmt.Printf("shrink: %v\n", shrink)

}

CURRENT Creating dynamically-sized slices #

Dynamically-sized slices can be created with make

import "fmt"

func main(){
	arr := make([]string, 5) /* slice of strings of length 5 */
	fmt.Printf("Cap: %d, Len: %d, %v", cap(arr), len(arr), arr)

	arr[0] = "Hello, world"
	fmt.Println(arr)

}