因为做分布式相关项目需要接触go语言,本文是基于Udemy上的一门go基础课的笔记,主要是代码例子的形式来熟悉go的一些特性,比如struct, map, interface, Channels and Go Routines,适合接触过一些其他编程语言的同学来快速了解go。
Basic project : Card
首先以一个扑克牌的项目为例子来熟悉go的基本语法,涉及variable declaration, type conversion, receiver, slice, multiple return等知识。实现的是扑克中的newDeck(), deal(), shuffle()以及deckToFile()和FiletoDeck()功能。包含实现功能的deck.go文件,可执行的main.go文件,以及用来测试的deck_test.go文件。
Package :executable package & reusable package
package main --> executable package (means go build can make a main.exe)
deck.go
package main
import (
"fmt"
"io/ioutil"
"math/rand"
"os"
"strings"
"time"
)
// Create a new type of "deck"
// which is a slice of strings
type deck []string
func (d deck) print() {
for i, card := range d {
fmt.Println(i, card)
}
}
func newDeck() deck {
cards := deck{}
cardSuits := []string{"Spades", "Diamonds", "Hearts", "Clubs"}
cardValues := []string{"Ace", "Two", "Three", "Four"}
for _, suit := range cardSuits {
for _, value := range cardValues {
cards = append(cards, value+" of "+suit)
}
}
return cards
}
func deal(d deck, handSize int) (deck, deck) {
return d[:handSize], d[handSize:]
}
func (d deck) toString() string {
return strings.Join([]string(d), ",")
}
func (d deck) saveToFile(filename string) error {
return ioutil.WriteFile(filename, []byte(d.toString()), 0666)
}
func newDeckFromfile(filename string) deck {
bs, err := ioutil.ReadFile(filename)
if err != nil {
fmt.Println("Error:", err)
os.Exit(1)
}
s := strings.Split(string(bs), ",")
return deck(s)
}
func (d deck) shuffle() {
source := rand.NewSource(time.Now().UnixNano())
r := rand.New(source)
for i := range d {
newPosition := r.Intn(len(d) - 1)
d[i], d[newPosition] = d[newPosition], d[i]
}
}
main.go
package main
import "fmt"
func main() {
cards := newDeck()
cards.saveToFile("my_cards")
fmt.Println(cards.toString())
hand, remainingCards := deal(cards, 5)
hand.print()
remainingCards.print()
newCards := newDeckFromfile("my_cards")
fmt.Println("new cards:", newCards.toString())
newCards.shuffle()
fmt.Println("shuffled new cards:", newCards.toString())
}
deck_test.go
package main
import (
"os"
"testing"
)
func TestNewDeck(t *testing.T) {
d := newDeck()
if len(d) != 16 {
t.Errorf("Expected deck length of 16, but got %v", len(d))
}
if d[0] != "Ace of Spades" {
t.Errorf("Wrong first card")
}
if d[len(d)-1] != "Four of Clubs" {
t.Errorf("Wrong last card")
}
}
func TestSaveToDeckAndNewDeckTestFromFile(t *testing.T) {
os.Remove("_decktesting")
d := newDeck()
d.saveToFile("_decktesting")
loadedDeck := newDeckFromfile("_decktesting")
if len(loadedDeck) != 16 {
t.Errorf("Expected 16 cards in deck, got %v", len(loadedDeck))
}
os.Remove("_decktesting")
}
Struct
以一个person type为例子,熟悉struct。注意struct是pass by value的,所以要想改变原来的对象,需要以指针为receiver。
package main
import "fmt"
type contactInfo struct {
email string
zipCode int
}
type person struct {
firstName string
lastName string
contactInfo
}
func main() {
// Create a person object
var tom person
tom.firstName = "Tom"
tom.lastName = "Anderson"
// Another way to create a person with initialization
jim := person{
firstName: "Jim",
lastName: "Party",
contactInfo: contactInfo{
email: "jin@gmail.com",
zipCode: 9400,
},
}
// Update value inside the object
jim.updateName("jimmy")
// function use struct person as receiver
jim.print()
}
func (p person) print() {
fmt.Printf("%+v", p)
}
// Need to use pointers, directly, structs are pass by value
func (p *person) updateName(newFirstName string) {
(*p).firstName = newFirstName
}
Map
map有点类似于Python里面的dict。用于函数时传的是地址,可以直接对原来的map修改。概念不难理解,主要是了解以下语法怎么写。
package main
import "fmt"
func main() {
//create a map
colors := map[string]string{
"red": "#ff0000",
"green": "#4bf745",
}
fmt.Println(colors)
//another way to initialize
colors2 := make(map[string]string)
colors2["white"] = "#ffffff"
//delete
map3 := make(map[int]string)
map3[10] = "lalalala"
fmt.Println(map3)
delete(map3, 10)
fmt.Println(map3)
//iterate map
printMap(colors2)
}
func printMap(c map[string]string) {
for color, hex := range c {
fmt.Println("Hex code for", color, "is", hex)
}
}
Interface
interface这个概念在很多编程语言中都有出现。与其他语言一样go里面的interface类型helps reuse code, 不可以用来直接实例化对象。go interfaces are “implicit”,不像Java需要去继承实现一个interface那种manually say this type satisfy certain interface, 以下代码为例,就是有getGreeting()方法的type就自动可以属于bot 。另外go里面没有generic type的概念。
package main
import "fmt"
//Cannot directly create a value out of an interface type
type bot interface {
getGreeting() string
}
type englishBot struct{}
type spanishBot struct{}
func main() {
eb := englishBot{}
sb := spanishBot{}
printGreeting(eb)
printGreeting(sb)
}
func printGreeting(b bot) {
fmt.Println(b.getGreeting())
}
func (eb englishBot) getGreeting() string {
return "Hi, there!"
}
func (sb spanishBot) getGreeting() string {
return "Halo"
}
interface可以嵌套
Another small example to understand interface (learn to read documents)
Main purpose of the project is getting and logging response of http request.
Need to go through go documents and learn about Reader interface and Writer interface
package main
import (
"fmt"
"io"
"net/http"
"os"
)
type logWriter struct{}
func main() {
resp, err := http.Get("http://google.com")
if err != nil {
fmt.Println("Error:", err)
os.Exit(1)
}
// initialize an empty byte slice with 99999 elements
b := make([]byte, 99999)
resp.Body.Read(b)
fmt.Println(string(b))
// better way to do the same thing
io.Copy(os.Stdout, resp.Body)
// use own custom writer
lw := logWriter{}
io.Copy(lw, resp.Body)
}
func (logWriter) Write(bs []byte) (int, error) {
fmt.Println(string(bs))
fmt.Println("Just wrote this many bytes:", len(bs))
return len(bs), nil
}
Channels and Go Routines
Sequence check link
slow: fetch one, complete one, move to next one
package main
import (
"fmt"
"net/http"
)
func main() {
links := []string{
"http://google.com",
"http://facebook.com",
"http://stackoverflow.com",
"http://golang.org",
"http://amazon.com",
}
for _, link := range links {
checkLink(link)
}
}
func checkLink(link string) {
_, err := http.Get(link)
if err != nil {
fmt.Println(link, "might be down!")
return
}
fmt.Println(link, "is up!")
}
Parallel way: Channels and Go Routines
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
links := []string{
"http://google.com",
"http://facebook.com",
"http://stackoverflow.com",
"http://golang.org",
"http://amazon.com",
}
c := make(chan string)
for _, link := range links {
go checkLink(link, c)
}
for l := range c {
go func(link string) {
time.Sleep(5 * time.Second)
checkLink(link, c)
}(l)
}
}
func checkLink(link string, c chan string) {
_, err := http.Get(link)
if err != nil {
fmt.Println(link, "might be down!")
c <- link
return
}
fmt.Println(link, "is up!")
c <- link
}
来源:CSDN
作者:Yichen �
链接:https://blog.csdn.net/yichenl2/article/details/104073359