[GO] About reference type

Premise

What is a reference type?

It refers to and holds the location of the data, not including itself. It is a data structure that is difficult to express in words.

Each Go has three reference types defined: ** slice **, ** map **, and ** channel **.

What is the built-in function make?

A built-in function that can generate three reference types for slice map channels. Since the pattern differs depending on the type, each understanding is required.

len and cap

These two built-in functions can be used effectively for three reference types, so I will keep them as prerequisite knowledge.

・ ** len ** You can check the number of elements of each type. ・ ** cap ** You can check the capacity of each type.

slice

What is a slice

A data structure that represents something like a ** variable length array **.

As mentioned above, you can generate a type with the built-in function make.

a := make([]int, 5)
a[0] = 1
fmt.Println(a)  //[1 0 0 0 0]

An int type slice with a capacity of 5 is generated, and an arbitrary index value is specified. The generation method and behavior are very similar to the array type, but unlike the array type, it is affected by one process when exchanging calls between functions.

Slice literal

Slices have literals that you can generate without using make.

a := []int{ 0, 1, 2, 3, 4 }
fmt.Println(a)  //[0 1 2 3 4]

Simple slice type and complete slice type

There is a function called ** simple slice type ** and ** complete slice type ** that takes parameters based on existing slices and creates new slices.

First, the simple slice expression takes a range of parameters related to the index of the slice, extracts a part of the slice represented by that parameter, and creates a new slice.

a := []int{ 0, 1, 2, 3, 4 }
fmt.Println(a[0:4])  //[0 1 2 3]

It is necessary to understand the description method because there are variations depending on the parameters to be taken and so on.

Next, unlike the simple slice type, the complete slice type can take three parameters: ** low **: ** high **: ** max **.

a := []int{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }

b := a[2:6:8]
fmt.Println(len(b))  //4
fmt.Println(cap(b))  //6

The capacity of a normal array type or slice is determined as ** number of elements --low value **, but if you take a parameter in the form of a complete slice type, ** max --low value ** There is a difference that the capacity is determined by.

You can also refer to the string in either slice expression. However, for multi-byte characters that cannot be expressed in ASCII, it is necessary to specify the index range in the byte string because [] byte is the unit.

append and copy

Let's see what functions the built-in functions ** append ** and ** copy ** have. -** append ** This is a function that allows you to add slice elements. -** copy ** A function that allows you to copy slice elements.

append.go


a := make([]int, 0, 0)
fmt.Println(cap(a))
a = append(a, []int{ 1, 2, 3 }...)
fmt.Println(a)  //[1, 2, 3]
fmt.Println(cap(a))  //4
a = append(a, []int{ 4, 5, 6 }...)
fmt.Println(a)  //[1, 2, 3, 4, 5, 6]
fmt.Println(cap(a))  //8

An element is added to the end of a slice with 0 elements and 0 capacity, and fluctuations in capacity are confirmed. From this program, slices have the property of being automatically expanded when the capacity is exceeded and elements are added. At first glance, it looks like it's doubling, but it's important to note that it's not doubling because it depends on the fighting value and the runtime.

In the above case, since the metadata is taken as the return value of append, the append is reflected and the value can be output with cap, but even if append is performed indirectly to the metadata, it is not reflected. It has a nature.

append.go


func test(a []int) {
   a = append(a, 3)
}

func main() {
   b := make([]int, 1, 2)
   test(b)
   fmt.Println(cap(b))  //2
}

copy.go


a := []int{ 1, 2, 3, 4, 5 }
b := []int{ 6, 7, 8 }
fmt.Println(copy(a, b), a)  //3 [6 7 8 4 5]

The number of copied elements and the copied elements are output. In this way, the element is overwritten from the beginning of the copy target. Even if the number of elements to be copied exceeds the number of elements to be copied, the number of elements to be copied will be copied.

You can also copy a string type, but you need to consider that it will be copied byte by byte, as described above for slice expressions.

map

What is a map?

A data structure that represents something like an associative array **.

Maps can also be generated with the built-in function make, following the example.

a := make(map[int]string)

a[1] = "test1"
a[2] = "test2"
fmt.Println(a)  //map[1:test1 2:test2]

The int type key value and the string type element type are defined, and the key value and element according to the type are assigned and output. Also, by taking a hint to allocate a memory area in the second argument for the number of elements, it can be usefully used when the map is large.

Map literal

Maps also have literals to generate without using make.

a := map[int]string{ 1: "test1", 2: "test2" }

fmt.Println(a)

Elements can be written on multiple lines for readability, but due to Go's characteristics, the last value must end with a comma to mean continuation.

You can also generate slices in the map.

a := map[int][]int{
   1: []int{1}
   2: []int{2}
}
fmt.Println(a)  //map[1:[1] 2:[2]]

By the way, since [] int in the element specification can be omitted, I think that there is no particular merit to write [] int.

How to refer to an element

By assigning a variable like ** a [1] **, you can refer to the corresponding key value element, but in Go, the basic type does not have the state of ** nil **. Therefore, if the initial value of the type is set in the element, it will be processed while retaining it. However, you can work around it by using an idiom that assigns to two variables.

a := map[int]string{ 1: "test1", 2: "test2" }

if a, ok := a[1]; ok {
   fmt.Printf("%v, %v\n", a, ok)  //test1, true
} else {
   fmt.Printf("%v, %v\n", a, ok)  //※  , false

In the above program, the second variable evaluates if there is an element corresponding to the key and is assigned as a bool type. Branch processing is performed according to the conditional expression, and it is output. Of course, if the assigned key value does not exist in the map, else will be executed and an empty string and false will be output.

delete A built-in function for removing elements from the map.

a := map[int]string{ 1: "test1", 2: "test2" }

fmt.Println(len(a))  //2

delete(a, 2)
fmt.Println(len(a))  //1

** delete ** The change in the number of elements before and after is compared using len. Before delete, the result is as many as the number of elements in the map, but by using delete, you can see that the number of elements is reduced. Since delete can be deleted by specifying an arbitrary key value, it means that "test2" whose key value is 2 is deleted in the above program.

By the way, although there is practical capacity in the map, cap cannot be used due to its nature.

channel

What is a channel?

[Previous article (control syntax)](https://qiita.com/noa-1129/items/819f4a8d81dcdd0c19cd#%E3%82%B4%E3%83%AB%E3%83%BC%E3%83% It is a data structure that is responsible for sending and receiving data between goroutines, summarized in 81% E3% 83% B3).

There is a type of channel in a channel.

var a chan int

As we will see later, you can specify subtypes such as ** receive-only channels ** and ** send-only channels **. If not specified, both transmission and reception are possible.

When generated by make, it will be as follows.

a := make(chan int, 5)

You can specify the amount of space that can temporarily hold data called ** buffer size **.

Receive and send

Use channels to send and receive data.

a := make(chan int, 5)

a <- 1  //Send
a <- 2  //Send
fmt.Println(cap(a))  //5
fmt.Println(len(a))  //2

b := <-a  //Receive
fmt.Println(b)  //  1

fmt.Println(cap(a))  //5
fmt.Println(len(a))  //1

In the above program, a channel with a buffer size of 5 is generated, and the data in the channel is compared when the channel is transmitted and received. First, looking at the capacity and the number of elements when the values of 1 and 2 are sent to channel a, the capacity is the buffer size, so the value is 5, and since two values are sent, the number of elements is 2. Become. Therefore, we receive one value in the variable b. That way, the capacity doesn't change, but the number of elements is reduced by one. Also, since 1 is output from the variable b, it can be seen that data is being sent and received in the relationship of ** first in, first out **.

Goroutine

Now, let's send and receive data between goroutines.

func test(a <- chan int) {
   for {
      b, ok := <-a
      if ok == false {
         break
      }
      fmt.Println(b)  //1
   }                  //2
}                     //3
                      //4

func main() {
   a := make(chan int)
   go test(a)

   b := 1
   for b < 5 {
      a <- b
      b++
   }
   close(a)
}

In the above program, the function test for receiving the channel that takes an arbitrary argument is defined, and when channel a is closed (returns a false value), branch processing that interrupts the loop during output is performed. I am. Then, the function main shares the goroutine that creates the channel a used by the function test and continues to send the variable b that increments until the value is less than 5, and the function test is the value received from the channel. Continues to output. Since it is closed when the data transmission is completed, when the value is 5 or more, the second argument ok returns false and the processing ends.

select statement

If there are two or more channels that represent the reception of a variable, the goroutine will stop if the previous channel cannot receive it. The control syntax select can solve this problem.

a := make(chan int, 1)
b := make(chan int, 1)
b <- 5

select {
case <- a:
   fmt.Println("x is done")
case <- b:
   fmt.Println("y is done")  //output
default:
   fmt.Println("?")
}

The variable a has a channel generated, but cannot be received because it has not been sent. Normally, the goroutine would stop at this point, but because it uses the select syntax, the process continues. Also, if the processing of multiple cases is successful, it will be executed randomly.

Finally

This time I summarized the reference type! !! The reference type has a lot of content, and all the idioms that appear frequently in Go programs, so I want to understand it well.

References

Author Aiga Matsuo [Shoeisha Starting Go Language](https://www.shoeisha.co.jp/search?search=%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%86% E3% 82% A3% E3% 83% B3% E3% 82% B0Go% E8% A8% 80% E8% AA% 9E)

Recommended Posts

About reference type
About python reference
About type comparison by PHP
Python # About reference and copy
About the basic type of Go
[Multivariate analysis] About quantification type I (001)
About LangID
About CAGR
About virtiofs
Shell type
About Permission
About sklearn.preprocessing.Imputer
About gunicorn
About requirements.txt
About locale
About permissions
About Opencv ②
About axis = 0, axis = 1
About Opencv ③
About import
A Java programmer studied Python. (About type)
Dictionary type 2
Dictionary type 1
About numpy
About pip
About Linux
About numpy.newaxis
About endian
About Linux
About import
About Opencv ①
About Linux
About Linux
About Linux ①
About cv2.imread
About _ and __
About wxPython