go语言圣经练习答案——第一章入门(前四题)

1,476 阅读2分钟

第一题

题意:修改echo程序,使其能够打印os.Args[0],即被执行命令本身的名字。

package main

import (
	"fmt"
	"os"
)

func main() {
	fmt.Println(os.Args[0])
}

第二题

题意:修改echo程序,使其打印每个参数的索引和值,每个一行。

package main

import (
	"fmt"
	"os"
)

func main() {
	for index,arg := range os.Args[0:] {
		fmt.Printf("%d  : %s\n",index,arg)
	}
}

第三题

题意:练习 1.3: 做实验测量潜在低效的版本和使用了 strings.Join 的版本的运行时间差异。(1.6节讲解了部分 time 包,11.4节展示了如何写标准测试程序,以得到系统性的性能评测。)

package practise_test

import (
	"strings"
	"testing"
)

func StrPlus2(a []string) string {
	return strings.Join(a," ")
}

func StrPlus1(a []string) string{
	var s,sep string
	for i := 0 ; i < len(a) ; i++ {
		s += sep + a[i]
		sep = " "
	}
	return s
}

func BenchmarkStrPlus1(b *testing.B) {
	for i := 0 ; i < b.N ; i++ {
		StrPlus1([]string{"BGM","is","learning","Golang"})
	}
}

func BenchmarkStrPlus2(b *testing.B) {
	for i := 0 ; i <b.N ; i++ {
		StrPlus2([]string{"BGM","is","learning","Golang"})
	}
}

测试结果截图:

结果分析:

strings.Join函数做字符串连接效率比直接"+"要高一倍以上。

strings.Join函数的源码:

// Join concatenates the elements of its first argument to create a single string. The separator
// string sep is placed between elements in the resulting string.
func Join(elems []string, sep string) string {
	switch len(elems) {
	case 0://如果elems长度为0,就返回空字符串
		return ""
	case 1://如果elems长度为1,就返回唯一的元素
		return elems[0]
	}
	n := len(sep) * (len(elems) - 1)//统计分隔符sep一共需要占用的长度
	for i := 0; i < len(elems); i++ {//统计包括elems本身在内的总长度
		n += len(elems[i])
	}

	var b Builder//Builder是一个struct 内部数据用[]byte存储
	b.Grow(n)//保证b的[]byte容量大于n
	b.WriteString(elems[0])//写第一个元素
	for _, s := range elems[1:] {//循环写入sep+elems[i]
		b.WriteString(sep)
		b.WriteString(s)
	}
	return b.String()
}

效率提高主要是省掉了字符串"+"操作时每次都重新再分配空间和拷贝复制数据的过程。

第四题

题意:修改dup2,出现重复的行时打印文件名称。

package main

import (
	"bufio"
	"fmt"
	"os"
)

type LNFile struct {
	Count int
	FileNames []string
}

func main() {
	counts := make(map[string]*LNFile)
	files := os.Args[1:]
	if len(files) == 0 {
		countLines(os.Stdin,counts)
	} else {
		for _,arg := range files {
			f,err := os.Open(arg)
			if err != nil {

```				fmt.Fprintf(os.Stderr,"dup2:%v\n",err)
			}
			countLines(f,counts)
			f.Close()
		}
	}
	for line,n := range counts {
		if n.Count > 1 {
			fmt.Printf("%d %v\n%s\n",n.Count,n.FileNames,line)
		}
	}
}

func countLines(f *os.File, counts map[string]*LNFile) {
	input := bufio.NewScanner(f)
	for input.Scan() {
		key := input.Text()
		_,ok := counts[key]
		if ok {
			counts[key].Count++
			counts[key].FileNames = append(counts[key].FileNames,f.Name())
		} else {
			counts[key] = new(LNFile)
			counts[key].Count = 1
			counts[key].FileNames = append(counts[key].FileNames,f.Name())
		}
	}
}