learn go

Intro

Go 語言是由 Google 開發的開放原始碼項目,目的之一為了提高開發人員的程式設計效率。 Go 語言語法靈活、簡潔、清晰、高效。它對的並發特性可以方便地用於多核處理器 和網絡開發,同時靈活新穎的類型系統可以方便地撰寫模組化的系統。Go 可以快速編譯, 同時具有記憶體垃圾自動回收功能,並且還支持運行時反射。Go 是一個高效、靜態類型, 但是又具有解釋語言的動態類型特徵的系統級語法。

這邊有兩個可以選擇

GVM 會自動添加 script 到 ~/.bashrc , 但是筆者使用 fish shell

add gvm.fish to ~/.config/fish/functions/gvm.fish

content

function gvm
  bass source ~/.gvm/scripts/gvm ';' gvm $argv
end

Example

HelloWorld

package main
import "fmt"

func main()  {
    fmt.Println("Hello, World")
}
go run helloworld.go

golang 與 C 概念類似,一個 program 中只能有一個 main import 方面則與 Python 有些類似

兩種方式 run program

  1. go run program.go compile (save in temporary dir)and run
  2. go build program.go; ./program 在 local build 出 exectable file, and execute

more print

package main
import "fmt"

func main()  {
    // math
    fmt.Println("1 + 1 = ", 1+1)
    fmt.Println("1 - 1 = ", 1-1)
    fmt.Println("1 * 1 = ", 1*1)
    fmt.Println("1 / 1 = ", 1/1)
    fmt.Println("1 % 1 = ", 1%1)

    // string
    fmt.Println("\"Hello, Wolrd\" have ", len("Hello, World"), "words")
    // accesss particular character in string
    fmt.Println("Hello, World"[1])
    // concate
    fmt.Println("Hello, " + "World")

    //bool
    fmt.Println(true && true)
    fmt.Println(true && false)
    fmt.Println(true || true)
    fmt.Println(true || false)
    fmt.Println(!true)
    fmt.Println(!false)
}

Variables_Types

用 var 來定義變數,跟其他語言不大箱同的是,Go 必須將型態宣告寫在後面。

Basic types

  • bool
  • string
  • int, int8, int16, int32, int64
  • uint, uint8, uint16, uint32, uint64, uintptr
  • byte // alias for uint8
  • rune // alias for int32, represents a Unicode code point
  • float32 float64
  • complex64, complex128

初始化變數

如果要同時宣告不同的變數,也可以用小括弧把變數括起來,但是一定要換行。

package main

import "fmt"

var x, y, z int
var yes, no bool

func main() {
    fmt.Println(x, y, z, c, yes, no)
}
package main

import "fmt"

var (
	x int
	y int
	z int
	c bool
	yes bool
	no bool
)

func main() {
    fmt.Println(x, y, z, c, yes, no)
}

定義變數時可以直接賦予初始值,變數與賦予的值要互相對應。如果有初始化的話,型別就可以省略;變數會直接取用初始化的類型

package main

import "fmt"

var x, y, z int = 1, 2, 3
var yes, no = true, false

func main() {
    fmt.Println(x, y, z, yes, no)
}

短變數宣告

package main

import "fmt"

var x, y, z int = 1, 2, 3
yes, no := true, false

func main() {
    fmt.Println(x, y, z, yes, no)
}
var a  // 不定型別的變數
var a int // 宣告成 int
var a int = 10 // 初始化同時宣告
var a, b int // a 跟 b 都是 intvar a, b = 0, ""
var a int , b string
a := 0
a, b, c := 0, true, "tacolin" // 這樣就可以不同型別寫在同一行
var(
    a bool = false // 記得要不同行,不然會錯
    b int
    c = "hello"
)

強制轉型

浮點數 複數包涵兩種型態 float32、float64。 與其他語言相同, Go 當然也可以用浮點數。 要注意的是,如果初始化的時候沒有加小數點會被推斷為整數,另外初始化的時候沒有指定型態的話,會被自動推斷為 float64。 float64 在 Go 語言相當於 C 語言的 Double,且 float32 與 float64 是無法一起計算的,需要強制轉型。

// 這是強制轉型
轉型型態(變數)
package main

import "fmt"

func main() {
    var floatValue float64
    floatValue = 7.0
    var floatValue2 = 3.0
    fmt.Println("7.0/3.0 =", floatValue/floatValue2)
    var test float64
    var test2 float32
    test = 1.1
    test2 = 2.2
    fmt.Println("test + test2 =", float32(test) + test2)
}

string

package main
import "fmt"
func main() {
    var x string = "Hello, World"
    fmt.Println(x)
    fmt.Println(len("Hello World"))
    fmt.Printf("%c",x[1])
}
package main
import "fmt"
func main() {
    var x string
    x = "first"
    fmt.Println(x)
    x = x + "second"
    fmt.Println(x)
}

bool

如果是初始化讓 Go 自動判斷型態的型態會是 false,而不會是 true

package main

import "fmt"

func main() {
var a bool
    a = true
    fmt.Println("a =", a)

    b := false
    fmt.Println("b =", b)

    fmt.Println(true && true)
    fmt.Println(true && false)
    fmt.Println(true || true)
    fmt.Println(true || false)
    fmt.Println(!true)
}

複數

complex64, complex128

如果是初始化讓 Go 自動判斷型態的型態會是 complex128,而不會是 complex64

package main

import "fmt"

func main() {
    var complexValue complex64
    complexValue = 1.2 + 12i
    complexValue2 := 1.2 + 12i
    complexValue3 := complex(3.2, 12)

    fmt.Println("complexValue =", complexValue)
    fmt.Println("complexValue2 =", complexValue2)
    fmt.Println("complexValue3 =", complexValue3)

    fmt.Println("complexValue3 實數 =", real(complexValue3))
    fmt.Println("complexValue3 虛數 =", imag(complexValue3))
}

Scope

以下兩個範例 執行起來結果是相同 然而 Scope 看到的卻是不同

這邊 variable x 只存在 main

package main
import "fmt"
func main() {
    var x string = "Hello, World"
    fmt.Println(x)
}

這邊 variable x 是全域變數

package main
import "fmt"
var x string = "Hello, World"
func main() {
    fmt.Println(x)
}
func f() {
    fmt.Println(x)
}

這邊再舉另外一個例子 編譯時就會發生錯誤 .\main.go:11: undefined: x

variable x 是區域變數 只存在 main function, f function 看不到 main 的內容, 而 Println 卻要使用 x, 從而得到 undefined: x

package main
import "fmt"
func main() {
    var x string = "Hello, World"
    fmt.Println(x)
}
func f() {
    fmt.Println(x)
}

Constant

const 使用方式與 C 相同

package main
import "fmt"
func main() {
    const x string = "Hello, World"
    fmt.Println(x)
}
package main
import "fmt"
func main() {
    const x string = "Hello, World"
    fmt.Println(x)
    x = "Some other string"
    // this will cause error, reassign to a const var
}

Exercises

接收使用著輸入數字 輸出數字的兩倍

package main
import "fmt"
func main() {
    fmt.Print("Enter a number: ")
    var input float64
    fmt.Scanf("%f", &input)
    output := input * 2
    fmt.Println(output)
}

Celsius to Fahrenheit

package main
import "fmt"
func main() {
    fmt.Print("Enter a Celsius degree: ")
    var input float64
    fmt.Scanf("%f", &input)
    output := (input + 40) * 1.8 - 40
    fmt.Printf("current temperature Celsius: %f to Fahrenheit: %f", input, output)
}

Feets to Meters

package main
import "fmt"
func main() {
    fmt.Print("Enter a feet length: ")
    var input float64
    fmt.Scanf("%f", &input)
    output := input * 0.3048
    fmt.Printf("current feets: %f to meters: %f", input, output)
}

Flow

for loop

package main
import "fmt"
func main() {
    for i := 1; i <= 10; i++ {
        fmt.Println(i)
    }
}

跟 C 或者 Java 中一樣,可以讓前置、後置語句為空

package main
import "fmt"
func main() {
    i := 1
    for i <= 10 {
        fmt.Println(i)
        i = i + 1
    }
}

for 也是 go 的 while loop

無窮迴圈

package main

func main() {
    for {
    }
}

break, continue

break if i is 7

package main
import "fmt"
func main() {
    i := 1
    for i <= 10 {
        if i == 7 {
            break
        }
        else if i % 2 == 0 {
            fmt.Println(i)
        }
        i = i + 1
    }
}

loop 1 to 10, print odd number

package main
import "fmt"
func main() {
    for i := 0; i < 10; i++ {
        if i % 2 == 0 {
            continue
        }
        fmt.Println(i)
    }
}

if

package main
import "fmt"
func main() {
    for i := 1; i <= 10; i++ {
        if i % 2 == 0 {
            fmt.Println(i, "even")
        }
        else {
            fmt.Println(i, "odd")
        }
    }
}
package main

import (
   "fmt"
   "math"
)

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

for 一樣, if 語句可以在條件之前執行一個簡單的語句。 由這個語句定義的變數的作用範圍僅在 if 範圍之內。

package main

import (
    "fmt"
    "math"
)

func pow(x, n, lim float64) float64 {
    if v := math.Pow(x, n); v < lim {
        return v
    }
    return lim
}

func main() {
    fmt.Println(
        pow(3, 2, 10),
        pow(3, 3, 20),
    )
}

swtich

Switch 是比 if 更有閱讀性的控制結構,跟 C 語言不同的是他不需要 break 來跳出

如果使用 fallthrough 關鍵字,會執行下一個 Case

package main
import "fmt"
func main() {
    fmt.Print("Enter a int number(1-5): ")
    var input int
    fmt.Scanf("%d", &input)
    switch input {
    case intput < 0 :
        fmt.Println("small than zero")
    case 0: fmt.Println("Zero")
    case 1: fmt.Println("One")
    case 2: fmt.Println("Two")
    case 3: fmt.Println("Three")
    case 4: fmt.Println("Four")
    case 5: fmt.Println("Five")
    case 6, 7, 8:
        fallthrough
    default: fmt.Println("Unknown Number")
    }
}

Array

Go 語言還有其它種內建的型態

  1. 陣列
  2. 切片
  3. Map

陣列

var x [5]int 宣告一個 int array 長度為 5, 值默認為 0

var x [5]int
y := [5]float64{ 98, 93, 77, 82, 83 }
z := [5]float64{
    98,
    93,
    77,
    82,
    83,
}
package main
import "fmt"
func main() {
    var x [5]int
    x[4] = 100
    fmt.Println(x)
}

output

[0 0 0 0 100]

總和一個 list 的值

package main
import "fmt"
func main() {
    var x [5]float64
    x[0] = 98
    x[1] = 93
    x[2] = 77
    x[3] = 82
    x[4] = 83
    var total float64 = 0
    for i := 0; i < 5; i++ {
        total += x[i]
    }
    fmt.Println(total / 5)

這個範例假如你的 array 使用 int , total 使用 float64

你需要注意型態轉換

package main

import "fmt"

func main() {
    var x [5]int
    x[0] = 98
    x[1] = 93
    x[2] = 77
    x[3] = 82
    x[4] = 83
    var total float64 = 0
    for i := 0; i < len(x); i++ {
        total += float64(x[i])
    }
    fmt.Println(total / float64(len(x)))
}

使用 range

package main
import "fmt"
func main() {
    var x [5]float64
    x[0] = 98
    x[1] = 93
    x[2] = 77
    x[3] = 82
    x[4] = 83
    var total1 float64 = 0
    // for loop through array, like python enumerate(list)
    for i, value := range x {
        total1 += value
        fmt.Println(i, value)
    }
    fmt.Println(total1 / float64(len(x)))

    // for loop through array
    // use under_score if we dont need index
    for _, value := range x {
        total1 += value
    }
    fmt.Println(total1 / float64(len(x)))
}

Slices

Function

package main

import "fmt"

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

func main() {
	fmt.Println(add(42, 13))
}
package main

import (
	"fmt"
	"math"
	"math/rand"
)

func main() {
	fmt.Println("My favorite number is", rand.Intn(10))
	fmt.Println(math.Pi)
}

multi result

package main

import "fmt"

func swap(x, y string) (string, string) {
	return y, x
}

func main() {
	a, b := swap("hello", "world")
	fmt.Println(a, b)
}

refer