【Golang 1.18】Go! Go! Go! - 可愛又迷人的反派角色

依稀記得Golang剛推出的時候,深深被它的外表所吸引?不不不,筆者不是外貌協會的,其實它已經出現在地球上有很長一段時間了,當初Google在設計它的時候,就是想要語法簡潔高效能,筆者覺得它是取代Node.js - modules 無底洞 / Python - 執行速度 / Java - JVM跨平臺的利器。 最近感同深受,原本我的網誌是使用hexo - Nodejs去製作的,但因為佈景主題不合新版,所以就跳來使用hugo - Golang,雖然hugo的文章大多都是英文的,比起hexo有強大中文資訊來說,的確是筆者的痛點,但是…Build的速度至少差10倍吧。

作業環境

項目 版本
CPU Apple M1
macOS Big Sur 12.3 arm64
Golang 1.18 arm64
Visual Studio Code 1.66 arm64
MySQL 8.0.28 arm64

安裝

安裝Golang / MySQL

brew install go
brew install mysql

安裝VSCode套件

語法Go! Go! Go!

HelloWorld

  • 不免俗的,當然還是要來個HelloWorld啦…
  • 新增一個叫"GoGoGo"的資訊夾,裡面新增一個叫"main.go"的檔案…
  • 是不是覺得它的語法很像C語言呢?
package main

import (
	"fmt"
)

func main() {
	var message string = "Hello, World !!!" // 變數message,是個字串,值為"Hello, World !!!"
	fmt.Println(message)                    // 套件fmt => format工具
}
go run main.go      // 執行main.go
go build main.go    // 編譯main.go成執行檔
go fmt main.go      // 排版程式碼
go env              // 列出環境變數

常數

package main

import (
	"fmt"
)

func main() {
	constant()
}

/// 常數 => 不能改變的數
func constant() {

	const ip string = "127.0.0.1"   // 常數 <名稱: ip> <類型: string> = <值: "127.0.0.1">
	const height = 194.87           // const height float = 194.87 (自動判斷類型)

	const (                         // 也可以這樣合在一起處理
		Male   int = 1
		Female     = 2
	)

	fmt.Printf("ip = %s, height = %.2f, Male = %d\n", ip, height, Male) 
    // ip = 127.0.0.1, height = 194.87, Male = 1
}

變數

  • 可以改變的數
  • Go語言力求整潔,所以在run的時候,如果有沒用到的變數,會不能build,這點筆者覺得滿不錯的…
  • 此外在宣告變數的時候,會自動初始化數值 (也就是沒有nil)
package main

import (
	"fmt"
)

func main() {
	variable()
}

/// 變數 => 可以改變的數
func variable() {

	var number int
	var text string

	message := "自動判斷類型"
	text = "測試中"
	// num1, num2 := 1, 2

	// var (
	// 	a = 1
	// 	b = 2
	// )

	fmt.Printf("number = %d, text = %v, message = %s\n", number, text, message)
}

基本結構

Array - 陣列

package main

import (
	"fmt"
	"reflect"
)

func main() {
	arrayTest()
}

// Array => 在Golang裡面,它的容量大小是不能變動的
func arrayTest() {

	var myArray [3]int = [3]int{1, 20, 3}
	locations := [...]string{"North", "East", "South", "West"}
	numbers := [5]int{}
	_index := 0

	myArray[1] = 100
	numbers[3] = 20

	result := fmt.Sprintf("myArray(%d) => %d\tnumbers(%d) = %x", len(myArray), myArray, len(numbers), numbers)
	fmt.Println(result)

	for _index < len(locations) {
		location := locations[_index]
		fmt.Printf("%s(type => %s)\t", location, reflect.TypeOf(location).Kind())
		_index++
	}
}

Slice - 切片

package main

import (
	"fmt"
	"reflect"
)

func main() {
	sliceTest()
}

// Slice => 就是可變動大小的Array
func sliceTest() {
	people1 := []string{"Aaron", "Jim", "Bob", "Ken"} // 有值 (使用:4 / 容量:4)
	var people2 []string                              // 空的 (使用:0 / 容量:0) => nil
	people3 := make([]string, 4)                      // 空的 (使用:4 / 容量:4)
	people4 := make([]string, 0, 5)                   // 空的 (使用:0 / 容量:5)

	PrintSlice(people1[1:])
	PrintSlice(people2)
	PrintSlice(people3)
	PrintSlice(people4)

	fmt.Println(people1 == nil)

	// people1[4] = "William"
	people1 = append(people1, "William")                 // 增加(擴展)跟people1一樣的空間大小
	PrintSlice(people1)

	copyCount := copy(people3, people1[:len(people1)-2]) // 如果Copy不完整,沒有Copy到的會自動是初始值
	people1 = revomeSliceAtIndex(people1, 2)

	PrintSlice(people1)
	PrintSlice(people3)
	fmt.Println(copyCount)
}

// 列出slice的(長度/容量)
func PrintSlice(slice []string) {
	fmt.Printf("%v: (len=%d, cap=%d)\n", slice, len(slice), cap(slice))
}

// 移除特定位置的Slice => 切開再合起來
func revomeSliceAtIndex(source []string, index int) []string {

	sliceBeforeIndex := source[0:index]
	sliceAfterIndex := source[index+1 : len(source)+0]

	for i := 0; i < len(sliceAfterIndex); i++ {
		sliceBeforeIndex = append(sliceBeforeIndex, sliceAfterIndex[i])
	}

	return sliceBeforeIndex
}

Map - (key, value)

package main

import (
	"fmt"
    "reflect"
)

func main() {
	mapTest()
}

// Map => Key-Value pair
func mapTest() {

	var addresses map[string]string

	ages := make(map[string]int, 10)
	ages["alice"] = 31
	ages["charlie"] = 34

	colors := map[string]string{
		"red":   "#ff0000",
		"green": "#4bf745",
	}

    PrintMap(colors)

	fmt.Printf("addresses => %v(type => %v)\n", addresses, reflect.TypeOf(addresses).Kind())
	fmt.Printf("ages => %v(type => %v)\n", ages, reflect.TypeOf(ages).Kind())

    delete(colors, "red")
	PrintMap(colors)
}

// 列出Map的值 => 因為 map 是 reference type,所以不用使用 pointer
func PrintMap(aMap map[string]string) {
	for color, hex := range aMap {
		fmt.Printf("%v: %v\n", color, hex)
	}
}

package main

import (
	"fmt"
	"reflect"
)

func main() {
	pointerTest()
}

// Pointer => 記憶體位置 / 指標
func pointerTest() {

	var intPoint *int // *int指的是 => intPoint是記憶體位置,該位置放的值是int數字 / *intPoint才是數字
	number := 100

	intPoint = &number     // &number => 指的是number的記憶體位置
	fmt.Println(*intPoint) // *intPoint是數字

	increment(&number)
	fmt.Println(number)

	total, ans := add(36, 57)
	fmt.Println(total, ans)
}

// 數字+1
func increment(number *int) {
	*number++ // *number是數字,數字++才是對的,但因為number是記憶體位置,所以就直接被改了,不用回傳
}

// 兩數字的加法 => 回傳兩個值
func add(a int, b int) (int, string) {
	num := a + b
	return num, "isSuccess" // 因為是傳數值(✖記憶體位置),所以是Copy一份來做加法,要return回去結果才行…
}

Struct - 結構

  • 多個屬性要包在一起時使用
  • 介紹Switch的用法
package main

import (
	"fmt"
)

type Person struct {
	name   string
	age    int
	gender string
}

func main() {
	structTest()
}

// Struct => 有多個屬性要包在一起時使用
func structTest() {

	p0 := Person{}
	p1 := Person{name: "William-1", age: 30, gender: "Male"}
	p2 := Person{"William-2", 3, "Female"}

	People := []Person{p0, p1, p2}
	fmt.Println(People)

	for index := 0; index < len(People); index++ {

		person := People[index]

		switch person.gender {
		case "Male":
			fmt.Println("♂")
		case "Female":
			fmt.Println("♀")
		default:
			fmt.Println("未填寫")
		}
	}
}

範例程式碼下載

後記

  • 寫網誌真很辛苦,尤其又是Go的初學者,萬一不小心成為百萬網紅該怎麼辨呢?來彈首曲子休息一下吧…
  • 寫語法文章很乏味,下一篇就來寫寫跟MySQL的連線跟打API吧…