Go语言入门

因为做分布式相关项目需要接触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://*.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://*.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
}

Go语言入门Go语言入门 Yichen � 发布了1 篇原创文章 · 获赞 0 · 访问量 22 私信 关注
上一篇:流畅的Python 1. Python数据模型(特殊方法 __func__())


下一篇:Project Four: Blackjack