Go language Bible Chapter 1

Posted by chacha102 on Sat, 05 Mar 2022 09:07:39 +0100

1, Introduction

1,hello world

  • Go doesn't need a semicolon
  • The "{" symbol must be on the same line as the keyword func and cannot be on its own. In the expression of x+y, the newline character can be after the + operator, but not before the + operator
  • In order to write the main method in multiple files under a package at the same time, it can be added in front of useless files
//go:build ignore
// +build ignore

2. Command line parameters

  • The os package provides some functions and variables. The command line parameters are accessed by the program with the args variable in the os package. Outside the os package, use os Args. Variable os Args is a string slice.
  • slice can be understood as an ordered array s of dynamic capacity.
    • Access a single element through s[i]
    • Access a continuous sub interval through s[m:n]
      • s[m:n] represents a slice from the m-th to the n-1 st element
      • os.Args[1 : len(os.Args)]
      • If m or n is missing, the default is 0 or len(s), respectively. Therefore, the above formula can be abbreviated as OS Args[1:].
    • The array length is represented by len(s)
  • Comments start with / / and are written before the declaration of a package
  • j = i + + is illegal and only supports suffixes, so – i is also illegal
  • for is the only loop statement in Go
for initialization; condition; post{
    //...
}

//Traditional while loop
for condition{
    //...
}

//Traditional infinite loop
for{
    //...
}
  • The index variable i of the loop is declared at the beginning of the for loop.:= Symbols are used for short variable declarations
func main() {
	var s, sep string
	for i := 1; i < len(os.Args); i++ {
		s += sep + os.Args[i]
		sep = " "
	}
	fmt.Println(s)
}
  • Go does not allow useless temporary variables. The solution is to use an empty identifier: "" (i.e. underline)
func main() {
	s, sep := "", ""
	for _, arg := range os.Args[1:] {
		s += sep + arg
		sep = ""
	}
	fmt.Println(s)
	fmt.Print(2)
}
  • Supplementary usage range
/*
	Go range keyword in language is used for
		·for Iterate the elements of array, slice, channel or map in the loop;
		·In arrays and slices, it returns the index value of the element,
		·Returns the key value of the key value pair in the collection.
*/
package main

import "fmt"

func main() {
	//Use range to find the sum of a slice. Similar to using arrays
	nums := []int{2, 3, 4}
	sum := 0
	for _, num := range nums {
		sum += num
	}
	fmt.Println("sum:", sum)
	//Use range on the array to pass in two variables: index and value
	for i, num := range nums {
		if num == 3 {
			fmt.Println("index:", i)
		}
	}
	//range can also be used on key value pairs of map
	kvs := map[string]string{"a": "apple", "b": "banana"}
	for k, v := range kvs {
		fmt.Println("%s -> %s\n", k, v)
	}
	//range can also be used to enumerate Unicode strings.
	//The first parameter is the index of the character, and the second is the character (Unicode value) itself
	for i, c := range "go" {
		fmt.Println(i, c)
	}
}

  • The following ways to declare string variables are equivalent:
s := ""
var s string
var s = ""
var s string = ""
  • There can only be one main() method under a package. You can add / / + build ignore in front of files that do not need to be compiled
  • If the connection involves a large amount of data, this method is expensive. A simple and efficient solution is to use the Join function of the strings package:
func main() {
	fmt.Println(strings.Join(os.Args[1:], " "))
}
  • Finally, if you don't care about the output format and just want to see the output value, maybe just for debugging, you can format the output for us with Println
fmt.Println(os.Args[1:])
  • The output of this statement is the same as strings The results of join are very similar, but they are put in square brackets. Slices are printed in this format.

3. Find duplicate lines

dup1

//dup1 outputs a line that appears more than 1 times in the standard input, preceded by the number of times
func main() {	
	//The built-in function make can be used to create a new map
	counts := make(map[string]int)
	//Map stores a set of key / value pairs with the structure of map[key]value
	//key supports any type of equal (= =) comparison;
	//Values can be of any type
	//In this example, the key type is string and the value is int
	input := bufio.NewScanner(os.Stdin)
	//The bufio package creates a standard input stream
	for input.Scan() {
		//Equivalent to: counts[input.Text()]++
		line := input.Text()
		if line == "bye" {
			break //End input when entering bye
		}
		counts[line]++
	}
	//Note: ignore input Possible errors in err()
	for line, n := range counts { //Traversing input using range
		if n > 1 {
			fmt.Printf("%d\t%s\n", n, line)
			//f stands for formatted output format
			//All functions used for format ting will end with the letter f at the end,
			//For example, log Printf,fmt. Errorfand a series of functions ending with ln 			 Abbreviations), these functions format their parameters in% v by default, and will automatically add a line break after the output.

		}
	}
}

Running example

  • The Printf function has more than 10 such escape characters, which Go programmers call verb
  • Tab \ t line break \ n

dup2

  • Many programs can be read from standard input like dup, or from specific files. This version of the dup program can be read from standard input or a file list, using OS Open function to open one by one
//dup2 print the number of lines and text that appear multiple times in the input
package main
import (
	"bufio"
	"fmt"
	"os"
)
func main() {
	counts := make(map[string]int)
	files := os.Args[1:] //Gets the file name in the parameter
	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)
				continue //If an error occurs, terminate the cycle and continue
			}
			countLines(f, counts)
			f.Close()
		}
	}
	for line, n := range counts {
		if n > 1 {
			fmt.Printf("%d\t%s\n", n, line)
		}
	}
}

func countLines(f *os.File, counts map[string]int) {
	input := bufio.NewScanner(f)
	for input.Scan() {
		counts[input.Text()]++
	}
	//Note: ignore input Possible errors in err()
}

Running example

  • This version of dup uses "streaming" mode to read input and then split it into lines as needed, so that the program can handle a large amount of input in principle.

dup3

  • This version of dup will read the entire input into a large block of memory at one time, split all rows at once, and then process them.
  • ReadFile function (from the io/ioutil package): reads the contents of the entire named file
  • strings.Split function: divides a string into a slice composed of substrings (where split is the inverse operation of strings.Join)
  • Simplification of dup3:
    • Read only the specified file, not standard input, because ReadFile requires a file name as a parameter
    • Because it is only used in the current working line, the main function is put back
package main
import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)
func main() {
	counts := make(map[string]int)
	for _, filename := range os.Args[1:] {
		data, err := ioutil.ReadFile(filename)
		//The ReadFile function returns a byte slice that can be converted into a string
		//So it can be called strings Split split
		if err != nil {
			fmt.Fprintf(os.Stderr, "dup3: %v\n", err)
			continue
		}
		for _, line := range strings.Split(string(data), "\n") {
			counts[line]++
		}
	}
	for line, n := range counts {
		if n > 1 {
			fmt.Println("%d\t%s\n", n, line)
		}
	}
}

practice

  • Modify dup2 and print the file name when duplicate lines appear
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 {
		countLine(os.Stdin, counts)
	} else {
		for _, arg := range files {
			f, err := os.Open(arg)
			if err != nil {
				fmt.Fprintf(os.Stderr, "dup2: %v\n", err)
				continue
			}
			countLine(f, counts)
			f.Close()
		}
	}
	for line, n := range counts {
		if n.Count > 1 {
			fmt.Printf("%d\t%v\t%s\n", n.Count, n.FileNames, line)
		}
	}
}

func countLine(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())
		}
	}
}

Running example

4. GIF animation

//lissajous generates GIF animation of random lissajous graphics
package main
import (
	"bytes"
	"image"
	"image/color"
	"image/gif"
	"io"
	"io/ioutil"
	"math"
	"math/rand"
	"time"
)
var palette = []color.Color{color.White, color.Black}
const (
	whiteIndex = 0 // The first color in the drawing board
	blackIndex = 1 // Next color in the palette
)
func main() {
	//rand.Seed(time.Now().UTC().UnixNano())
	//if len(os.Args) > 1 && os.Args[1] == "web" {
	//	handler := func(w http.ResponseWriter, r *http.Request) {
	//		lissajous(w)
	//	}
	//	http.HandleFunc("/", handler)
	//	log.Fatal(http.ListenAndServe("localhost:8000", nil))
	//	return
	//}
	//lissajous(os.Stdout)
	
	//If it's Rand Int, the random numbers obtained at this time are all repeated random data
	//The following writing method uses the current time seeding pseudo-random number generator to ensure that each random is random
	rand.Seed(time.Now().UTC().UnixNano())
	//Change to direct output byte file
	buf := &bytes.Buffer{}
	lissajous(buf)
	if err := ioutil.WriteFile("output.gif", buf.Bytes(), 0666); err != nil {
		panic(err)
	}
}
func lissajous(out io.Writer) {
	const ( //const is used to name constants
		cycles  = 5     // Number of complete x oscillator changes
		res     = 0.001 // Angular resolution
		size    = 100   // The image canvas contains [- size..+size]
		nframes = 64    // Number of frames in the animation
		delay   = 8     // Inter frame delay in 10ms
	)
	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 < 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
		//append function: add elements at the end of the original slice
		anim.Delay = append(anim.Delay, delay)
		anim.Image = append(anim.Image, img)
	}
	gif.EncodeAll(out, &anim) // Note: ignore coding errors
}

Running example

practice

  • Exercise 1.5: change the color palette in the Lissajous program from black to green. We can use color RGBA {0xrr, 0xgg, 0xbb, 0xff} to get the color value of #RRGGBB. The three hexadecimal strings represent red, green and blue pixels respectively
var palette = []color.Color{color.RGBA{0, 0, 0, 0xFF}, 
color.RGBA{0, 0xFF, 0, 0xFF}}

Running example

  • Exercise 1.6: modify the Lissajous program, modify its palette to generate richer colors, and then modify the third parameter of SetColorIndex to see the display results
package main

import (
	"bytes"
	"image"
	"image/color"
	"image/gif"
	"io"
	"io/ioutil"
	"math"
	"math/rand"
	"time"
)

const nColors = 10

func main() {
	seed := time.Now()
	//Unix returns t as Unix time, which is the number of seconds elapsed since January 1, 1970 UTC time. The results do not depend on the location associated with T.
	//unix like operating systems usually record time as 32-bit seconds, but because the method here returns a 64 bit value, it has been effective for billions of years in the past or in the future.
	rand.Seed(seed.Unix())

	var palette []color.Color //Palette palette
	for i := 0; i < nColors; i++ {
		r := uint8(rand.Uint32() % 256)
		g := uint8(rand.Uint32() % 256)
		b := uint8(rand.Uint32() % 256)
		palette = append(palette, color.RGBA{r, g, b, 0xff})
	}

	buf := &bytes.Buffer{}
	lissajous1(buf, palette)
	if err := ioutil.WriteFile("output2.gif", buf.Bytes(), 0666); err != nil {
		panic(err)
	}
}

func lissajous1(out io.Writer, palette []color.Color) {
	const (
		cycles  = 5
		res     = 0.001
		size    = 100
		nframes = 64
		delay   = 8
	)
	freq := rand.Float64() * 3.0
	anim := gif.GIF{LoopCount: nframes}
	phase := 0.0
	for i := 0; i < nframes; i++ {
		rect := image.Rect(0, 0, 2*size+1, 2*size+1)
		img := image.NewPaletted(rect, palette)
		nColor := uint8(i % len(palette))
		for t := 0.0; t < 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), nColor)
		}
		phase += 0.1
		anim.Delay = append(anim.Delay, delay)
		anim.Image = append(anim.Image, img)
	}
	gif.EncodeAll(out, &anim)
}

Running example

5. Get a URL

  • The program fetch shows the minimum need to obtain information from the Internet. It obtains the content of each specified URL and then outputs it without parsing.
  • http. The get function generates an HTTP request
    • If there is no error, the returned result is stored in the response structure resp, where the Body field of resp contains a readable data stream of the server-side response
    • Using ioutil Readall can read all contents in the Body field
    • Body.Close is used to close the body stream to avoid resource leakage, and use Printf to output the response to standard output
  • os. The exit function terminates the process and returns a status error code with a value of 1

Topics: Go Back-end