The basic structure of golang language data code

Overview

We will use a few sections to learn the basics of Go language. The structure of this article is as follows:

数据
    new 分配
    构造函数与复合字面
    make 分配
    数组
    切片
    二维切片
    映射
    打印
    追加
 初始化
    常量
    变量
    init 函数
数据

This section covers the way Go allocates memory for variables, and the commonly used array and map data structures.

Go provides two allocation methods, the built-in functions new and make.

key point:

make only applies to maps, slices and channels and does not return pointers.
To get a clear pointer, use new to allocate memory.
The
format of the new function allocated by new is: new(T)
Features: It returns a pointer to the newly allocated zero value of type T

The built-in function new is a built-in function used to allocate memory, but unlike functions of the same name in other languages, it does not initialize the memory, but only resets the memory to zero.

Compared with Java, Go's new is that Java can initialize an object through new execution construction, while Go cannot initialize (assign initial value), it can only be set to "zero value"

In other words, new(T) will allocate zeroed memory space for a new item of type T and return its address, which is a value of type *T. In Go terms, it returns a pointer to a newly allocated zero value of type T.

This design eliminates the need for colorful constructors and parameters for different objects like Java.

Since the memory returned by new has been zeroed, no further initialization is necessary. The user only needs to create a new object with new to work normally.

E.g:

bytes.Buffer 的文档中提到“零值的 Buffer 就是已准备就绪的缓冲区。"
sync.Mutex 并没有显式的构造函数或 Init 方法, 而是零值的 sync.Mutex 就已经被定义为已解锁的互斥锁了。
p := new(SyncedBuffer) // type *SyncedBuffer
var v SyncedBuffer // type SyncedBuffer

The above two methods will allocate memory space, but the types are different.

Constructor and compound literal In
some scenarios, an initialization constructor is still needed, as shown in this code in the os package:

func NewFile(fd int, name string) *File {
    if fd < 0 {
        return nil
    }
    f := new(File)
    f.fd = fd
    f.name = name
    f.dirinfo = nil
    f.nepipe = 0
    return f
}

The above code is too verbose. We can simplify it by using compound literals:

func NewFile(fd int, name string) *File {
    if fd < 0 {
        return nil
    }
    f := File{fd, name, nil, 0}
    return &f
}

Note that File{fd, name, nil, 0} is a compound literal. This expression creates a new instance each time it is evaluated.

The fields of compound literals must be listed in order. But if the elements are clearly marked in the form of field:value pairs, they can appear in any order when initializing the fields, and the field values ​​that are not given will be assigned a zero value. Therefore, we can use the following form:

return &File{fd: fd, name: name} The format of make to allocate the built-in function make is: make(T, args)
Features: It is only used to create slices, maps and channels, and the return type is T (not *T) ) Is an initialized (not zeroed) value.

Slices, maps, and channels are essentially reference data types and must be initialized before use. For example, a slice is a descriptor with three items, including a pointer to (inside the array) data, length, and capacity. Before these three items are initialized, the slice is nil.

For slices, maps, and channels, make is used to initialize the internal data structure and prepare the values ​​to be used.

For example: make([]int, 10, 100) allocates an array space with 100 ints, and then creates a
slice structure new([]int) with a length of 10 and a capacity of 100, pointing to the first 10 elements in the array Will return a pointer to the newly allocated, zeroed slice structure, that is, a pointer to the nil
slice value.

The following example illustrates the difference between new and make:

var p *[]int = new([]int)       // 分配切片结构;*p == nil;基本没用
var v  []int = make([]int, 100) // 切片 v 现在引用了一个具有 100 个 int 元素的新数组
 
// 没必要的复杂:
var p *[]int = new([]int)
*p = make([]int, 100, 100)
 
// 习惯用法:
v := make([]int, 100)

Explain again the key points:

make only applies to maps, slices and channels and does not return pointers.
To get a clear pointer, use new to allocate memory.
Arrays
Arrays are very useful when planning memory layout, and sometimes they can avoid excessive memory allocation. In Go, arrays are mainly used as slice components and used when constructing slices.

The main difference between arrays in Go and C. In Go:

The array is the value. Assigning one array to another will copy all its elements.
If you pass an array to a function, it will receive a copy of the array instead of a pointer.
The size of the array is part of its type. The types [10]int and [20]int are different.
Array-valued properties are useful, but costly; if you want the behavior and efficiency of C, you can pass a pointer to the array.

In Go, the more accustomed usage is to use slices.

Slicing
Slicing provides a more versatile, powerful and convenient way for sequenced data by encapsulating arrays.

Except for cases such as matrix transformations that require explicit dimensions, most array programming in Go is done by slicing.

A slice holds a reference to the underlying array. If you assign a slice to another slice, they will refer to the same array. If a function passes a slice as a parameter, its modification to the slice element is also visible to the caller, which can be understood as passing a pointer to the underlying array.

Modify the length: As long as the slice does not exceed the limit of the underlying array, its length is variable, just generate a new slice to point to its own variable again.

Length of slice:

len (slice)
The capacity of the slice can be obtained through the built-in function cap, which will give the maximum length that the slice can obtain. The function is:

cap (slice)
If the data exceeds its capacity, the slice will be reassigned. The return value is the resulting slice.

It is very common to append things to slices, so there is a special built-in function append.

In general, if we want to write an append method, the final return value must be returned to the slice. Example:

  func Append(slice, data[]byte) []byte {
    l := len(slice)
    if l + len(data) > cap(slice) {  // 重新分配
        // 为了后面的增长,需分配两份。
        newSlice := make([]byte, (l+len(data))*2)
        // copy 函数是预声明的,且可用于任何切片类型。
        copy(newSlice, slice)
        slice = newSlice
    }
    slice = slice[0:l+len(data)]
    for i, c := range data {
        slice[l+i] = c
    }
    return slice
}

As above, the input parameter is the slice and the inserted element value, and the return value is the slice. Note that the length of the slice will change.
Because although Append can modify the elements of a slice, the slice itself (its runtime data structure contains pointers, length, and capacity) is passed by value.

Two-dimensional slices
To create an equivalent two-dimensional array or slice, you must define an array of arrays, or slices of slices. Example:

type Transform [3][3]float64 // A 3x3 array is actually an array containing multiple arrays.
type LinesOfText [][]byte // A slice containing multiple byte slices.
Each row has its own length:
since the slice length is variable, there may be multiple slices of different lengths inside.

Mapping
is the realization of the map structure in the data structure in Go, which is stored in the form of key: value.

The mapped value can be of various types.
The mapped keys can be integers, floating-point numbers, complex numbers, strings, pointers, interfaces, etc.

The mapped key (or index) can be any type supported by equality operators, such as integers, floating-point numbers, complex numbers, strings, pointers, interfaces (as long as their dynamic types support equality), structures, and arrays. Slices cannot be used as mapping keys because their equality is not yet defined. Like slices, mappings are also reference types.

If the mapping is passed into the function as a parameter, and the content of the mapping is changed, the modification is also visible to the caller.

The mapping can be constructed using general compound literal syntax, and its key-value pairs are separated by commas, a bit like JSON:

var timeZone = map[string]int{ “UTC”: 0 60 60, “EST”: -5 60 60, “CST”: -6 60 60, “MST”: -7 60 60, “PST”: -8 60 60, } Get the value:






offset := timeZone["EST"]
Note: If you try to get a value through a key that does not exist in the map, the zero value corresponding to the type of the item in the map will be returned. For example, if a map contains integers, it will return 0 when looking for a key that does not exist.

Determine whether a value exists:

seconds, ok = timeZone[tz] The
above is the usual "comma ok" method:

If tz exists, seconds will be assigned the appropriate value, and ok will be set to true;-if it does not exist, seconds will be set to zero and ok will be set to false.
If you only need to determine whether there is an item in the mapping and do not care about the actual value, you can use the blank identifier _ to replace the general variable of the value.

_, present := timeZone[tz]
To delete an item in the mapping, you can use the built-in function delete. This operation is safe even if the corresponding key is not in the map.

delete(timeZone, “PDT”)
Print
Go's formatted printing style is similar to C's printf, but it is richer and more versatile. These functions are located in the fmt package, and the first letters of the function names are all uppercase: such as fmt.Printf, fmt.Fprintf, fmt.Sprintf, etc.

Look at the example:

// For the ones ending with f, pass in the formatted string as a parameter, without line
break fmt.Printf("hello, %v \n","zhang3")
fmt.Fprintf(os.Stdout,"hello,% v \n","zhang3")
str := fmt.Sprintf("hello, %v \n","zhang3")

//The following ones will wrap
fmt.Println(str)
// Note the following, spaces will be automatically inserted between the elements
fmt.Fprintln(os.Stdout,"f1","f2","f3")
Sprintf use To construct a string: String functions (Sprintf, etc.) will return a string instead of writing it to the data stream.

Fprint is used to write to various streams: formatted print functions such as fmt.Fprint accept any object that implements the io.Writer interface as the first argument; such as os.Stdout and os.Stderr.

The following is a description of the formatted characters supported by Printf:
– Format: %d
like %d does not accept marks representing symbols or sizes. These attributes will be determined according to the actual type.

var x uint64 = 1<<64-1 // x is an unsigned integer, the following int64(x) is converted to a conforming integer
fmt.Printf("%d %x; %d %x\n", x, x , int64(x), int64(x))
will print

18446744073709551615 ffffffffffffffff; -1 -1
-Format: %v
%v can be understood as the actual value.
It can also print arbitrary values, even arrays, structures, and maps.

fmt.Printf("%v\n", timeZone) // Or just use fmt.Println(timeZone)
which will output

map[CST:-21600 PST:-28800 EST:-18000 UTC:0 MST:-25200]
%+v and %#v
When printing the structure, the format %+v will bring the field name of each field, and The format %#v will bring the type.

type T struct {
a int
b float64
c string
}
t := &T{ 7, -2.35, “abc\tdef” }
fmt.Printf("%v\n", t)
fmt.Printf("%+v\n", t)
fmt.Printf("%#v\n", t)
将打印

&{7 -2.35 abc def} // Please pay attention to the ampersand
&{a:7 b:-2.35 c:abc def} // with the field name
&main.T{a:7, b:-2.35, c :"Abc\tdef"} //With type
-format: %q
When encountering string or []byte values, you can use %q to generate a quoted string; and the format %#q will use backticks as much as possible .

– Format: %x
%x can also be used for strings, byte arrays and integers, and generate a very long hexadecimal string, and the format with spaces (% x) will also insert spaces between bytes .

-Format: %T
It will print the type of a certain value.

fmt.Printf("%T\n", timeZone)
will print

map[string] int
-Customize output for structure diagram
Similar to toString() in java, for the default format of structure diagram custom type, you only need to define a method with String() string signature for the type. For our simple type T, the following operations can be performed.

func (t *T) String() string {
        return fmt.Sprintf("%d/%g/%q", t.a, t.b, t.c)
}
 
fmt.Printf("%v\n", t)
会打印出如下格式:

7/-2.35/"abc\tdef"
-- 任意数量的
Printf 的签名为其最后的实参使用了 ...interface{} 类型,这样格式的后面就能出现任意数量,任意类型的形参了。

func Printf(format string, v ...interface{}) (n int, err error) {
在 Printf 函数的实现中,v 看起来更像是 []interface{} 类型的变量,但如果将它传递到另一个变参函数中,它就像是常规实参列表了。实际上,它直接将其实参传递给 fmt.Sprintln 进行实际的格式化。
// Println 通过 fmt.Println 的方式将日志打印到标准记录器。
func Println(v ...interface{}) {
    std.Output(2, fmt.Sprintln(v...))  // Output 接受形参 (int, string)
}

Pay attention to the writing of ...interface{} and v... above.

Append (description of append function)
The signature of append function is like this:

func append(slice []T, 元素 ...T) []T

The T is a placeholder of any given type. In fact, you cannot write a function whose type T is determined by the caller. This is why append is a built-in function: it needs the support of the compiler.
append will append elements to the end of the slice and return the result. We must return the result, because the underlying array may be changed (note that the length of the array is part of the type).

以下简单的例子

x := []int{1,2,3}
x = append(x, 4, 5, 6)
fmt.Println(x)
将打印

[1 2 3 4 5 6]
将一个切片追加到另一个切片很简单:在调用的地方使用 ...

x := []int{1,2,3}
y := []int{4,5,6}
x = append(x, y...)
fmt.Println(x)

If there is no..., it will fail to compile due to a type error, because y is not of type int. The function of the three dots "..." is a bit like the function of "expanding", that is, the element of the y slice is put here.

The initialization
of huaGo that initializes GO is very powerful. During the initialization process, it can not only build complex structures, but also correctly handle the initialization sequence between different package objects.

Constants
Constants are created at compile time, even if the local variables defined in the function are the same.
Constants can only be numbers, characters (runes), strings, or Boolean values.

Due to compile-time restrictions, the expressions that define them must be constant expressions that can be evaluated by the compiler. For example, 1<<3 is a constant expression.

Enumeration Constants
Enumeration constants are created using the enumerator iota. Since iota can be part of an expression, and expressions can be implicitly repeated, it is easier to construct complex sets of values.

  type ByteSize float64
 
  const (
      // 通过赋予空白标识符来忽略第一个值
      _           = iota // ignore first value by assigning to blank identifier
      KB ByteSize = 1 << (10 * iota)
      MB
      GB
      TB
      PB
      EB
      ZB
      YB
  )

Variables
Variable initialization is similar to constants, but its initial value can also be a general expression that is calculated at runtime.

var (
    home   = os.Getenv("HOME")
    user   = os.Getenv("USER")
    gopath = os.Getenv("GOPATH")
)

init function
Each source file can set some necessary states by defining its own parameterless init function. The format is:

func init() { 
    ...
 }

The end of the init method execution means that the initialization is over: init will be called only after all variable declarations in the package have been evaluated by their initializers, and those init only after all imported packages have been initialized Will be evaluated.

The init function is also often used to check or correct the state of the program before it actually starts executing. Example:

  func init() {
    if user == "" {
        log.Fatal("$USER not set")
    }
    if home == "" {
        home = "/home/" + user
    }
    if gopath == "" {
        gopath = home + "/go"
    }
    // gopath 可通过命令行中的 --gopath 标记覆盖掉。
    flag.StringVar(&gopath, "gopath", gopath, "override default GOPATH")
  }

So far

As above, I supplement the project structure of go language development:

cd  $GOPATH/src/github.com/goincation/code/chapter2

- sample
	- data
		data.json		-- 包含一组数据源
	- matchers
		rss.go			-- 搜索rss源的匹配器
	- search 
		default.go	-- 搜索数据用的默认匹配器
		feed.go		-- 用于读取json数据文件
		match.go		-- 用于支持不同匹配器的接口
		search.go	-- 执行搜索的主控制逻辑
	main.go 			-- 程序的入口
		

This article is reproduced at
https://blog.csdn.net/vir56k/article/details/105113310

Therefore, for beginners to learn go language, an official quick learning document is recommended:

https://tour.golang.org/welcome/2
If you have a favorite, you can check it out. Personally, I feel that this is a senior boss, and the content is also very rich, and I have put forward a lot of valuable opinions for beginners. At the same time, I also hope that colleagues and bigwigs in the IT industry can give a lot of advice.

Guess you like

Origin blog.csdn.net/MatChen/article/details/111033580