go练习题

go练习题

文章目录

一、 复合类型习题

1. 重写reverse函数,使用数组指针代替slice。

func main(){
	ss := [6]int{0,1,2,3,4,5}
	reverse2(&ss)
}
func reverse2(ss *[6]int) {
	for i, j := 0, len(ss)-1; i < j; i, j = i+1, j-1 {
		ss[i], ss[j] = ss[j], ss[i]
	}
}

也可以使用以下方法实现数组内元素反转

func main(){
	ss := [6]int{0,1,2,3,4,5}
	var s = make([]int,0)
	copy(s,ss[:])
}

2. 编写一个rotate函数,通过一次循环完成旋转。


func rotate(s []int, k int, direction string) []int {
	res := make([]int, len(s))
	if direction == "left" {
		for i := 0; i < len(s); i++ {
			index := i + k
			if index >= len(s) {
				index = index - len(s)
			}
			res[i] = s[index]
		}
	}
	return res
}

1. 编写一个非递归版本的comma函数,使用bytes.Buffer代替字符串链接操作

设计思路

使用bytes.Buffer实现切相同数量的子串为一组。

代码实现
  • 方法一
func comma3(s string, seq int) string {
	var (
		buf bytes.Buffer
		tem []byte
		str = []byte(s)
	)

	// 区分小数点
	comm := bytes.IndexByte(str, '.')
	if comm != -1 {
		tem = str[comm:]
		str = str[:comm]
	}

	if len(str) <= seq {
		return s
	}

	if strings.HasPrefix(s, "-") || strings.HasPrefix(s, "+") {
		buf.WriteByte(str[0])
		str = str[1:]
	}

	for n := len(str); n > 0; n -= seq {
		m := n % seq
		if m > 0 {
			buf.Write(str[:m])
			str = str[m:n]
			n -= m
		}
		buf.WriteByte(',')
		fmt.Fprintf(&buf, "%s", str[:seq])
		str = str[seq:]
	}
	buf.Write(tem)
	return buf.String()

}
  • 方法二
func comma(s string) string {
	var buf1, buf2 bytes.Buffer
	lenS := len(s)

	if lenS <= 3 {
		return s
	}
	count := 0
	for i := lenS - 1; i >= 0; i-- {
		count++
		buf1.WriteByte(s[i])
		if count % 3 == 0 {
			buf1.WriteString(",")
		}
	}

	n := buf1.String()
	for j := len(n) - 1; j >= 0; j-- {
		buf2.WriteByte(n[j])
	}
	return buf2.String()

}

2. 编写一个函数,判断两个字符串是否是相互打乱的,也就是说它们有着相同的字符,但是对应不同的顺序。

设计思路

首先定义一个flag,使用内置函数bytes.IndexByte先取出该字节所另一字串的下标,再判断字串1的下标和字串2的下标是否相等。如果不相等则flag+1,直到flag和s1或s2的长度相等为止。

代码实现
func isDisorder(str1, str2 string) bool {
	if len(str1) != len(str2) {
		return false
	}

	var (
		s1   = []byte(str1)
		s2   = []byte(str2)
		flag int
	)

	for i := 0; i < len(s1); i++ {
		index := bytes.IndexByte(s2, s1[i])
		if index != i && bytes.Contains(s2, []byte{s1[i]}) {
			flag++
		}
	}

	if flag == len(str1) {
		return true
	}
	return false
}

3. 写一个函数在原地完成消除[]string中相邻重复的字符串的操作

代码思路

定义一个容量和原切片相等的变量newSlice,遍历原切片,如果当前元素和后一个元素的值不相等,那么就把当前元素append到newSlice中。完成后,将原切片的最后一个元素再加入到newSlice中。

代码实现
func distincStr(s []string) []string {
	var (
		i     int
		index int
	)

	newSlice := s[:0]
	for _, v := range s {
		index = i + 1
		if index >= len(s) {
			break
		}

		if v != s[index] {
			newSlice = append(newSlice, v)

		}
		i++
	}

	newSlice = append(newSlice, s[len(s)-1])

	return newSlice
}


4. 修改charcount程序,使用unicode.IsLetter等相关的函数,统计字母、数字等Unicode中不同的字符类别

代码实现
func charCount2(data *bytes.Reader) {
	letters := make(map[rune]int)
	nums := make(map[rune]int)

	in := bufio.NewReader(data)
	for {
		r, _, err := in.ReadRune()
		if err == io.EOF {
			break
		}

		if err != nil {
			fmt.Fprintf(os.Stderr, "charcount:%v\n", err)
			os.Exit(1)
		}

		if unicode.IsLetter(r) {
			letters[r]++
		}

		if unicode.IsNumber(r) {
			nums[r]++
		}
	}

	fmt.Print("\nletters\tcount\n")
	for letter, num := range letters {
		fmt.Printf("%q\t%d", letter, num)
	}

	fmt.Print("\nnumbers\tcount\n")
	for number, num := range nums {
		fmt.Printf("%q\t%d\n", number, num)
	}

}
func main(){
	b, err := ioutil.ReadFile("./map.txt")
	if err != nil {
		fmt.Println(err.Error())
	}
	rd := bytes.NewReader(b)
	charCount2(rd)
	}

5. 编写一个程序wordfreq程序,报告输入文本中每个单词出现的频率。在第一次调用Scan前先调用input.Split(bufio.ScanWords)函数,这样可以按单词而不是按行输入。

代码实现
func main(){
	f, err := os.ReadFile("./map.txt")
	if err != nil {
		fmt.Println(err.Error())
	}
	rd := bytes.NewReader(f)
	wordC := wordfreq(rd)
	for text, count := range wordC {
		fmt.Printf("%q\t%d\n", text, count)
	}
}


func wordfreq(data *bytes.Reader) map[string]int {
	wordCount := make(map[string]int)
	input := bufio.NewScanner(data)
	input.Split(bufio.ScanWords)
	for input.Scan() {
		//fmt.Println(input.Text())
		wordCount[input.Text()]++
	}
	return wordCount
}


第4题和第5题通用文件map.txt可以写任意内容,包括:字母,数字,符号,中文。

6.练习 7.8: 很多图形界面提供了一个有状态的多重排序表格插件:主要的排序键是最近一次点击过列头的列,第二个排序键是第二最近点击过列头的列,等等。定义一个sort.Interface的实现用在这样的表格中。比较这个实现方式和重复使用sort.Stable来排序的方式。

package main

import (
	"errors"
	"fmt"
	"math/rand"
	"os"
	"sort"
	"strings"
	"text/tabwriter"
	"time"
)

// 练习 7.8: 很多图形界面提供了一个有状态的多重排序表格插件:
//主要的排序键是最近一次点击过列头的列,第二个排序键是第二最近点击过列头的列,等等。
//定义一个sort.Interface的实现用在这样的表格中。比较这个实现方式和重复使用sort.Stable来排序的方式。

// LogTable 日志表格
type LogTable struct {
	Sequence int
	Mode     string
	Level    string
	Comment  string
	Time     time.Time
}

// PrintTable 打印表格
func PrintTable(logtable []*LogTable) {
	const format = "%v\t%v\t%v\t%v\t%v\t\n"
	lt := new(tabwriter.Writer)
	lt = lt.Init(os.Stdout, 0, 8, 2, ' ', 0)
	fmt.Fprintf(lt, format, "序号", "日志模式", "日志等级", "日志内容", "记录时间")
	fmt.Fprintf(lt, format, "-----", "------", "-----", "----", "------")
	for _, v := range logtable {
		fmt.Fprintf(lt, format, v.Sequence, v.Mode, v.Level, v.Comment, v.Time)
	}

	lt.Flush()
}

// UniversalSort 按任意键排序
type UniversalSort struct {
	log   []*LogTable
	means map[string]int
	less  func(i, j *LogTable) bool
}

func (u UniversalSort) Len() int           { return len(u.log) }
func (u UniversalSort) Less(i, j int) bool { return u.less(u.log[i], u.log[j]) }
func (u UniversalSort) Swap(i, j int)      { u.log[i], u.log[j] = u.log[j], u.log[i] }

var tables = []*LogTable{
	{4, "参数模块日志", "ERROR", "[ERROR]:ssh from to 11.11.1.1", randate()},
	{1, "系统模块日志", "INFO", "[INFO]:ssh from to 11.11.1.1", randate()},
	{5, "参数模块日志", "WARN", "[WARN]:mysql_host", randate()},
	{6, "系统模块日志", "INFO", "[INFO]:ssh from to 11.11.1.1", randate()},
	{8, "参数模块日志", "DEBUG", "[DEBUG]:ssh from to 11.11.1.1", randate()},
	{7, "系统模块日志", "INFO", "[INFO]:ssh from to 11.11.1.1", randate()},
	{2, "参数模块日志", "WARN", "[WARN]: no Invalid param - mysql-port", randate()},
	{3, "系统模块日志", "INFO", "[INFO]:ssh from to 11.11.1.1", randate()},
	{9, "参数模块日志", "INFO", "[INFO]:ssh from to 11.11.1.1", randate()},
}

func bySeqence() *UniversalSort {
	return &UniversalSort{
		log: tables,
		less: func(i, j *LogTable) bool {
			return i.Sequence < j.Sequence
		},
	}
}

func byMode() *UniversalSort {
	return &UniversalSort{
		log: tables, less: func(i, j *LogTable) bool {
			return i.Mode < j.Mode
		},
	}
}

func byLevel() *UniversalSort {
	return &UniversalSort{
		log: tables, less: func(i, j *LogTable) bool {
			return i.Level < j.Level
		},
	}
}

func byComment() *UniversalSort {
	return &UniversalSort{
		log: tables, less: func(i, j *LogTable) bool {
			return i.Comment < j.Comment
		},
	}
}

func byTime() *UniversalSort {
	return &UniversalSort{
		log: tables, less: func(i, j *LogTable) bool {
			return i.Time.Before(j.Time)
		},
	}
}

// choiceSort 实现了是基于哪种规则做的正序或倒序
func (u *UniversalSort) bySort(means map[string]int) (*UniversalSort, error) {
	var nu *UniversalSort
	for m, c := range means {
		m = strings.ToLower(m)
		if c < 1 {
			return nu, errors.New("no invaild count")
		}
		switch m {
		case "mode":
			nu = byMode()
		case "level":
			nu = byLevel()
		case "comment":
			nu = byComment()
		case "time":
			nu = byTime()
		default:
			nu = bySeqence()
		}
		nu.means = u.means
		if c%2 != 0 {
			sort.Sort(nu)
		}
		sort.Sort(sort.Reverse(nu))
	}

	return nu, nil

}

func randate() time.Time {
	min := time.Date(1970, 1, 0, 0, 0, 0, 0, time.UTC).Unix()
	max := time.Date(2070, 1, 0, 0, 0, 0, 0, time.UTC).Unix()
	delta := max - min

	sec := rand.Int63n(delta) + min
	return time.Unix(sec, 0)
}
func main() {

	PrintTable(tables)
	fmt.Println("=======1.seq sort========")
	u := new(UniversalSort)
	var m = make(map[string]int, 0)
	m["time"] = 1
	if _, err := u.bySort(m); err != nil {
		fmt.Println(err.Error())
	}
	PrintTable(tables)

}

7. 增加额外的handler让客户端可以创建,读取,更新和删除数据库记录。例如,一个形如 /update?item=socks&price=6 的请求会更新库存清单里一个货品的价格并且当这个货品不存在或价格无效时返回一个错误值。(注意:这个修改会引入变量同时更新的问题)


package main

import (
	"fmt"
	"html/template"
	"net/http"
	"strconv"
	"sync"
)

type dollars float32

var mu sync.Mutex

func (d dollars) String() string { return fmt.Sprintf("$%.2f", d) }

type database map[string]dollars

func (db database) listHttp() func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		// 解析模板
		t, err := template.ParseFiles("./table_temp.html")
		if err != nil {
			fmt.Errorf("parse template error:%v\n", err.Error())
		}
		if err := t.Execute(w, db); err != nil {
			fmt.Fprintf(w, err.Error())
		}

		var rows = `<p>{{ .A }}  |  {{ .B }}</p>`

		rowsTemp := template.Must(template.New("rows").Parse(rows))

		type row struct {
			A string
			B dollars
		}

		for custom, price := range db {
			d := row{A: custom, B: price}
			if err := rowsTemp.Execute(w, d); err != nil {
				fmt.Errorf("prase rows template error :%v\n", err.Error())
			}
		}
	}
}

func (db database) priceHttp() func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		item := r.URL.Query().Get("item")
		price, ok := db[item]
		if !ok {
			w.WriteHeader(http.StatusNotFound)
			fmt.Fprintf(w, "no such item:%q\n", item)
			return
		}
		fmt.Fprintf(w, "%s\n", price)
	}
}

//createHttp 增加一件商品
func (db database) createHttp() func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		item := r.URL.Query().Get("item")
		price := r.URL.Query().Get("price")
		p, err := strconv.ParseFloat(price, 64)
		if err != nil {
			fmt.Fprintf(w, "%v\n", err.Error())
			return
		}
		db[item] = dollars(p)
		if _, ok := db[item]; ok {
			fmt.Fprintf(w, "%s:$%.2f 创建成功\n", item, db[item])
		}
	}
}

//updateHttp 更新一件商品
func (db database) updateHttp() func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		item := r.URL.Query().Get("item")
		p, err := strconv.ParseFloat(r.URL.Query().Get("price"), 64)
		if err != nil {
			fmt.Fprintf(w, "%v\n", err.Error())
		}
		if v, ok := db[item]; ok {
			if dollars(p) == v {
				fmt.Fprintf(w, "%s:$%.2f\nf无需更改", item, dollars(p))
				return
			}
			mu.Lock()
			db[item] = dollars(p)
			mu.Unlock()
		} else {
			fmt.Fprintf(w, "%s不存在\n", item)
			return

		}
		fmt.Fprintf(w, "%s:$%.2f 修改成功\n", item, db[item])
	}
}

// deleteHttp 删除一件商品
func (db database) deleteHttp() func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		item := r.URL.Query().Get("item")
		if _, ok := db[item]; !ok {
			fmt.Fprintf(w, "%s 不存在\n", item)
			return
		}
		mu.Lock()
		delete(db, item)
		mu.Unlock()
		fmt.Fprintf(w, "%s 商品已删除\n", item)
	}
}

func main() {
	db := database{"coat": 20.34, "clothes": 350.89}

	http.HandleFunc("/list", db.listHttp())
	http.HandleFunc("/price", db.priceHttp())
	http.HandleFunc("/create", db.createHttp())
	http.HandleFunc("/update", db.updateHttp())
	http.HandleFunc("/delete", db.deleteHttp())
	if err := http.ListenAndServe(":8090", nil); err != nil {
		fmt.Println(err.Error())
	}

}

8. 练习 8.1: 修改clock2来支持传入参数作为端口号,然后写一个clockwall的程序,这个程序可以同时与多个clock服务器通信,从多个服务器中读取时间,并且在一个表格中一次显示所有服务器传回的结果,类似于你在某些办公室里看到的时钟墙。如果你有地理学上分布式的服务器可以用的话,让这些服务器跑在不同的机器上面;或者在同一台机器上跑多个不同的实例,这些实例监听不同的端口,假装自己在不同的时区。像下面这样:

$ TZ=US/Eastern    ./clock2 -port 8010 &
$ TZ=Asia/Tokyo    ./clock2 -port 8020 &
$ TZ=Europe/London ./clock2 -port 8030 &
$ clockwall NewYork=localhost:8010 Tokyo=localhost:8020 London=localhost:8030
设计思路

首先要搞清楚服务端和客户端,由题目可知,服务端是多个clock,客户端是clockwall,clockwall会监听所有clock的服务端口,将每个clock在网络上输出的内容获取到本地并输出。

代码实现
代码目录结构

在这里插入图片描述

服务端代码clock/clock.go
package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"time"
)

// ./clock -host localhost -port 8000 &
func main() {
	ip := flag.String("host", "localhost", "listen host's ip")
	p := flag.Int("port", 8000, "listen host's port")
	flag.Parse()

	go Listen(*ip, *p)

	for {
		time.Sleep(1 * time.Second)
	}

}

func Listen(ip string, port int) {
	listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", ip, port))
	if err != nil {
		log.Fatal(err)
	}

	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Fatal(err)
		}

		go HandleConn(ip, port, conn)

	}
}

func HandleConn(ip string, port int, n net.Conn) {
	defer n.Close()
	var (
		zone string
		t    = time.Now()
	)
	switch port {
	case 8000:
		zone = "America/New_York"
	case 8001:
		zone = "Asia/Shanghai"
	case 8002:
		zone = "Asia/Harbin"
	default:
		log.Fatal("Not time zone")

	}
	local, _ := time.LoadLocation(zone)
	writeInfo := fmt.Sprintf("%v\t%v\t%v\t%v\t\n", zone, ip, port, t.In(local).Format("2006-01-02 15:04:05"))
	if _, err := io.WriteString(n, writeInfo); err != nil {
		log.Fatal(err)
	}package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"strconv"
	"strings"
	"time"
)

func main() {
	var (
		zoneArr    []string
		addressArr []string
		i          int = 0
	)

	for _, param := range os.Args[1:] {
		if strings.HasPrefix(param, "-") {
			zoneArr = append(zoneArr, strings.Split(param, "-")[1])
		} else {
			addressArr = append(addressArr, param)
		}
	}

	for _, zone := range zoneArr {
		flag.StringVar(&zone, zone, fmt.Sprintf("localhost:800%d", i), "please input ip:port")
		i++
	}

	flag.Usage = func() {
		fmt.Println("Usage: practise [-h] [NewYork=localhost:8000] [Tokyo=localhost:8001] [London=localhost:8002]")
		flag.PrintDefaults()
	}

	flag.Parse()
	fmt.Fprintf(os.Stdout, fmt.Sprintf("%v\t%10v\t%10v\t%10v\t\n", "ZONE", "IP", "PORT", "time"))
	fmt.Fprintf(os.Stdout, fmt.Sprintf("%v\t%10v\t%10v\t%10v\t\n", "-----", "------", "-----", "-----"))

	for {
		for _, param := range addressArr {
			ip := strings.Split(param, ":")[0]
			port := strings.Split(param, ":")[1]
			p, _ := strconv.Atoi(port)
			go dial(ip, p)
		}
		time.Sleep(3 * time.Second)
	}

}

func dial(host string, port int) {
	conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", host, port))
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	copy(os.Stdout, conn)
}

func copy(dst io.Writer, src io.Reader) {
	if _, err := io.Copy(dst, src); err != nil {
		log.Fatal(err)
	}
}


}
客户端代码 clock/clockwall.go
package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"strconv"
	"strings"
	"time"
)

func main() {
	var (
		zoneArr    []string
		addressArr []string
		i          int = 0
	)

	for _, param := range os.Args[1:] {
		if strings.HasPrefix(param, "-") {
			zoneArr = append(zoneArr, strings.Split(param, "-")[1])
		} else {
			addressArr = append(addressArr, param)
		}
	}

	for _, zone := range zoneArr {
		flag.StringVar(&zone, zone, fmt.Sprintf("localhost:800%d", i), "please input ip:port")
		i++
	}

	flag.Usage = func() {
		fmt.Println("Usage: practise [-h] [NewYork=localhost:8000] [Tokyo=localhost:8001] [London=localhost:8002]")
		flag.PrintDefaults()
	}

	flag.Parse()
	fmt.Fprintf(os.Stdout, fmt.Sprintf("%v\t%10v\t%10v\t%10v\t\n", "ZONE", "IP", "PORT", "time"))
	fmt.Fprintf(os.Stdout, fmt.Sprintf("%v\t%10v\t%10v\t%10v\t\n", "-----", "------", "-----", "-----"))

	for {
		for _, param := range addressArr {
			ip := strings.Split(param, ":")[0]
			port := strings.Split(param, ":")[1]
			p, _ := strconv.Atoi(port)
			go dial(ip, p)
		}
		time.Sleep(3 * time.Second)
	}

}

func dial(host string, port int) {
	conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", host, port))
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	copy(os.Stdout, conn)
}

func copy(dst io.Writer, src io.Reader) {
	if _, err := io.Copy(dst, src); err != nil {
		log.Fatal(err)
	}
}

使用方式
  • 服务端
$ cd clock
$ go build
$ ./clock -host localhost -port 8001 &
  • 客户端
$ cd clockwall
$ go build
$ ./clockwall -NewYork localhost:8000 -ShangHai localhost:8001 

9. 写一个du工具,每隔一段时间将root目录下的目录大小计算并显示出来。

package main

import (
    "flag"
    "sync"
    "time"
)

// 写一个du工具,每隔一段时间将root目录下的目录大小计算并显示出来。

var realTime = flag.Bool("r", false, "real-time output message")

func dirents(dir string) []os.FileInfo {
    entries,err := ioutil.ReadDir(dir)
    if err != nil {
        fmt.Fprintf(os.Stderr, "du1:%v\n",err )
        return nil
    }
    return entries

}

func walkDir(dir string,size chan<-int64,wg *sync.WaitGroup){
    defer wg.Done()
    for _,d := range dirents(dir){
        if d.IsDir() {
            wg.Add(1)
            subdir := filepath.Join(dir,d.Name())
            go walkDir(subdir,size,wg)
        } else {
            size <- d.Size()
        }
    }

}

func printDiskUsage(nfiles, nbytes int64) {
    fmt.Printf
func printDiskUsage(nfiles, nbytes int64) {​
    fmt.Printf("%d files  %.1f GB\n", nfiles, float64(nbytes)/1e9)}("%d files  %.1f GB\n", nfiles, float64(nbytes)/1e9)
}

func main() {
    flag.Parse()
    root := flag.Args()
    if len(root) == 0 {
        root = []string{"/root"}
    }

    var (
        ticker         *time.Ticker
        wg sync.WaitGroup
        fileSize       = make(chan int64)
        nfiles, nbytes int64
    )

    if *realTime {
        ticker = time.NewTicker(10 * time.Second)
    }
    
    for _,r:= range root {
        wg.Add(1)
        go walkDir(r, fileSize,&wg)
    }

    go func ()  {
        wg.Wait()
        close(fileSize)
    }()

    for {
        Loop:
        select {
        case <- ticker.C:
            printDiskUsage(nfiles,nbytes)
            break Loop
        case e,ok := <-fileSize:
            if !ok{
                break Loop
            }
            nfiles++
            nbytes += e
        }
    }

}

go build
./clock -r /root
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值