《Go语言圣经/gopl》第四章习题上

前言

本章为《Go语言圣经》第四章前半部分即4.1-4.9,主要为算法题或语法题。后半部分4.10-4.14为编程应用题,文字阐述量较大,故笔者将其分为上下两章进行分开叙述。
项目源码地址

4.1-4.2

前置知识

题目中的各种编码怎么产生?

SHA256

var a string
fmt.Scan(&a)
c1 := sha256.Sum256([]byte(a))

SHA384

var a string
fmt.Scan(&a)
c1 := sha512.Sum384([]byte(x))

SHA512

var a string
fmt.Scan(&a)
c1 := sha512.Sum512([]byte(x))

4.1

题目

编写一个函数,计算两个SHA256哈希码中不同bit的数目。(参考2.6.2节的PopCount函数。)

解析

枚举SHA256编码即可,注意SHA256编码一共有32位,每位有8byte

代码
package main

import (
	"crypto/sha256"
	"fmt"
)

func main() {
	var a, b string
	fmt.Scan(&a, &b)

	c1 := sha256.Sum256([]byte(a))
	c2 := sha256.Sum256([]byte(b))

	fmt.Printf("out1: %x\n", c1)
	fmt.Printf("out2: %x\n", c2)

	cnt := 0
	for i := 0; i < 32; i++ {
		a, b := c1[i], c2[i]

		for j := 0; j < 8; j++ {
			if (a >> j & 1) != (b >> j & 1) {
				cnt++
			}
		}
	}

	fmt.Println("diff:", cnt)
}

4.2

题目

编写一个程序,默认情况下打印标准输入的SHA256编码,并支持通过命令行flag定制,输出SHA384或SHA512哈希算法。

解析

语法题,根据输入flag来进行函数的使用即可

代码
package main

import (
	"crypto/sha256"
	"crypto/sha512"
	"fmt"
	"os"
	"strconv"
)

func main() {
	var x string
	flag := 0

	for p, val := range os.Args[1:] {
		if p == 1 {
			x = val
		} else {
			flag, _ = strconv.Atoi(val)
		}
	}

	if flag == 0 {
		res := sha256.Sum256([]byte(x))
		fmt.Printf("%x", res)
	} else if flag == 1 {
		res := sha512.Sum384([]byte(x))
		fmt.Printf("%x", res)
	} else {
		res := sha512.Sum512([]byte(x))
		fmt.Printf("%x", res)
	}
}

4.3-4.7

前置知识

题目代码
// reverse reverses a slice of ints in place.
func reverse(s []int) {
    for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
        s[i], s[j] = s[j], s[i]
    }
}

4.3

题目

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

解析

语法题,将reverse函数中的slice替换即可,注意数组要取地址

代码
package main

import "fmt"

func reverse(s *[5]int) {
	l, r := 0, 4

	for l < r {
		s[l], s[r] = s[r], s[l]
		l++
		r--
	}
}

func main() {
	a := [5]int{1, 2, 3, 4, 5}

	reverse(&a)

	fmt.Println(a)
}

4.4

题目

编写一个rotate函数,通过一次循环完成左移K次

解析

算法题,一个数组向左移k次即前k%len个数移动至最后,后len-k%len个数移动至开头

代码
package main

import "fmt"

func reverse(arr []int, k int) []int {
	len := len(arr)
	res := make([]int, len)
	p := 0
	k %= len

	for i := k; i < len; i++ {
		res[p] = arr[i]
		p++
	}

	for i := 0; i < k; i++ {
		res[p] = arr[i]
		p++
	}

	return res
}

func main() {
	var k int
	arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

	fmt.Scan(&k)

	arr = reverse(arr, k)

	fmt.Println(arr)
}

4.5

题目

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

解析

算法题,双指针算法轻松搞定,请参考LeetCode 80.删除有序数组中的重复项

代码
package main

import "fmt"

func main() {
	var a []string
	var n int

	fmt.Scan(&n)

	for n > 0 {
		var s string
		fmt.Scan(&s)
		a = append(a, s)
		n--
	}

	for i := 0; i < len(a); i++ {
		j := i + 1
		for j < len(a) && a[j] == a[i] {
			j++
		}
		a = append(a[0:i+1], a[j:]...)
		i = j - 1
	}

	fmt.Println(a)
}

4.6

题目

编写一个函数,原地将一个UTF-8编码的[]byte类型的slice中相邻的空格(参考unicode.IsSpace)替换成一个空格返回

解析

与上题类似,注意空格符号判断使用unicode.IsSpace()函数

代码
package main

import (
	"fmt"
	"unicode"
)

func uniqueSpace(s []byte) []byte {
	for i := 0; i < len(s); i++ {
		if unicode.IsSpace(rune(s[i])) {
			j := i
			for j < len(s) && unicode.IsSpace(rune(s[j])) {
				j++
			}
			s = append(s[:i], s[j-1:]...)
			i = j - 1
		}
	}
	return s
}

func main() {
	s := "afdigneigu  \n\t   fjidfiefh  jsdifj frewf      "
	str := []byte(s)

	str = uniqueSpace(str)

	fmt.Println(string(str))
}

4.7

题目

修改reverse函数用于原地反转UTF-8编码的[]byte。是否可以不用分配额外的内存

解析

算法题,与4.1类似,编写一个reverse函数即可

代码
package main

import "fmt"

func reverse(s []byte) {
	l, r := 0, len(s)-1

	for l < r {
		s[l], s[r] = s[r], s[l]
		l++
		r--
	}
}

func main() {
	a := "123456789"
	s := []byte(a)

	reverse(s)

	fmt.Println(string(s))
}

4.8-4.9

前置知识

题目代码

charcount程序

// Charcount computes counts of Unicode characters.
package main

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

func main() {
    counts := make(map[rune]int)    // counts of Unicode characters
    var utflen [utf8.UTFMax + 1]int // count of lengths of UTF-8 encodings
    invalid := 0                    // count of invalid UTF-8 characters

    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
        }
        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)
        }
    }
    if invalid > 0 {
        fmt.Printf("\n%d invalid UTF-8 characters\n", invalid)
    }
}

4.8

题目

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

解析

笔者仅完成了统计字母,数字
统计字母函数unicode.IsLetter()
统计数字函数unicode.IsDigit()

代码
// Charcount computes counts of Unicode characters.
// cat .\test.txt | go run .\main.go
package main

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

func main() {
	counts := make(map[rune]int)    // counts of Unicode characters
	var utflen [utf8.UTFMax + 1]int // count of lengths of UTF-8 encodings
	invalid := 0                    // count of invalid UTF-8 characters

	numberCnt, letterCnt := 0, 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
		}
		if unicode.IsDigit(r) {
			numberCnt++
		} else if unicode.IsLetter(r) {
			letterCnt++
		}
		counts[r]++
		utflen[n]++
	}

	fmt.Println("Number count:", numberCnt)
	fmt.Println("letter count:", letterCnt)

	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)
		}
	}
	if invalid > 0 {
		fmt.Printf("\n%d invalid UTF-8 characters\n", invalid)
	}
}

4.9

题目

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

解析

此处注意使用的读入函数为bufio.NewScanner(os.Stdin)并非上一题的 bufio.NewReader(os.Stdin)
进行分割之后使用,以下代码进行遍历

	for in.Scan() {
		word := in.Text()
	}
代码
// cat .\test.txt | go run .\main.go
package main

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

func main() {
	hash := make(map[string]int)

	in := bufio.NewScanner(os.Stdin)
	in.Split(bufio.ScanWords)
	for in.Scan() {
		word := in.Text()
		hash[word]++
	}

	for word, cnt := range hash {
		fmt.Println(word, cnt)
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值