Go: Basics

Packages

package main

import (
	"fmt"
	"math/rand"
)

Exported names

func main() {
    
    
	fmt.Println(math.Pi)
}

Functions

func add(x, y int) int {
    
    
	return x + y
}

// multiple results
func swap(x, y string) (string, string) {
    
    
	return y, x
}

// named-return values
func split(sum int) (x, y int) {
    
    
	x = sum * 4 / 9
	y = sum - x
	return
}

Variables

// package level var
var c, python, java bool

// func level var
func main() {
    
    
	var i int
	fmt.Println(i, c, python, java)
}

// var with initializer
var i, j int = 1, 2
func main() {
    
    
	var c, python, java = true, false, "no!"
	fmt.Println(i, j, c, python, java)
}

// short var declaration
k := 3 // invalid syntax
func main() {
    
    
	c, python, java := true, false, "no!"
	fmt.Println(c, python, java)
}

/* basic types
bool string int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte = uint8
rune = int32
float32 float64
complex64 complex128
*/

The int, uint, and uintptr types are usually 32 bits wide on 32-bit systems and 64 bits wide on 64-bit systems. When you need an integer value you should use int unless you have a specific reason to use a sized or unsigned integer type.

var i int = 42
var f float64 = float64(i) // require explicit conversion

// type inference
var i = "42"
fmt.Printf("i is of type %T\n", i)

// const
const Pi = 3.14
const World = "世界" // const cannot be declared using the := syntax
const (
	Big = 1 << 100
	Small = Big >> 99 // An untyped constant takes the type needed by its context.
	
)

For

// go for loop requires braces {} but no parentheses
func main() {
    
    
	sum := 0
	for i := 0; i < 10; i++ {
    
    
		sum += i
	}
	fmt.Println(sum)
}

// while loop
func main() {
    
    
	sum := 1
	for sum < 1000 {
    
    
		sum += sum
	}
	fmt.Println(sum)
}

// while true
func main() {
    
    
	for {
    
    
	
	}
}

If

func sqrt(x float64) string {
    
    
	if x < 0 {
    
    
		return fmt.Sprint(math.Sqrt(-x)) + "i"
	}
	return fmt.Sprint(math.Sqrt(x))
}

// if with a short statement
// var v is invalid outside if
func pow(x, n, lim float64) float64 {
    
    
	if v := math.Pow(x, n); v < lim {
    
    
		return v
	}
	return lim
}

// if and else
func pow(x, n, lim float64) float64 {
    
    
	if v := math.Pow(x, n); v < lim {
    
    
		return v
	} else {
    
    
		fmt.Printf("%g >= %g\n", v, lim)
	}
	// can't use v here, though
	return lim
}

Switch

func main() {
    
    
	fmt.Print("Go runs on ")
	switch os := runtime.GOOS; os {
    
    
	case "darwin":
		fmt.Println("OS X.")
	case "linux":
		fmt.Println("Linux.")
	default:
		fmt.Printf("%s.\n", os)
	}
}

// switch with no condition
func main() {
    
    
	t := time.Now()
	switch {
    
    
	case t.Hour() < 12:
		fmt.Println("Good morning!")
	case t.Hour() < 17:
		fmt.Println("Good afternoon.")
	default:
		fmt.Println("Good evening.")
	}
}

Defer

A defer statement defers the execution of a function until the surrounding function returns. The deferred call’s arguments are evaluated immediately, but the function call is not executed until the surrounding function returns.
Deferred function calls are pushed onto a stack. When a function returns, its deferred calls are executed in last-in-first-out order.

A defer statement pushes a function call onto a list. The list of saved calls is executed after the surrounding function returns. Defer is commonly used to simplify functions that perform various clean-up actions.

More types

Pointers and struct

// pointers
// go has no pointer arithmetic
var p *int
i := 42
p = &i
*p = 21 // set i through a pointer

// struct
// a struct is a collection of fields
type Vertex struct {
    
    
	X int
	Y int
}
func main() {
    
    
	v := Vertex{
    
    1, 2}
	v.X = 4 // struct fields are accessed using a dot
	fmt.Println(v.X)
}

Struct fields can be accessed through a struct pointer.
To access the field X of a struct when we have the struct pointer p we could write (*p).X. However, that notation is cumbersome, so the language permits us instead to write just p.X, without the explicit dereference.

type Vertex struct {
    
    
	X int
	Y int
}
func main() {
    
    
	v := Vertex{
    
    1, 2}
	p := &v
	p.X = 1e9
	fmt.Println(v)
}

A struct literal denotes a newly allocated struct value by listing the values of its fields.
You can list just a subset of fields by using the Name: syntax. (And the order of named fields is irrelevant.)
The special prefix & returns a pointer to the struct value.

type Vertex struct {
    
    
	X, Y int
}
var (
	v1 = Vertex{
    
    1, 2}  // has type Vertex
	v2 = Vertex{
    
    X: 1}  // Y:0 is implicit
	v3 = Vertex{
    
    }      // X:0 and Y:0
	p  = &Vertex{
    
    1, 2} // has type *Vertex
)

Array

The type [n]T is an array of n values of type T.
An array’s length is part of its type, so arrays cannot be resized.

func main() {
    
    
	var a[2] string // normal declare
	a[0] = "Hello"
	a[1] = "World"
	primes := [6]int{
    
    2, 3, 5, 7, 11, 13} // short declare
}

Slice basic

The type []T is a slice with elements of type T.
A slice is formed by specifying two indices, a low and high bound, separated by a colon:
a[low : high]
This selects a half-open range which includes the first element, but excludes the last one.

func main() {
    
    
	primes := [6]int{
    
    2, 3, 5, 7, 11, 13}
	var s []int = primes[1:4] // s = {3, 5, 7}
}

Slice are like reference to array

A slice does not store any data, it just describes a section of an underlying array.
Changing the elements of a slice modifies the corresponding elements of its underlying array.
Other slices that share the same underlying array will see those changes.

This is an array literal:
[3]bool{true, true, false}
And this creates the same array as above, then builds a slice that references it:
[]bool{true, true, false}

Slice defaults

When slicing, you may omit the high or low bounds to use their defaults instead.

The default is zero for the low bound and the length of the slice for the high bound.
For the array var a [10]int
these slice expressions are equivalent:

a[0:10]
a[:10]
a[0:]
a[:]

Slice length and capacity

A slice has both a length and a capacity.
The length of a slice is the number of elements it contains.
The capacity of a slice is the number of elements in the underlying array, counting from the first element in the slice.
The length and capacity of a slice s can be obtained using the expressions len(s) and cap(s). You can extend a slice’s length by re-slicing it, provided it has sufficient capacity.

func main() {
    
    
	s := []int{
    
    2, 3, 5, 7, 11, 13}
	printSlice(s)
	// Slice the slice to give it zero length.
	s = s[:0]
	printSlice(s)
	// Extend its length.
	s = s[:4]
	printSlice(s)
	// Drop its first two values.
	s = s[2:]
	printSlice(s)
}
func printSlice(s []int) {
    
    
	fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

The “zero value” of a slice is nil.
A nil slice has a length and capacity of 0 and has no underlying array.

Slice make

Slices can be created with the built-in make function; this is how you create dynamically-sized arrays. The make function allocates a zeroed array and returns a slice that refers to that array:

a := make([]int, 5)  // len(a)=5

To specify a capacity, pass a third argument to make:

b := make([]int, 0, 5) // len(b)=0, cap(b)=5
b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:]      // len(b)=4, cap(b)=4

Slice append

func append(s []T, vs ...T) []T

var s []int
s = append(s, 2, 3, 4)

The first parameter s of append is a slice of type T, and the rest are T values to append to the slice.
The resulting value of append is a slice containing all the elements of the original slice plus the provided values.
If the backing array of s is too small to fit all the given values a bigger array will be allocated. The returned slice will point to the newly allocated array.

For + range

The range form of the for loop iterates over a slice or map.
When ranging over a slice, two values are returned for each iteration. The first is the index, and the second is a copy of the element at that index.

var pow = []int{
    
    1, 2, 4, 8, 16, 32, 64, 128}
func main() {
    
    
	for i, v := range pow {
    
    
		fmt.Printf("2**%d = %d\n", i, v)
	}
}

You can skip the index or value by assigning to _.

for i, _ := range pow
for _, value := range pow

If you only want the index, you can omit the second variable.

for i := range pow

Map

A map maps keys to values.
The zero value of a map is nil. A nil map has no keys, nor can keys be added.
The make function returns a map of the given type, initialized and ready for use.

type Vertex struct {
    
    
	Lat, Lang float64
}
var m map[string]Vertex
func main() {
    
    
	m = make(map[string]Vertex)
	m["Bell Labs"] = Vertex{
    
    
		Lat: 40.68433,
		Lang: -74.39967, // "," is required even after last field
	}
	fmt.Println(m["Bell Labs"])
}

// map literals
var m = map[string]Vertex{
    
    
	"Bell Labs": Vertex{
    
    
		40.68433, -74.39967,
	},
	"Google": Vertex{
    
    
		37.42202, -122.08408,
	},
}

// If the top-level type is just a type name, you can omit it from the elements of the literal.
var m = map[string]Vertex{
    
    
	"Bell Labs": {
    
    40.68433, -74.39967},
	"Google":    {
    
    37.42202, -122.08408},
}

Mutating maps

m[key] = elem // Insert or update an element in map m
elem = m[key] // Retrieve an element
delete(m, key) // Delete an element

// Test that a key is present with a two-value assignment:
elem, ok = m[key]
// If key is in m, ok is true. If not, ok is false. If key is not in the map, then elem is the zero value for the map's element type.
// Note: If elem or ok have not yet been declared you could use a short declaration form:
elem, ok := m[key]

Function values

func compute(fn func(float64, float64) float64) float64 {
    
    
	return fn(3, 4)
}
func main() {
    
    
	hypot := func(x, y float64) float64 {
    
    
		return math.Sqrt(x*x + y*y)
	}
	fmt.Println(hypot(5, 12))

	fmt.Println(compute(hypot))
	fmt.Println(compute(math.Pow))
}

Function closures

Go functions may be closures. A closure is a function value that references variables from outside its body. The function may access and assign to the referenced variables; in this sense the function is “bound” to the variables.

A closure is a persistent scope which holds on to local variables even after the code execution has moved out of that block. Languages which support closure (such as JavaScript, Swift, and Ruby) will allow you to keep a reference to a scope (including its parent scopes), even after the block in which those variables were declared has finished executing, provided you keep a reference to that block or function somewhere.

The scope object and all its local variables are tied to the function and will persist as long as that function persists.

This gives us function portability. We can expect any variables that were in scope when the function was first defined to still be in scope when we later call the function, even if we call the function in a completely different context.

package main
import "fmt"
// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
    
    
	i, j := 0, 1
	return func() int {
    
    
		k := i + j
		ret := i
		i = j
		j = k
		return ret
	}
}
func main() {
    
    
	f := fibonacci()
	for i := 0; i < 10; i++ {
    
    
		fmt.Println(f())
	}
}

Guess you like

Origin blog.csdn.net/Hita999/article/details/112485487