Playlist:
Scanf vs bufio
Source code: guessing-game/v5
The source file uses the following code block to handle the inputstream and type convertion:
reader := bufio.NewReader(os.Stdin)
for {
input, err := reader.ReadString('\n')
if err != nil {
fmt.Println("An error occured while reading input. Please try again", err)
continue
}
input = strings.Trim(input, "\r\n")
guess, err := strconv.Atoi(input)
if err != nil {
fmt.Println("Invalid input. Please enter an integer value")
continue
}
...
}
However, in this case, we can use fmt.Scanf as a shorthand. It implictly helps you find and read the input intger into the guess, omit non-integer inputs and meanless linebreaks.
var guess int
for {
fmt.Scanf("%d", &guess)
...
}
When should we use bufio:
- Use
bufiowhen you want to read or write data more efficiently, especially in cases where you are reading or writing data in small chunks. The buffering provided bybufiocan significantly reduce the number of system calls, leading to better performance. - Use
bufio.Readerwhen you want to read data line by line from an input source, especially when processing large text files where reading line by line is more memory-friendly. - Use
bufio.Writerwhen you need to write data in smaller chunks, and you want to take advantage of buffering to reduce the number of writes.
Goroutine
Go program is a threaded program. You can have multiple threads owning seperate PCs, seperate sets of registers and seperate stacks in one single execution. All things in go including the func main() are goroutine s. Though goroutine offers great convinience to write serial codes for threaded program, we still need to take care of some implict challenges like data sharing (data race, use flag -race), coordination (channels, sync.Cond, sync.WaitGroup) and deadlock.
Running a single go program creates a new process in the machine and gets its own memory address. Each thread (goroutine) created by the process shares it.
Web Crawler
Playground: Ex: Web Crawler
Avoid race
type concurrentMap struct {
mu sync.Mutex
fetched map[string]bool
}
var cm concurrentMap
// Crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func Crawl(url string, depth int, fetcher Fetcher) {
if depth <= 0 {
return
}
cm.mu.Lock()
already := cm.fetched[url]
cm.fetched[url] = true
cm.mu.Unlock()
if already {
return
}
body, urls, err := fetcher.Fetch(url)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("found: %s %q\n", url, body)
var done sync.WaitGroup
for _, u := range urls {
done.Add(1)
go func(u string) {
defer done.Done()
Crawl(u, depth-1, fetcher)
}(u)
}
done.Wait()
return
}
Coordination
Channels actually has a Mutex object in it to protect concurrent usages for recieving and sending massages. The Mutex object is hidden because go programmer should not care about traditional lock-based synchronization (abstraction). Golang encourages a communication-based approach using goroutines and channels to win concurrent efficiency with little overhead.
func worker(url string, ch chan []string, fetcher Fetcher) {
urls, err := fetcher.Fetch(url)
if err != nil {
ch <- []string{}
} else {
ch <- urls
}
}
func master(ch chan []string, fetcher Fetcher) {
n := 1
fetched = make(map[string]bool)
for urls := range ch {
for _, u := range urls {
if fetched[u] == false {
feteched[u] == true
n += 1
go worker(u, ch, Fetcher)
}
}
n -= 1
if n == 0 {
break
}
}
}
func main() {
ch := make(chan []string)
go func() {
ch <- []string{url}
}
master(ch, Fetcher)
}
Simple Dict
Source code: simpledict/v4
Spec: fires up a new request to another online translator and works concurrently with the current one for better effieciency.
var done sync.WaitGroup
done.Add(1)
go func(w string) {
defer done.Done()
query(w, 0)
}(word)
go func(w string) {
defer done.Done()
query(w, 1)
}(word)
done.Wait()
Flaws. Occasionally, the fmt.Println() prints ahead. It is better to return the fmt.Sprint()object and print the reponse in func main().
➜ go-by-example git:(master) ✗ go run simpledict/v4/main.go hello
1 hello UK: [ˈheˈləu] US: [həˈlo]
int.喂;哈罗
n.引人注意的呼声
v.向人呼(喂)
0 hello UK: [ˈheˈləu] US: [həˈlo]
int.喂;哈罗
n.引人注意的呼声
Go to project
Go Module
Document: Using Go Modules
- Initializing Go Modules: You can use the
go mod initcommand to initialize a new Go module in your project. The command should be executed in the root directory of your project, and it creates thego.modfile that will track the project's dependencies and versions.
go mod init path/to/root
Replace path/to/root with the import path you want to use for your project. The import path can be any valid Go package path that represents your project's root.
- Listing Dependencies: The
go list -m allcommand allows you to list all the direct dependencies of your Go module along with their versions. This helps you see the current dependency tree of your project.
go list -m all
- Tidying Dependencies: The
go mod tidycommand is used to clean up thego.modfile by removing any dependencies that are no longer imported by your code. It also ensures that the dependencies and their versions are consistent.
go mod tidy
- Downloading and Installing Dependencies: The
go getcommand is used to download and install the dependencies listed in thego.modfile. It fetches the specified versions of the modules and places them in the appropriate location within your Go workspace.
go get ./...
- Downloading Dependencies without Installing: The
go mod downloadcommand only downloads the dependencies without installing them. It fetches the modules and saves them to the Go module cache but does not build or install the packages.
go mod download
Additional Notes:
-
When you use the
go getorgo mod downloadcommands, Go modules will automatically update thego.sumfile, which contains checksums of the downloaded modules. This ensures the security and integrity of the dependencies. -
The
go.modfile serves as the single source of truth for your project's dependencies. It is automatically updated when you run certain commands, likego getorgo mod tidy. However, it's essential to review and commit changes to thego.modfile to version control when working collaboratively on a project. -
If you ever need to manually edit the
go.modfile, make sure to follow the guidelines for Go modules, including specifying correct versions, using semantic versioning, and avoiding direct editing of the requirements section. -
Keep in mind that Go modules are designed to work outside of the traditional GOPATH workspace, which allows for more flexibility in managing dependencies and versioning.
Go Test
Unit Test
/* In *_test.go */
import "testing"
func Test*(t *testing.T) {
/* Add your own tests. */
}
flag -cover shows the coverage.
- Expectation: 50-60% on avergae, favorable >80%
About the attention s.
func NumUnmarshal(){
jsonStr:=`{"id":1,"name":"Jerry"}`
var res map[string]interface{}
_ = json.Unmarshal([]byte(jsonStr), &res) // any numeric values will be converted into floating point
fmt.Printf("%T\n",res["id"]) // print the type of res[id]
// fmt.Printf("#{res["id"]}") do the same thing
i := res["id"].(int64) // try to assert type of i is int64 (false)
/* you can set decoder to make it work as properly */
//decoder := json.NewDecoder(bytes.NewReader([]byte(jsonStr)))
//decoder.UseNumber()
//_ = decoder.Decode(&res)
//i, _ := res["id"].(json.Number).Int64()
/* In most time, we use a struct to hold the bytes */
//type Result struct {
// ID int64 `json:"id"`
// Name string `json:"name"`
//}
fmt.Println(i)
}
Mock Test
todo.
Benchmark
todo.
Go Practice
Typical layers of abstraction for a project design
- 支持对话题发布回帖。
- 回帖id生成需要保证不重复、唯一性。
- 新加回帖追加到本地文件,同时需要更新索引,注意Map的并发安全问题 。