《go語言聖經》練習答案--第四章


練習 4.1: 編寫一個函數,計算兩個SHA256哈希碼中不同bit的數目。

思路:首先進行按位異或,異或之后二進制序列相同的為 0,不同的為 1,這樣就又轉換為求二進制中 1 的個數。

package main

import (
	"crypto/sha256"
	"fmt"
)

var pc [256]byte

func init() {
	for i := range pc {
		pc[i] = pc[i/2] + byte(i&1)
	}
}

func main() {

	c1 := sha256.Sum256([]byte("x"))
	c2 := sha256.Sum256([]byte("X"))
	fmt.Println(popCount(c1, c2))

}

func popCount(s1, s2 [32]byte) int {

	count := 0
	for i := 0; i < 32; i++ {
		temp := s1[i] ^ s2[i]
		count += int(pc[temp])
	}

	return count
}

練習 4.2: 編寫一個程序,默認情況下打印標准輸入的SHA256編碼,並支持通過命令行flag定制,輸出SHA384或SHA512哈希算法。

package main

import (
	"crypto/sha256"
	"crypto/sha512"
	"flag"
	"fmt"
)

var hashMethod = flag.Int("s", 256, "選擇哈希版本:256、384、512。")

func main() {
	flag.Parse()
	printHash()
}

func printHash() {
	var s string
	fmt.Println("輸入要解析的字符串:")
	fmt.Scanf("%s\n", s)

	switch *hashMethod {
	case 256:
		fmt.Printf("%x\n", sha256.Sum256([]byte(s)))
	case 384:
		fmt.Printf("%x\n", sha512.Sum384([]byte(s)))
	case 512:
		fmt.Printf("%x\n", sha512.Sum512([]byte(s)))

	}
}

練習 4.3: 重寫reverse函數,使用數組指針代替slice。

package main

import "fmt"

func main() {
	s := [...]int{0, 1, 2, 3, 4, 5}
	fmt.Println(s)
	reverse(&s)
	fmt.Println(s)
}

func reverse(s *[6]int) {
	fmt.Printf("%T\n", s)
	for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
		s[i], s[j] = s[j], s[i]
	}
}

輸出

[0 1 2 3 4 5]
*[6]int      
[5 4 3 2 1 0]

練習 4.4: 編寫一個rotate函數,通過一次循環完成旋轉。

func main() {
	s := []int{0, 1, 2, 3, 4, 5}
	s = rotate(s, 2)
	fmt.Println(s)
}

func rotate(s []int, n int) []int {
	result := make([]int, len(s))
	index := len(s) - n
	for i := 0; i < len(s); i++ {

		if index >= len(s) {
			index = 0
		}
		result[i] = s[index]
		index++
	}
	return result
}

練習 4.5: 寫一個函數在原地完成消除[]string中相鄰重復的字符串的操作。

使用 map 去重

func nonEqual(strings []string) []string {
	m1 := make(map[string]int)

	for i := 0; i < len(strings); i++ {
		if _, ok := m1[strings[i]]; ok {
			i++
			continue
		} else {
			m1[strings[i]] = i
		}
	}
	i := 0
	for k, _ := range m1 {
		strings[i] = k
		i++
	}
	return strings[:i]
}

雙重循環去重

func nonEqual1(strings []string) []string {
	for i := 0; i < len(strings)-1; i++ {
		for j := i + 1; j < len(strings); j++ {
			if strings[i] == strings[j] {
				copy(strings[j:], strings[j+1:])
				strings = strings[:len(strings)-1]
				j--
			}
		}
	}
	return strings
}

 練習 4.6: 編寫一個函數,原地將一個UTF-8編碼的[]byte類型的slice中相鄰的空格(參考unicode.IsSpace)替換成一個空格返回

 

package main

import (
	"fmt"
	"unicode"
)

func main() {

	s := a([]byte{'1', '2', ' ', ' ', '3', ' ', ' ', ' '})
	fmt.Printf("%c %d", s, len(s))

}

func a(s []byte) []byte {
	for i := 0; i < len(s)-1; i++ {
		if unicode.IsSpace(rune(s[i])) {
			if unicode.IsSpace(rune(s[i+1])) {
				copy(s[i+1:], s[i+2:])
				s = s[:len(s)-1]
				i--
			}
		}
	}
	return s
}

輸出

[1 2   3  ] 5

練習 4.7: 修改reverse函數用於原地反轉UTF-8編碼的[]byte。是否可以不用分配額外的內存?

 不理解,反轉 []byte 和是否 UTF-8 編碼有關系?

練習 4.8: 修改charcount程序,使用unicode.IsLetter等相關的函數,統計字母、數字等Unicode中不同的字符類別。

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"unicode"
	"unicode/utf8"
)

func main() {
	counts := make(map[rune]int)    // counts of Unicode characters 不同字符的計數,類型為 map
	var utflen [utf8.UTFMax + 1]int // count of lengths of UTF-8 encodings 字符編碼長度的計數,類型為 [5]int
	invalid := 0                    // count of invalid UTF-8 characters 無效字符的計數
	var utftype [3]int              //1代表字母 2代表數字 0代表其他類別

	in := bufio.NewReader(os.Stdin)
	for {
		r, n, err := in.ReadRune() // returns rune, nbytes, error
		if err == io.EOF {
			break
		}
		if err != nil {
			fmt.Fprintf(os.Stderr, "charcount: %v\n", err)
			os.Exit(1)
		}
		if r == unicode.ReplacementChar && n == 1 {
			invalid++
			continue
		}
		switch {
		case unicode.IsLetter(r):
			utftype[1]++
		case unicode.IsNumber(r):
			utftype[2]++
		default:
			utftype[0]++
		}
		counts[r]++
		utflen[n]++
	}
	fmt.Printf("rune\tcount\n")
	for c, n := range counts {
		fmt.Printf("%q\t%d\n", c, n)
	}
	fmt.Print("\nlen\tcount\n")
	for i, n := range utflen {
		if i > 0 {
			fmt.Printf("%d\t%d\n", i, n)
		}
	}
	fmt.Print("\ntype\tcount\n")
	for i, n := range utftype {
		var s string
		switch i {
		case 0:
			s = "Other"
		case 1:
			s = "Letter"
		case 2:
			s = "Number"
		}
		fmt.Printf("%s\t%d\n", s, n)
	}
	if invalid > 0 {
		fmt.Printf("\n%d invalid UTF-8 characters\n", invalid)
	}
}

練習 4.9: 編寫一個程序wordfreq程序,報告輸入文本中每個單詞出現的頻率。在第一次調用Scan前先調用input.Split(bufio.ScanWords)函數,這樣可以按單詞而不是按行輸入。

func wordfreq() {
	counts := make(map[string]int)
	in := bufio.NewScanner(os.Stdin)
	in.Split(bufio.ScanWords)
	for in.Scan() {
		counts[in.Text()]++
	}
	for k, v := range counts {
		fmt.Println("%s %d\n", k, v)
	}
}

練習 4.10: 修改issues程序,根據問題的時間進行分類,比如不到一個月的、不到一年的、超過一年。

package main

import (
	"fmt"
	github "hello/Github"
	"log"
	"os"
	"time"
)

func main() {

	//now 為現在的時間,yearAgo 為距現在一年的時間,monthAgo 為距現在一月的時間。
	now := time.Now()
	yearAgo := now.AddDate(-1, 0, 0)
	monthAgo := now.AddDate(0, -1, 0)

	//三個切片,用來存儲 不足一個月的問題,不足一年的問題,超過一年的問題。
	yearAgos := make([]*github.Issue, 0)
	monthAgos := make([]*github.Issue, 0)
	lessMonths := make([]*github.Issue, 0)

	result, err := github.SearchIssues(os.Args[1:])
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%d issues:\n", result.TotalCount)
	for _, item := range result.Items {
		//如果 yearAgo 比 創建時間晚,說明超過一年
		if yearAgo.After(item.CreatedAt) {
			yearAgos = append(yearAgos, item)
			//如果 monthAgo 比 創建時間晚,說明超過一月 不足一年
		} else if monthAgo.After(item.CreatedAt) {
			monthAgos = append(monthAgos, item)
			//如果 monthAgo 比 創建時間早,說明不足一月。
		} else if monthAgo.Before(item.CreatedAt) {
			lessMonths = append(lessMonths, item)
		}
	}

	fmt.Printf("\n一年前\n")
	for _, item := range yearAgos {
		fmt.Printf("#%-5d %9.9s %.55s %v\n",
			item.Number, item.User.Login, item.Title, item.CreatedAt)
	}

	fmt.Printf("\n一月前\n")
	for _, item := range monthAgos {
		fmt.Printf("#%-5d %9.9s %.55s %v\n",
			item.Number, item.User.Login, item.Title, item.CreatedAt)
	}

	fmt.Printf("\n不足一月\n")
	for _, item := range lessMonths {
		fmt.Printf("#%-5d %9.9s %.55s %-40v\n",
			item.Number, item.User.Login, item.Title, item.CreatedAt)
	}
}

 練習 4.14: 創建一個web服務器,查詢一次GitHub,然后生成BUG報告、里程碑和對應的用戶信息。

package main

import (
	github "hello/Github"
	"html/template"
	"log"
	"net/http"
)

func main() {

	http.HandleFunc("/", handle)
	http.ListenAndServe("0.0.0.0:8080", nil)
}

func handle(w http.ResponseWriter, r *http.Request) {
	keywords := []string{"goland", "java"}
	result, err := github.SearchIssues(keywords)
	if err != nil {
		log.Fatal(err)
	}
	var issueList = template.Must(template.New("issuelist").Parse(`
<h1>{{.TotalCount}} issues</h1>
<table>
<tr style='text-align: left'>
  <th>#</th>
  <th>State</th>
  <th>User</th>
  <th>Title</th>
</tr>
{{range .Items}}
<tr>
  <td><a href='{{.HTMLURL}}'>{{.Number}}</a></td>
  <td>{{.State}}</td>
  <td><a href='{{.User.HTMLURL}}'>{{.User.Login}}</a></td>
  <td><a href='{{.HTMLURL}}'>{{.Title}}</a></td>
</tr>
{{end}}
</table>
`))
	issueList.Execute(w, result)
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM