Go tutorial 笔记

1 variables

1.1 string

var name string = "hello"

or:

var name string
name = "hello"

Both are ok.

1.2 int

var number uint16 = 260

Here uint16 is optional, we can and we should specify the data type, but we don’t have to.

// let golang implicitly define the data type of the number
// let golang guess the type based on 260 instead
var number = 260

ftm.Printf like ‘print format’

var number = 9999999999999999
// go guessed type is: int
fmt.Printf("%T", number)

and

var number = 9999999999999999.1
// go guessed type is: float64
fmt.Printf("%T", number)

1.3 walrus operator :=

Using walrus operator is the easiest and fastest way to declare a variable.

// the var keyword is omitted
// let golang guess the data type
// shortcut of
// var number int = 6
number := 6

1.4 default value

func main() {
  // false
  var bl bool;
  fmt.Println(bl)
}

and:

func main() {
  // 0
  var d float64;
  f mt.Println(d)
}

2 fmt, printf, sprintf

// Hello int 10
fmt.Printf("Hello %T %v", 10, 10)

%g will print the FLOAT as it is, but not for integer.

bool:                    %t
int, int8 etc.:          %d
uint, uint8 etc.:        %d, %#x if printed with %#v
float32, complex64, etc: %g
string:                  %s
chan:                    %p
pointer:                 %p
// Number: 10000000001
fmt.Printf("Number: %b", 1025);

%X for capital letter in hexidecimal

5 console input (bufio scanner) & type conversion

import (
	"bufio"
	"fmt"
	"os"
	// strconv
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	fmt.Printf("Type the year you were born: ")
	scanner.Scan() // always get a string here
	input := scanner.Text()
	fmt.Printf("You typed: %q", input)
}

run the code above:

Type something: hello
You typed: "hello"

scanner.Scan() always get a string, so if a number is needed, the package strconv must be used.
The following code to calculate the age at the end of 2020 based on input:

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	fmt.Printf("Type the year you were born: ")
	scanner.Scan()
	// _ should be error, omitted here
	input, _ := strconv.ParseInt(scanner.Text(), 10, 64)
	fmt.Printf("You will be %d years old at the end of 2020", 2020-input)
}

output:

Type the year you were born: 1960
You will be 60 years old at the end of 2020

6 Arithmetic and math

Go will not perform implicit type conversion:

package main

import (
	"fmt"
)

func main() {

	var num1 float64 = 8
	var num2 int = 4

	// must convert the type to float64, unlike C
	answer := num1 / float64(num2)

	// 2.000000
	fmt.Printf("%f", answer)
}

7 conditions and boolean expressions

< > <= >= == !=

func main() {

	x := 5
	val := x < 5

	// false
	fmt.Printf("%t", val)
}

8 chained conditionals (AND, OR, NOT)

9 if, else if, else

func main() {
	x := 5
	y := 5
	if x == y {
		fmt.Println("if is true")
	} else {
		fmt.Println("if is false")
	}
}

output:

if is true

10 for loops

func main() {
	x := 0

	for x < 6 {
		fmt.Println(x)
		x++
	}

	// same as the above loop
	for x := 0; x < 6; x++ {
		fmt.Println(x)
	}
}

output:

0
1
2
3
4
5
0
1
2
3
4
5
for {

}

equals to

for true {

}

both are infinite loops, keywords break and continue can be used to break the loop

11 switch

11.1 C-like code:

func main() {
	ans := -2
	switch ans {
	case 1, -2:
		fmt.Println("1")
		fmt.Println("2")
	case 2:
		fmt.Println("3")
	default:
		fmt.Println(999)
	}
}

output:

1
2

11.2 The following syntax also valid:

func main() {
	ans := 1
	switch {
	case ans > 0:
		fmt.Println("positive")
	case ans < 0:
		fmt.Println("negative")
	default:
		fmt.Println("zero")
	}
}

12. Arrays

The length of an array is fixed,

func main() {
	// this array can contain 5 strings at the most
	var arr [5]string

	// [     ]
	fmt.Println(arr)
}
func main() {
	// this array can contain 5 strings at the most
	var arr [5]int
	// [0 0 0 0 0]
	fmt.Println(arr)
}

concise syntax:

func main() {
	arr := [5]int{100}
	// [100 0 0 0 0]
	fmt.Println(arr)

}

How to get the length of an array:

arr := [5]int{3, 4, 5, 6, 7}

// 5
fmt.Println(len(arr))

for loop and array:

func main() {
	arr := [3]int{4, 5, 6}
	sum := 0

	for i := 0; i < len(arr); i++ {
		sum += arr[i]
	}

	// 15
	fmt.Println(sum)
}

multi-dimensional array:

arr2D := [2][3]int{{1, 2, 3}, {4, 5, 6}}

// 4
fmt.Println(arr2D[1][0])

13 Slices

slices are built upon arrays and are more powerful

func main() {
	var x [5]int = [5]int{1, 2, 3, 4, 5}
	// a slice !
	var s []int  = x[:]

	// [1 2 3 4 5]
	fmt.Println(s)
}

maybe same kind of syntax of slicing with python:

a[start:stop]  # items start through stop-1
a[start:]      # items start through the rest of the array
a[:stop]       # items from the beginning through stop-1
a[:]           # a copy of the whole array
a[start:stop:step] # start through not past stop, by step
a[-1]    # last item in the array
a[-2:]   # last two items in the array
a[:-2]   # everything except the last two items
# Similarly, step may be a negative number:
a[::-1]    # all items in the array, reversed
a[1::-1]   # the first two items, reversed
a[:-3:-1]  # the last two items, reversed
a[-3::-1]  # everything except the last two items, reversed

function append is used to add new elements to a slice:

func main() {
	var a []int = []int{5, 6, 7, 8, 9};
	a = append(a, 10)
	// [5 6 7 8 9 10]
	fmt.Println(a)
}

with function make:

func main() {
	// 5 is the capacity of the slice
	a := make ([]int, 5)

	// [0 0 0 0 0]
	fmt.Println(a)
}

14 range

	var a[]int = []int{1, 2, 3, 6, 7, 8, 9}
	for index, element := range a {

		// 0 1
		// 1 2
		// 2 3
		// 3 6
		// 4 7
		// 5 8
		// 6 9
		fmt.Println(index, element)
	}

14.1 looking for duplicates, version 1:

func main() {
	var a[]int = []int{1, 3, 4, 56, 7, 12, 4, 6}
	for i, element := range a {
		for j, element2 := range a {
			if 	element == element2 && i != j {
				fmt.Println(i,j,element2)
			}
		}
	}
}

14.2 looking for duplicates, version 2:

func main() {
	var a[]int = []int{1, 3, 4, 56, 7, 12, 4, 6}
	for i, element := range a {
		for j := i + 1; j < len(a); j++ {
			element2 := a[j]
			if element2 == element {
				fmt.Println(element)
			}
		}
	}
}

15 Maps

syntax:

// a map, key is string, value is int
var mp map[string]int

example code:

func main() {
	var mp map[string]int = map[string]int{
		"apple":  5,
		"pear":   6,
		"orange": 9,
	}
	// map[apple:5 orange:9 pear:6]
	fmt.Println(mp)
}

create an empty map:

mp2 := make(map[string]int)

delete, edit, add of map:

func main() {
	var mp map[string]int = map[string]int{
		"apple":  5,
		"pear":   6,
		"orange": 9,
	}

	mp["else"] = 10      // add
	mp["pear"] = -100    // update
	delete(mp, "orange") // delete

	// map[apple:5 else:10 pear:-100]
	fmt.Println(mp)
}

checking if a key exists or not:

var mp map[string]int = map[string]int{
	"apple":  5,
	"pear":   6,
	"orange": 9,
}

// get to know how many keys in the map
fmt.Println(len(mp))

// check if a key exists or not
// exist: ok is true, val = value
// not exist: ok false, val = default of the type
val, ok := mp["apple"]
if (ok) {
	fmt.Print(val)
} else {
	fmt.Println("not exist, default value: ", val)
}

output:

5

16 Functions

func test(x int, y int) {}

// above is the same as
func test(x, y int) {}

syntax:

// 2 returns
func test(x, y, z int) (int, int) {
	return x + y, x - y
}

// 1 return
func test(x, y, z int) int {
	return x + y
}

label return type:

func test(x, y int) (z1 int, z2 int) {
// or as below, also valid:
// func test(x, y int) (z1, z2 int) {

	z1 = x + y
	z2 = x - y

	// no need to write: return z1, z2
	return
}

func main() {
	ans1, ans2 := test(14, 7)

	// 21, 7
	fmt.Println(ans1, ans2)
}

defer:

func test(x, y int) (z1 int, z2 int) {

	// delays a function or method
	// until the function returns
	// usually used for cleanup
	// or to close resources
	defer fmt.Println("hello")

	z1 = x + y
	z2 = x - y
	fmt.Println("function will return!")
	return
}

17 Advanced function concepts

assign function to a variable

func test() {
	fmt.Println("Hello")
}

func main() {
	x := test
	x()

	// equals to
	// test()
}

another example:

func test() {
	fmt.Println("Hello")
}
func main() {
	test := func() {
		fmt.Println("hello")
	}
	test()
}

IIFE: Immediately Invoked Function Expression

func main() {
	test := func(x int) int {
		return x * -1
	}(99)

	// -99
	fmt.Println(test)
}

function that receive a function as parameter

func test2(myFunc func(int) int) {
	fmt.Println(myFunc(7))
}

func main() {
	test := func(x int) int {
		return x * -1
	}

	test2(test)
}

Returning a function inside a function:

func returnFunc(x string) func() {

	// this function called function closure
	return func() {
		fmt.Println(x)
	}

}

func main() {
	returnFunc("hello")()
}

18 mutable & immutable

slice, map are mutable data types

func main() {
	 x := []int{3, 4, 5}
	 y := x
	 y[0] = 100

	 // [100 4 5] [100 4 5]
	 fmt.Println(x, y)
}

array is different:

func main() {
	x := [2]int{3, 4}

	// y makes a copy of x, get [3, 4]
	y := x

	// only y changes to [100, 4]
	// x remains the same
	y[0] = 100

	// [3 4] [100 4]
	fmt.Println(x, y)
}

19 pointers

& ampersand: gets the pointer
* asterik: deference

func main() {
	x := 7
	// 0xc0000a6068
	fmt.Println(&x)
}
func main() {
	x := 7
	y := &x
	// 7 0xc00001a0c8
	fmt.Println(x, y)

	*y = 8

	// 8 0xc00001a0c8
	fmt.Println(x, y)
}

a string example

func changeValue(str *string) {
	*str = "changed!"
}

func changeValue2(str string) {
	str = "changed!"
}

func main() {
	toChange := "hello"
	fmt.Println(toChange)
	changeValue(&toChange)
	fmt.Println(toChange)
}

output:

hello
changed!

and

hello
hello

20 structs

type Point struct {
	x        int32
	y        int32
	isOnGrid bool
}

func main() {
	var p1 Point = Point{1, 2, false}
	var p2 Point = Point{-5, 7, true}
	p1.x = 99
	fmt.Println(p1, p2)
}
type Point struct {
	x        int32
	y        int32
}

func main() {

	// only set x, y use default
	p1 := Point{x: 3}
	// {3 0}
	fmt.Println(p1)
}

use pointer:

type Point struct {
	x int32
	y int32
}

func changeX(pt *Point) {
	pt.x = 100
}

func main() {
	p1 := &Point{y: 3}
	// &{0 3}
	fmt.Println(p1)
	changeX(p1)
	// &{100 3}
	fmt.Println(p1)
}

without pointer:

type Point struct {
	x int32
	y int32
}

func changeX(pt Point) {
	pt.x = 100
}

func main() {
	p1 := Point{y: 3}
	// {0 3}
	fmt.Println(p1)
	changeX(p1)
	// {0 3}
	fmt.Println(p1)
}

The following are the same:

p1 := &Point{y: 3}
(*p1).x = 8

and

p1 := &Point{y: 3}
p1.x = 8

struct in struct:

type Point struct {
	x int32
	y int32
}

type Circle struct {
	radius float64

	// a name is not required
	// center *Point
	*Point
}

func main() {
	c1 := Circle{4.56, &Point{4, 5}}

	// 4
	fmt.Println(c1.x)
}

21 structs methods

A student struct has a method called getAge:

type Student struct {
	name   string
	grades []int
	age    int
}

// (s Student) means this is going to
// act on a Student object
func (s Student) getAge() int {
	return s.age
}

func main() {

	s1 := Student{"Tim", []int{70, 90, 80, 85}, 19}

	// 19, ok
	fmt.Println(s1.getAge())
}

!!The following setAge() method doesn’t work!!

type Student struct {
	name   string
	grades []int
	age    int
}

// (s Student) means this is going to
// act on a Student object
func (s Student) getAge() int {
	return s.age
}

// ! no effect, must use pointer -> (s *Student)
func (s Student) setAge(age int)  {
	s.age = age
}

func main() {
	s1 := Student{"Tim", []int{70, 90, 80, 85}, 19}
	// 19
	fmt.Println(s1.getAge())
	s1.setAge(39)
	// 19, didn't change!
	fmt.Println(s1.getAge())
}

correct version:

type Student struct {
	name   string
	grades []int
	age    int
}

// (s Student) means this is going to
// act on a Student object
func (s Student) getAge() int {
	return s.age
}

func (s *Student) setAge(age int)  {
	s.age = age
}

func main() {
	s1 := Student{"Tim", []int{70, 90, 80, 85}, 19}
	fmt.Println(s1)
	s1.setAge(39)
	fmt.Println(s1)
}

If you have a method that is modifying the object itself, you actually want to make sure that you have the pointer here. For most methods you are always going to want to use the pointer, like:

func (s *Student) setAge(age int)  { }

THE COMMON PRACTICE JUST TAKE THE POINTER ANYWAYS. BECAUSE IT’S NOT REALLY GONNA MATTER IF YOU TAKE THE POINTER VERSUS NOT TAKE IT WHEN YOU ARE RETURNING A VALUE.

type Student struct {
	name   string
	grades []int
	age    int
}


// here (s *Student) will be also ok
func (s Student) getAverageGrade() float32 {
	sum := 0
	for _, v := range s.grades {
		sum += v
	}
	return float32(sum)/float32(len(s.grades));

}

func main() {
	s1 := Student{"Tim", []int{70, 90, 80, 85}, 19}

	// 81.25
	fmt.Println(s1.getAverageGrade())
}

22 interfaces

Interface is a way of looking at a set of related objects or types. For example rectangle and circle are different, but they both have a method called area(), although the implementation are different.
Interface lets you look at these types as if they are the same. Interface lets us define some kind of behaviour that’s similar between objects or similar between types.

package main

import (
	"fmt"
	"math"
)

type rect struct {
	width  float64
	height float64
}

type circle struct {
	radius float64
}

type shape interface {
	// Anything, any type, any struct that has
	// this area() method that returns float64
	// is of type shape.
	// It implements the interface shape.
	area() float64

	// if needed, more methods can be added
	// pow(x int) float64
}

// rect implements the interface shape
// rect is a type of shape
func (r rect) area() float64 {
	return r.width * r.height
}

// circle has implemented the interface shape too
// circle is a type of shape too
func (c circle) area() float64 {
	return math.Pi * c.radius * c.radius
}

func main() {
	c1 := circle{4.5}
	r1 := rect{5, 7}

	// then the below code should work!
	shapes := []shape{c1, r1}

	for _, shape := range shapes {
		fmt.Println(shape.area())
	}
}

output:

63.61725123519331
35

Interfaces can be used as parameter type, return type, variable type, etc, very flexible

func getArea(s shape) float64 {
	return s.area()
}
func main() {
	shapes := []shape{c1, r1}
	for _, shape := range shapes {
		fmt.Println(getArea(shape))
	}
}

If pointer is used:

package main

import (
	"fmt"
	"math"
)

type rect struct {
	width  float64
	height float64
}

type circle struct {
	radius float64
}

type shape interface {
	// Anything, any type, any struct that has
	// this area() method that returns float64
	// is of type shape.
	// It implements the interface shape.
	area() float64

	// if needed, more methods can be added
	// pow(x int) float64
}

func getArea(s shape) float64 {
	return s.area()
}

// rect implements the interface shape
// rect is a type of shape
func (r *rect) area() float64 {
	return r.width * r.height
}

// circle has implemented the interface shape too
// circle is a type of shape too
func (c *circle) area() float64 {
	return math.Pi * c.radius * c.radius
}

func main() {
	c1 := circle{4.5}
	r1 := rect{5, 7}

	shapes := []shape{&c1, &r1}

	for _, shape := range shapes {
		fmt.Println(getArea(shape))
	}
}

An object can implement multiple interfaces, not just one.


youtube 视频:
https://www.youtube.com/watch?v=lh_Uv2imp14 1 ~ 22

你可能感兴趣的:(golang)