最近在学习go语言,在看tour of go的时候发现上面的练习被贴出来不少,但是最近看gopl的时候发现书上的练习贴出来的很少,于是想贴出来自己的,都是自己做的,有问题请指教;如果有补充的可以联系,我会把相应代码段贴上来并且注上信息。
代码段大都是从github上下载下来然后修改的,版权都是书作者的,在代码段中就不注释了。我用的ide是Goland(推荐)。代码段中的os.Args()功能我用得很少,所需要的变量大都换成现成的了。
exercise1.7/page17
// Fetch prints the content found at each specified URL.
package main
import (
"fmt"
"net/http"
"os"
"io"
)
func main() {
url:="http://gopl.io"//网址可以随意换
resp, err := http.Get(url)
if err != nil {
fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
os.Exit(1)
}
_,err2:=io.Copy(os.Stdout,resp.Body)
resp.Body.Close()
if err2 != nil {
fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err2)
os.Exit(1)
}
}
exercise1.12/page22
// Lissajous generates GIF animations of random Lissajous figures.
//简易版,只能修改cycles变量,并必须直接在/后输入数字,否则cycle默认是5
package main
import (
"image"
"image/color"
"image/gif"
"io"
"math"
"math/rand"
)
//!-main
// Packages not needed by version in book.
import (
"log"
"net/http"
"time"
"strconv"
)
//!+main
var palette = []color.Color{color.White, color.Black}
const (
whiteIndex = 0 // first color in palette
blackIndex = 1 // next color in palette
)
func main() {
//!-main
// The sequence of images is deterministic unless we seed
// the pseudo-random number generator using the current time.
// Thanks to Randall McPherson for pointing out the omission.
rand.Seed(time.Now().UTC().UnixNano())
//!+http
handler := func(w http.ResponseWriter, r *http.Request) {
cy,_:=strconv.Atoi(r.URL.Path[1:])
lissajous(w,cy)
}
http.HandleFunc("/", handler)
//!-http
log.Fatal(http.ListenAndServe("localhost:8000", nil))
return
}
func lissajous(out io.Writer, cy int) {
const (
//cycles = 5 // number of complete x oscillator revolutions
res = 0.001 // angular resolution
size = 100 // image canvas covers [-size..+size]
nframes = 64 // number of animation frames
delay = 8 // delay between frames in 10ms units
)
cycles:=5 //设置cycles变量
if cy!=0 {
cycles =cy
}
freq := rand.Float64() * 3.0 // relative frequency of y oscillator
anim := gif.GIF{LoopCount: nframes}
phase := 0.0 // phase difference
for i := 0; i < nframes; i++ {
rect := image.Rect(0, 0, 2*size+1, 2*size+1)
img := image.NewPaletted(rect, palette)
for t := 0.0; t < float64(cycles)*2*math.Pi; t += res {
x := math.Sin(t)
y := math.Sin(t*freq + phase)
img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5),
blackIndex)
}
phase += 0.1
anim.Delay = append(anim.Delay, delay)
anim.Image = append(anim.Image, img)
}
gif.EncodeAll(out, &anim) // NOTE: ignoring encoding errors
}
exercise3.4/page60
// Surface computes an SVG rendering of a 3-D surface function.
//只修改了main函数
package main
import (
"fmt"
"math"
"io"
"net/http"
"log"
)
const (
width, height = 600, 320 // canvas size in pixels
cells = 100 // number of grid cells
xyrange = 30.0 // axis ranges (-xyrange..+xyrange)
xyscale = width / 2 / xyrange // pixels per x or y unit
zscale = height * 0.5 // pixels per z unit
angle = math.Pi / 6 // angle of x, y axes (=30°)
)
var sin30, cos30 = math.Sin(angle), math.Cos(angle) // sin(30°), cos(30°)
func main() {
handler := func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type","image/svg+xml")
surface(w)
}
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe("localhost:8000", nil))
return
}
func surface(w io.Writer) {
fmt.Fprintf(w,"<svg xmlns='http://www.w3.org/2000/svg' "+
"style='stroke: grey; fill: white; stroke-width: 0.7' "+
"width='%d' height='%d'>\n", width, height)
for i := 0; i < cells; i++ {
for j := 0; j < cells; j++ {
ax, ay := corner(i+1, j)
bx, by := corner(i, j)
cx, cy := corner(i, j+1)
dx, dy := corner(i+1, j+1)
fmt.Fprintf(w,"<polygon points='%g,%g %g,%g %g,%g %g,%g'/>\n",
ax, ay, bx, by, cx, cy, dx, dy)
}
}
fmt.Fprintf(w,"</svg>\n")
}
func corner(i, j int) (float64, float64) {
// Find point (x,y) at corner of cell (i,j).
x := xyrange * (float64(i)/cells - 0.5)
y := xyrange * (float64(j)/cells - 0.5)
// Compute surface height z.
z := f(x, y)
// Project (x,y,z) isometrically onto 2-D SVG canvas (sx,sy).
sx := width/2 + (x-y)*cos30*xyscale
sy := height/2 + (x+y)*sin30*xyscale - z*zscale
return sx, sy
}
func f(x, y float64) float64 {
r := math.Hypot(x, y) // distance from (0,0)
return math.Sin(r)/r
}
exercise4.1/page74
//did it by myself ,page 74
//comma2 can deal with floating-point number and an optional sign
package main
import (
"fmt"
"bytes"
"strings"
)
func comma(s string)string {
var buf bytes.Buffer
n,l:=0,len(s)
if s[0]=='-'||s[0]=='+'{
buf.WriteByte(s[0])
n=1
}
if strings.Contains(s,"."){
l=strings.Index(s,".")
}
for i:=n;i<l;i++{
if (l-i)%3==0&&i>n{
buf.WriteString(",")
}
fmt.Fprintf(&buf,"%c",s[i])
}
fmt.Fprintf(&buf,"%s",s[l:])
return buf.String()
}
func main() {
s:="-311376114.7342424"
fmt.Println(comma(s))
}
exercise4.1/page84
//如题模仿了2.6.2的方法,page 84
package main
import (
"fmt"
"crypto/sha256"
)
var pc[256]byte
func init() {
for i:=range pc{
pc[i]=pc[i/2]+byte(i&1)
}
}
func main() {
n:=0
c1 := sha256.Sum256([]byte("x"))
c2 := sha256.Sum256([]byte("X"))
for i:=range c1{
c1[i]&=c2[i]
n+=int(pc[c1[i]])
}
fmt.Println(n)
}
exercise4.2/page84
//bufio的使用,page 84
package main
import (
"bufio"
"crypto/sha256"
"crypto/sha512"
"fmt"
"os"
)
func main() {
ch := 0
if len(os.Args)>1 {
flag := os.Args[1]
if flag == "384" {
ch = 1
}
if flag == "512" {
ch = 2
}
}
inputReader := bufio.NewReader(os.Stdin)
fmt.Println("Please enter a string")
input, err := inputReader.ReadString('\n')
if err != nil {
fmt.Println(err)
return
}
switch ch {
case 1:
fmt.Println(sha512.Sum384([]byte(input)))
case 2:
fmt.Println(sha512.Sum512([]byte(input)))
default:
fmt.Println(sha256.Sum256([]byte(input)))
}
}
exercise4.6/page93
//this piece of code is written by myself,page 94
package main
import (
"fmt"
"unicode"
"unicode/utf8"
)
func main() {
bytes:=[]byte{'\t','\n','\v','a'}
fmt.Println(bytes)//[9,10,11,128]
bytes=elim(bytes)
fmt.Println(bytes)//[11,97]
}
func elim(bytes []byte)[]byte{
j:=0
for i:=0;i<len(bytes)-1;{
v1,_:=utf8.DecodeRune(bytes[i:])
v2,_:=utf8.DecodeRune(bytes[i+1:]);
if unicode.IsSpace(v1)&&unicode.IsSpace(v2){
copy(bytes[i:],bytes[i+1:])
bytes[i]=' '
j++
} else{
i++
}
}
return bytes[:len(bytes)-j]
}
exercise4.1/page99
//the usage of function bufio and scanner, did it myself, page 99
package main
import (
"fmt"
"os"
"bufio"
)
func main() {
rec:=make(map[string]int)
file,err:=os.Open("hello.txt")
if err!=nil{
fmt.Println(err)
return
}
input:=bufio.NewScanner(file)
input.Split(bufio.ScanWords)
for input.Scan(){
rec[input.Text()]++
}
for i,v:=range rec{
fmt.Println(i,v)
}
}
exercise5.10/page140
//use maps instead of slices
func topoSort(m map[string][]string) []string {
var order []string
seen := make(map[string]bool)
var visitAll func(items map[string]bool)
visitAll = func(items map[string]bool) {
for item := range items {
if !seen[item] {
seen[item] = true
order = append(order, item)
nums2:=make(map[string]bool)
for _,nu:=range m[item]{
nums2[nu]=true
}
visitAll(nums2)
//order = append(order, item)
}
}
}
nums:=make(map[string]bool)
for num:=range m{
nums[num]=true
}
visitAll(nums)
return order
}
exercise5.17/page143
//the package html needs the command:"go get ... " and vpn
//the code is modified from "gopl.io/ch5/links.go"
package main
import (
"fmt"
"net/http"
"golang.org/x/net/html"
)
func main() {
url:="http://gopl.io"
resp, err := http.Get(url)
if err != nil {
fmt.Println("get failed")
return
}
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
fmt.Errorf("getting %s: %s", url, resp.Status)
}
doc, err := html.Parse(resp.Body)
resp.Body.Close()
if err != nil {
fmt.Errorf("parsing %s as HTML: %v", url, err)
}
t:=ElementsByTagName(doc,"a")
for _,v:=range t{
fmt.Println(v)
}
}
//returns the elements whose data matches one of the slice name
func ElementsByTagName (doc *html.Node,name...string)[]*html.Node{
var nodes []*html.Node
for _,s:=range name{
visitNode := func(n *html.Node) {
if n.Type == html.ElementNode && n.Data == s{
nodes = append(nodes, n)
}
}
forEachNode(doc, visitNode, nil)
}
return nodes
}
// Copied from gopl.io/ch5/outline2.
func forEachNode(n *html.Node, pre, post func(n *html.Node)) {
if pre != nil {
pre(n)
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
forEachNode(c, pre, post)
}
if post != nil {
post(n)
}
}
exercise5.18/page149
/the "defer" statement can deal with the "return" value, marvelous! Only "Fetch" is here since nothing else has been changed
// Fetch saves the contents of a URL into a local file.
// Fetch downloads the URL and returns the
// name and length of the local file.
func fetch(url string) (filename string, n int64, err error) {
resp, err := http.Get(url)
if err != nil {
return "", 0, err
}
defer resp.Body.Close()
local := path.Base(resp.Request.URL.Path)
if local == "/" {
local = "index.html"
}
f, err := os.Create(local)
if err != nil {
return "", 0, err
}
defer func() {
if err==nil{
err=f.Close()
}
f.Close()
}()
n, err = io.Copy(f, resp.Body)
return local, n, err
}
exercise5.19/page153
//I found that if a function which has a value to return ends with the "panic" statement, "return" statement is not necessary
package main
import "fmt"
func noret() (err error){
defer func(){
if p:=recover();p!=nil{
err=fmt.Errorf("%v",p)
}
}()
panic("google")
}
func main() {
a:=noret()
fmt.Println(a)
}
exercise6.1&6.2/page167
//the method remove is an enhanced version which removes useless slice elements when removing
func (s *IntSet) Add(x ...int) {
for _,x:=range x {
word, bit := x/64, uint(x%64)
for word >= len(s.words) {
s.words = append(s.words, 0)
}
s.words[word] |= 1 << bit
}
}
func (s *IntSet) Len() int {
return len(s.words)
}
func (s *IntSet) Clear() {
s.words = nil
}
func (s *IntSet) Remove(x ...int) {
for _,x:=range x {
word, bit := x/64, uint(x%64)
if s.Has(x) {
s.words[word] ^= 1 << bit
for lent := len(s.words) - 1; s.words[lent] == 0; lent-- {
if lent == 0 {
s.Clear()
break
}
s.words = s.words[:lent]
}
}
}
}
func (s *IntSet) Copy() *IntSet {
t:= IntSet{}
copy(t.words,s.words)
return &t
}
exercise7.1/page173
//"scanner" is very useful
type WordCounter int
func (w *WordCounter)Write(p []byte)(int, error) {
s:=bytes.NewReader(p)
bs:=bufio.NewScanner(s)
bs.Split(bufio.ScanWords)
for bs.Scan(){
*w++
}
return int(*w),nil
}
type LineCounter int
func (l *LineCounter)Write(p []byte)(int ,error){
s:=bytes.NewReader(p)
bs:=bufio.NewScanner(s)
bs.Split(bufio.ScanLines)
for bs.Scan(){
*l++
}
return int(*l),nil
}
exercise7.2/page174
//I am not sure whether it's right
func CountingWriter(w io.Writer)(io.Writer, *int64) {
var w1 io.Writer
w1=w
var buf bytes.Buffer
fmt.Fprint(&buf,w1)
ln:=int64(len(buf.Bytes()))
return w1,&ln
}
exercise7.3/page174
//easy inorder traversal, not sure of the correctness
func (t *tree) String() (s string) {
if t != nil {
if t.left!=nil{
s +=t.left.String()
}
s += strconv.Itoa(t.value)+" "
if t.right!=nil {
s +=t.right.String()
}
}
return s
}
exercise7.4/page175
//my own newreader function
//almost copy from the package "strings", the interface
//"Reader" is very useful
type Reader struct{
s string
i int //current reading index
}
func NewReader(s string) *Reader {
return &Reader{s,0}
}
func (r *Reader) Read(b []byte) (n int, err error) {
if len(b)==0{
return 0,nil
}
if r.i>=len(r.s){
return 0,io.EOF
}
n=copy(b,r.s[r.i:])
r.i+=n
return
}
exercise7.4/page175
//not sure of the correctness
func LimitReader(r io.Reader,n int64)io.Reader{
var buf bytes.Buffer
fmt.Fprint(&buf,r)
if int64(len(buf.Bytes()))>n{
return bytes.NewReader(buf.Bytes()[:n])
panic(io.EOF)
}
return bytes.NewReader(buf.Bytes())
}
exercise7.11/page195
//other functions are similar to this
type dollars float32
func (d dollars) String() string { return fmt.Sprintf("$%.2f", d) }
type database struct {
data map[string]dollars
mu sync.Mutex
}
func (db database) create(w http.ResponseWriter, req *http.Request) {
db.mu.Lock()
item := req.URL.Query().Get("item")
if _, ok := db.data[item]; ok {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "already exists! item: %v ", item)
defer db.mu.Unlock()
return
}
price, _ := strconv.ParseFloat(req.URL.Query().Get("price"), 32)
if price < 0 {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "can not be negative! price: %v", item)
defer db.mu.Unlock()
return
}
db.data[item] = dollars(price)
fmt.Fprintf(w,"create complete:\n%v: %v\n",item,price)
db.mu.Unlock()
}
func main() {
db := database{map[string]dollars{"shoes": 50, "socks": 5}, sync.Mutex{}}
http.HandleFunc("/list", db.list)
http.HandleFunc("/price", db.price)
http.HandleFunc("/create", db.create)
http.HandleFunc("/update", db.update)
http.HandleFunc("/delete", db.delete)
log.Fatal(http.ListenAndServe("localhost:8000", nil))
}
exercise7.12/page195
/*纸上得来终觉浅,绝知此事要躬行!书上的练习做了之后才理解更深刻!而且更加强了找相关 信息的能力,模仿也很重要,虽然可能对于厉害的人来说这很简单,但是做出来这道练习我还是很开心的,让我对之前的看过的内容有了新的认识*/
func (db database) list(w http.ResponseWriter, req *http.Request) {
List.Execute(w, db)
}
var List = template.Must(template.New("List").Parse(`
<h1>list</h1>
<table>
<tr style='text-align: left'>
<th>Item</th>
<th>Price</th>
</tr>
{{range $k,$v :=.}}
<tr>
<td>{{$k}}</td>
<td>{{$v}}</td>
</tr>
{{end}}
</table>
`))
exercise8.1/page222
8.1完成得有问题,无法按表格在原位置输出时间
port.go
///port.go realizes the portFlag,模仿的书上的flag样例
package port
import (
"fmt"
"flag"
)
type Port string
type portFlag struct{
TZ string
Port
}
func (p portFlag)String ()string{
return fmt.Sprintf("Timezone: %s, Port:%s",p.TZ,p.Port)
}
func (p *portFlag)Set (s string)error{
p.Port=Port(s)
switch s{
case "8010":
p.TZ="NewYork"
return nil
case "8020":
p.TZ="London"
return nil
case "8030":
p.TZ="Tokyo"
return nil
}
return fmt.Errorf("invalid port %q",s)
}
func PortFlag(name string, value Port,usage string)*portFlag {
f:=portFlag{"Beijing",value}
flag.CommandLine.Var(&f,name,usage)
return &f
}
clock.go
// Clock is a TCP server that periodically writes the time.
package main
import (
"io"
"log"
"net"
"time"
"gopl.io/ch8/port"
"flag"
)
var portflag=port.PortFlag("port","8000","the port")
func handleConn(c net.Conn) {
defer c.Close()
for {
_, err := io.WriteString(c, portflag.TZ+": "+time.Now().Format("15:04:05\n"))
if err != nil {
return // e.g., client disconnected
}
time.Sleep(1 * time.Second)
}
}
func main() {
flag.Parse()
listener, err := net.Listen("tcp", "localhost:"+string(portflag.Port))
if err != nil {
log.Fatal(err)
}
//!+
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err) // e.g., connection aborted
continue
}
go handleConn(conn) // handle connections concurrently
}
//!-
}
clockwall.go
// clockwall is a read-only TCP client.
package main
import (
"io"
"log"
"net"
"os"
)
func main() {
go call("8010")
go call("8020")
call("8030")
}
func mustCopy(dst io.Writer, src io.Reader) {
if _, err := io.Copy(dst, src); err != nil {
log.Fatal(err)
}
}
func call(port string) {
conn, err := net.Dial("tcp", "localhost:"+port)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
mustCopy(os.Stdout, conn)
}