go Bible notes - Introduction


Configure the idea environment (go version 1.17.1) and learn according to the go language Bible directory.

Hello, World

package main

import "fmt"

func main() {
	/* This is my first simple program */
	fmt.Println("Hello, World!")
}

Go is a compiled language. The tool chain of go language converts the source code and its dependencies into the machine instructions of the computer. The tools provided by go language are called through a separate command go, which has a series of subcommands. The simplest subcommand is run. This command compiles one or more to Go ends the source file, links the library file, and runs the final generated executable file.

$ go run helloworld.go 

Compile instruction

$ go build helloworld.go 

The Mac compiler generates helloworld.com Exec executable.

package

The code of go language is organized through packages, which are similar to libraries or modules in other languages. A package consists of one or more packages located in a single directory Go source code file composition, directory defines the role of the package. Each source file starts with a package declaration statement. In this example, package main indicates which package the file belongs to, followed by a series of import ed packages, followed by the program statements stored in the file.

Go's standard library provides more than 100 packages to support common functions such as input, output, sorting and text processing.

The required packages must be imported properly. If the necessary packages are missing or unnecessary packages are imported, the program cannot be compiled. This strict requirement avoids the introduction of unused packages during program development.

The Go language does not need to add a semicolon at the end of a statement or declaration unless there are multiple statements on a line. In fact, the compiler will actively convert the line break after a specific symbol into a semicolon, so the position where the line break is added will affect the correct parsing of Go code ]Or}). for instance, The left parenthesis {of the function must be on the same line as the func function declaration and at the end, and cannot be exclusive. In the expression x + y, the line can be wrapped after + and not before + (there are resolvable keywords or characters at the end of the line)

Command line parameters

os package provides some functions and variables that interact with the operating system in a cross platform manner. The command line parameters of the program can be obtained from the args variable of the os package; os package external use os Args accesses this variable.

os.Args variable is a slice of string. Slice is the basic concept of Go language, which will be introduced in detail later. Now take slice s as a sequence of array elements. The length of the sequence changes dynamically. Use s[i] to access a single element, and use s[m:n] to obtain subsequences.

The following is an implementation of echo command in Unix. Echo prints its command-line parameters into one line. The program imports two packages, which are enclosed in parentheses and written as a list instead of separate import declarations. Both forms are legal, and the list form is used much more.

package main

import (
   "fmt"
   "os"
)

func main() {
   var s, sep string
   for i := 1; i < len(os.Args); i++ { //Symbol: = is part of a short variable declaration
      s += sep + os.Args[i]
      sep = " "
   }
   fmt.Println(s)
}
for initialization; condition; post {
    // zero or more statements
}

The three parts of the for loop need not be surrounded by parentheses. Braces enforce that the left brace must be on the same line as the post statement.

The initialization statement is optional and is executed before the start of the loop. If initialization exists, it must be a simple statement, that is, short variable declaration, self increment statement, assignment statement or function call. Condition is a boolean expression whose value is calculated at the beginning of each loop iteration. If true, the loop body statement is executed. The post statement executes after the execution of the loop body, and then evaluates the condition again. When the condition value is false, the loop ends.

Each of the three parts of the for loop can be omitted. If initialization and post are omitted, the semicolon and condition can also be omitted (infinite loop).

// a traditional "while" loop
for condition {
    // ...
}

Another form of a for loop, traversing a range of some data type, such as a string or slice.

os. The first element of args: OS Args [0], is the name of the command itself; Other elements are parameters passed to the program when it starts. The slice expression in the form of s[m:n] generates slices from the m-th element to the n-1 st element. The elements used in the next example are included in OS Args [1: len (os.Args)] slice. If m or n of the slice expression is omitted, 0 or len(s) will be passed in by default, so the previous slice can be abbreviated as OS Args[1:].

The second version of echo shows this form:

package main

import (
   "fmt"
   "os"
)

func main() {
   s, sep := "", ""
   for _, arg := range os.Args[1:] {
      s += sep + arg
      sep = " "
   }
   fmt.Println(s)
}

The solution to this situation in Go language is to use blank identifier, i.e_ (i.e. underline). The null identifier can be used to discard the unnecessary loop index and retain the element value when any syntax requires a variable name but the program logic does not (e.g. in a loop). Most Go programmers use range and like this_ Write the echo program because it implicitly rather than explicitly indexes OS Args, easy to write right. It's just a little concise.

This version of echo uses * * a short variable declaration to declare and initialize s * * and seps. These two variables can also be declared separately. There are several ways to declare a variable. The following are equivalent:

s := ""
var s string
var s = ""
var s string = ""

Which one to use, which one not to use, and why? The first form is a short variable declaration, which is the most concise, but can only be used inside functions, not package variables. The second form depends on the default initialization zero value mechanism of the string and is initialized to "". The third form is rarely used unless multiple variables are declared at the same time. The fourth form explicitly indicates the type of variable. When the variable type is the same as the initial value type, the type is redundant, but if the two types are different, the variable type must be. In practice, one of the first two forms is generally used. If the initial value is important, the type of variable is explicitly specified, otherwise implicit initialization is used.

The contents of the string s are updated each time the loop iterates+= Connect the original string, space and the next parameter to generate a new string and assign it to s. The original content of S is no longer used. It will be garbage collected at an appropriate time.

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:], " "))
}

A little look at the Join function will not produce a new string.

func Join(elems []string, sep string) string {
   switch len(elems) {
   case 0:
      return ""
   case 1:
      return elems[0]
   }
   n := len(sep) * (len(elems) - 1)
   for i := 0; i < len(elems); i++ {
      n += len(elems[i])
   }

   var b Builder
   b.Grow(n)
   b.WriteString(elems[0])
   for _, s := range elems[1:] {
      b.WriteString(sep)
      b.WriteString(s)
   }
   return b.String()
}

Find duplicate lines

Programs that copy, print, search, sort, count, or similar files have a similar program structure: a loop that processes input, performs computational processing on each element, and produces output at the same time or finally. We will show three versions of a program called dup; Inspired by Unix's uniq command, it looks for adjacent duplicate lines.

The first version of dup prints lines that appear multiple times in standard input, starting with the number of repetitions. The program will introduce if statement, map data type and bufio package.

// Dup1 prints the text of each line that appears more than
// once in the standard input, preceded by its count.
package main

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

func main() {
    counts := make(map[string]int)
    input := bufio.NewScanner(os.Stdin)
    for input.Scan() {
        counts[input.Text()]++
    }
    // NOTE: ignoring potential errors from input.Err()
    for line, n := range counts {
        if n > 1 {
            fmt.Printf("%d\t%s\n", n, line)
        }
    }
}

The next version of the dup program reads standard input or uses OS Open opens named files and operates on them.

// Dup2 prints the count and text of lines that appear more than once
// in the input.  It reads from stdin or from a list of named files.
// Open multiple files
package main

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

func main() {
    counts := make(map[string]int)
    files := os.Args[1:]
    if len(files) == 0 {
        countLines(os.Stdin, counts)
    } else {
        for _, arg := range files {
            f, err := os.Open(arg)
            // If err is equal to the built-in value nil, the file is successfully opened. Otherwise print
            if err != nil {
                fmt.Fprintf(os.Stderr, "dup2: %v\n", err)
                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: ignoring potential errors from input.Err()
}

Note that the countLines function is called before its declaration. Functions and package level entities can be declared in any order without affecting their invocation. It's better to follow certain norms

A map is a reference to a data structure created by the make function. When a map is passed to a function as a parameter, the function receives a copy of the reference. Any modification of the underlying data structure of the map by the called function can be seen by the caller function through the map reference. In our example, the value inserted by the countLines function into counts will also be seen by the main function. Similar to reference passing in C + +, the pointer is actually another pointer, but the value stored inside points to the same block of memory

Comment: called before its declaration, I can't understand. Compiled languages, the order is irrelevant. But that's wrong.

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)
        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.Printf("%d\t%s\n", n, line)
        }
    }
}

The library functions bufio and io/ioutil are used.

GIF animation

The following program will demonstrate the use of the image package in the Go language standard library. We will use this package to generate a series of bit mapped images, and then encode these images into a GIF animation. The graphics we generated are called Lissajous figures. This effect is a visual effect that appeared in old films in the 1960s. They are the curves generated by the vibration of the co oscillator at two latitudes, such as the curves generated by the input of two sin sine waves on the x-axis and y-axis respectively.

// Lissajous generates GIF animations of random Lissajous figures.
package main

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

var palette = []color.Color{color.White, color.Black}

const (
  whiteIndex = 0 // first color in palette
  blackIndex = 1 // next color in palette
)

func 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())
  lissajous(os.Stdout)
}

func lissajous(out io.Writer) {
  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
  )

  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
    anim.Delay = append(anim.Delay, delay)
    anim.Image = append(anim.Image, img)
  }
  gif.EncodeAll(out, &anim) // NOTE: ignoring encoding errors
}
$ go build gopl.io/ch1/lissajous
$ ./lissajous >out.gif

Get URL

With the help of the powerful package net, Go language can more easily send and receive information through the network, establish a lower layer network connection and write server programs. In these situations, the native concurrency feature of Go language (introduced in Chapter 8) is particularly easy to use.

In order to show the simplest way to obtain information based on HTTP, an example program fetch is given below, which will obtain the corresponding url and print its source text; The inspiration of this example comes from curl tool. Of course, curl provides more complex and rich functions. Only the simplest examples are written here.

// Fetch prints the content found at a URL.
package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
)

func main() {
    for _, url := range os.Args[1:] {
        resp, err := http.Get(url)
        if err != nil {
            fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
            os.Exit(1)
        }
        b, err := ioutil.ReadAll(resp.Body)
        resp.Body.Close()
        if err != nil {
            fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)
            os.Exit(1)
        }
        fmt.Printf("%s", b)
    }
}

1.6. Get multiple URL s concurrently

The most interesting and novel feature of Go language is its support for concurrent programming. Concurrent programming is a big topic, which will be discussed in Chapters 8 and 9. Here, we just have a taste of goroutine and channel in Go language.

The following example fetchall is basically the same as the fetch program in the previous section. The special feature of fetchall is that it will obtain all URL s at the same time, so the total execution time of this program will not exceed the task with the longest execution time. The execution time of the previous fetch program is the sum of the execution time of all tasks.

// Fetchall fetches URLs in parallel and reports their times and sizes.
package main

import (
    "fmt"
    "io"
    "io/ioutil"
    "net/http"
    "os"
    "time"
)

func main() {
    start := time.Now()
    ch := make(chan string)
    for _, url := range os.Args[1:] {
        go fetch(url, ch) // start a goroutine
    }
    for range os.Args[1:] {
        fmt.Println(<-ch) // receive from channel ch
    }
    fmt.Printf("%.2fs elapsed\n", time.Since(start).Seconds())
}

func fetch(url string, ch chan<- string) {
    start := time.Now()
    resp, err := http.Get(url)
    if err != nil {
        ch <- fmt.Sprint(err) // send to channel ch
        return
    }
    nbytes, err := io.Copy(ioutil.Discard, resp.Body)
    resp.Body.Close() // don't leak resources
    if err != nil {
        ch <- fmt.Sprintf("while reading %s: %v", url, err)
        return
    }
    secs := time.Since(start).Seconds()
    ch <- fmt.Sprintf("%.2fs  %7d  %s", secs, nbytes, url)
}

Then focus on research. It is similar to the thread model of java.

Web Services

The built-in Library of Go language makes it extremely easy to write a web server similar to fetch. In this section, we will show a micro server whose function is to return the URL that the current user is visiting. For example, users access http://localhost:8000/hello , then the response is URL Path = “hello”.

// Server1 is a minimal "echo" server.
package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/", handler) // each request calls handler
    log.Fatal(http.ListenAndServe("localhost:8000", nil))
}

// handler echoes the Path component of the request URL r.
func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path)
}

It's easy to stack features on top of this service. A more practical modification is to add a state to the URL accessed. For example, the following version outputs the same content, but will calculate the number of requests; The request result for URL will include the total number of times that various URLs are accessed, except for the direct access to / count URL.

// Server2 is a minimal "echo" and counter server.
package main

import (
    "fmt"
    "log"
    "net/http"
    "sync"
)

var mu sync.Mutex // mutex 
var count int // Shared variable

func main() {
    http.HandleFunc("/", handler)
    http.HandleFunc("/count", counter)
    log.Fatal(http.ListenAndServe("localhost:8000", nil))
}

// handler echoes the Path component of the requested URL.
func handler(w http.ResponseWriter, r *http.Request) {
    mu.Lock()
    count++
    mu.Unlock()
    fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path)
}

// counter echoes the number of calls so far.
func counter(w http.ResponseWriter, r *http.Request) {
    mu.Lock()
    fmt.Fprintf(w, "Count %d\n", count)
    mu.Unlock()
}

The handler function will print the http header and form data of the request, which makes it easier to check and debug the service:

// handler echoes the HTTP request.
func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "%s %s %s\n", r.Method, r.URL, r.Proto)
    for k, v := range r.Header {
        fmt.Fprintf(w, "Header[%q] = %q\n", k, v)
    }
    fmt.Fprintf(w, "Host = %q\n", r.Host)
    fmt.Fprintf(w, "RemoteAddr = %q\n", r.RemoteAddr)
    if err := r.ParseForm(); err != nil {
        log.Print(err)
    }
    for k, v := range r.Form {
        fmt.Fprintf(w, "Form[%q] = %q\n", k, v)
    }
}

Key points of this chapter

Named type

Type declarations make it easy to give a name to a particular type. Because struct type declarations are usually very long, we always have to give this kind of struct a name. In this chapter, there is an example of two-dimensional point type:

type Point struct {
    X, Y int
}
var p Point

Pointer

The Go language provides pointers. A pointer is a data type that directly stores the memory address of a variable. In other languages, such as C, pointer operations are completely unconstrained. In other languages, pointers are generally treated as "references", and you can't do much about them except passing them around. The Go language strikes a balance between these two scopes. The pointer is a visible memory address. The & operator can return the memory address of a variable, and the * operator can obtain the content of the variable pointed to by the pointer. However, there is no pointer operation in Go language, that is, it can't add or subtract the pointer like in C language.

Methods and interfaces

Methods are a class of functions associated with named types. The special thing in Go language is that methods can be associated with any named type

packages

Go language provides some useful packages, and these packages can be extended. The go language community has created and shared a lot. So in most cases, go language programming is to use the existing package to write our own code.

Keywords: Go Back-end

Added by khurramijaz on Tue, 25 Jan 2022 19:45:24 +0200