Go Language Bible - Introduction to Chapter 1 - 1.3&1.4 Find Duplicate Lines & GIF

Posted by Iasonic on Wed, 13 Oct 2021 19:00:29 +0200

Chapter 1 Introduction

1.3 Find duplicate rows

In Section 1.2, we have a brief overview of the basic grammar of the Go language. Next, let's take a look at an example to learn more.

Programs that copy, print, search, sort, count, or similar files all have a similar program structure: a loop that handles inputs, performs computational processing on each element, and produces output at the same time or at the end of the processing. We'll take a closer look at this program structure through some cases.

case 1:

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

1) Define a variable of type map, map is a collection of stored keys/values, where keys and values can be of any type, and create a map here using the built-in make function. Map types are also mentioned later

counts := make(map[string]int)

2) The second variable input is defined by the NewScanner function under the bufio package, so let's take a peeled onion look at this function

input:= bufio.NewScanner(os.Stdin)

The NewScanner function has a parameter r of type io.Reader and a return value of *Scanner. Looking at the function body, we find that it is a reference to a structure (when a variable itself is large and not easy to pass, we tend to choose to refer to it with a pointer instead of passing it directly)

func NewScanner(r io.Reader) *Scanner {
   return &Scanner{
      r:            r,
      split:        ScanLines,
      maxTokenSize: MaxScanTokenSize,
   }
}

Let's continue, io.Reader is not a base data/conform data type other non-custom type, so we'll have to go ahead and peel the onion to see what it is. It was originally an interface under the IO package. The interface also contains a method:Read, the Read method has a Reader receiver, and a byte slice type parameter p.The return value is an int type and an err interface with a method Error in it. So we finally stripped the onions

In fact, the general logic is usually: functions are interfaces, interfaces are methods, methods are non-custom variables. Of course, in more examples, we can also see that functions, interfaces, and even more custom types are full of all kinds of nesting.

type Reader interface {
   Read(p []byte) (n int, err error)
}
func (Reader) Read(p []byte) (n int, err error)
type error interface {
	Error() string
}
func (error) Error() string

Returning to the variable input, we pass a variable to NewScanner: the variable Stdin under the os package

input:= bufio.NewScanner(os.Stdin)

Let's go on and see what it is. It was the return value of the newFile function and it was a reference type. So what exactly did the newFile function do? It called another package inside the function body, made a judgment, and returned the reference value

var (
   Stdin  = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
)
func NewFile(fd uintptr, name string) *File {
	h := syscall.Handle(fd)
	if h == syscall.InvalidHandle {
		return nil
	}
	return newFile(h, name, "file")
}

Looking at the detailed invocation process, let's summarize the bufio package, which makes processing inputs and outputs convenient and efficient. The Scanner type is one of the most useful features of the package, which reads input and splits it into rows or words; it's usually the easiest way to process input in line form. Programs use short variable declarations to create variable input of type bufio.Scanner: =Bufio.NewScanner (os.Stdin) This variable reads from the program's standard input. Each call to input.Scan(), reads in the next line and removes the line break at the end of the line; the read can be obtained by calling input.Text(). The Scan function returns true when reading a line and false when there is no more input.

3) Two for loops

In the previous section, we looked at several variables (parameters) defined by customization, return values of functions, etc. Once these parameters have been obtained, use the for loop to process and output them

for input.Scan(){
		counts[input.Text()]++
	}
	for line,n :=range counts{
		if n>1{
			fmt.Printf("%d\t%s\n",n,line)
		}
	}

4) fmt.printf function

The fmt.Printf function produces formatted output for some expressions. Here is the common formatted output syntax

%d Decimal integer
%x, %o, %b Hexadecimal, Octal, Binary Integer
%f, %g, %e Floating point number: 3.141593 3.141592653589793 3.141593e+00 
%t Boolean: true or false 
%c Characters ( rune) (Unicode Code Point)
%s Character string 
%q A double quoted string"abc"Or single-quoted characters'c' 
%v The natural form of a variable ( natural format) 
%T Type of variable%% Literal percent sign (no operands)

case 2:

The case 2 process is slightly more complicated, but logically identical to case1

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!=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()]++
   }
}

case 3:Same

I believe there are many students who see this is already dirty, but that's okay. First we can try to understand cases, wait a little deeper, and then look back at these cases, we can see at a glance.

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)
      }
   }
}

1.4 GIF Animation

Let's continue with an example and learn more

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
   blackIndex = 1
)

func main() {
   rand.Seed(time.Now().UTC().UnixNano())
   lissajous(os.Stdout)
}

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

In this example, we use data structures such as structs and floating-point types. As for the logic of the whole program, it will be more complex. Let's wait to learn more about the Go Language.

Topics: Go