var name string = "hello"
or:
var name string
name = "hello"
Both are ok.
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)
:=
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
func main() {
// false
var bl bool;
fmt.Println(bl)
}
and:
func main() {
// 0
var d float64;
f mt.Println(d)
}
// 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
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
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)
}
< > <= >= == !=
func main() {
x := 5
val := x < 5
// false
fmt.Printf("%t", val)
}
func main() {
x := 5
y := 5
if x == y {
fmt.Println("if is true")
} else {
fmt.Println("if is false")
}
}
output:
if is true
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
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
func main() {
ans := 1
switch {
case ans > 0:
fmt.Println("positive")
case ans < 0:
fmt.Println("negative")
default:
fmt.Println("zero")
}
}
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])
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)
}
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)
}
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)
}
}
}
}
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)
}
}
}
}
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
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
}
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")()
}
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)
}
&
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
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)
}
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())
}
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