This is a creation in Article, where the information may have evolved or changed.
All of the following views are personal humble opinion, with different suggestions or additions to the Welcome emialaboutme
Original article Address
Questions about the For statement
Specification for a For statement
Internal implementation of the for statement-array
Answer questions
Questions about the For statement
We all know that in Golang, the loop statement is only for this one, write a loop in the code generally need to use for (of course, you can use Goto is also possible), although the Golang for statement is very convenient, but a lot of beginners like the For statement has a lot of questions, such as:
- How many expression formats are there in a for statement?
- What is the temporary variable in the for statement? (Why sometimes all values are equal to the last element after the assignment is traversed)
- What are the supported data types after range?
- Range String Type Why is the Rune type available?
- What happens when you add or delete data while traversing slice?
- What happens when you add or delete data while traversing a map?
In fact, many of the questions here can be seen Golang programming language specifications, interested students can see their own, and then according to their own understanding to answer these questions.
Specification for a For statement
The function of the For statement is used to specify a repeating block of statements with three expressions in the For statement:
The official norm:ForStmt = "for" [ Condition | ForClause | RangeClause ] Block .
- Condition = Expression.
- Forclause = [initstmt] ";" [Condition] “;” [Poststmt].
- Rangeclause = [expressionlist "=" | Identifierlist ": ="] "range" Expression.
Single condition judgment
Form:
for a < b { f(doThing)}// or 省略表达式,等价于truefor { // for true { f(doThing)}
In this format, only a single logical expression, the value of the logical expression is true, resumes execution, or the loop is stopped.
Two semicolon in a for statement
Form:
for i:=0; i < 10; i++ { f(doThing)}// orfor i:=0; i < 10; { i++ f(doThing)}// or var i intfor ; i < 10; { i++ f(doThing)}
In this format, the tone is divided into 3 expressions by two semicolons, the first is initialized ( 只会在第一次条件表达式之计算一次
), the second expression is a conditional expression, and the third expression is generally self-increment or decrement, but the expression can be any syntax-compliant expression. And with these three expressions, only the second expression is necessary, and the other expression can be empty.
Statements that are combined for and range
Form:
for k,v := range []int{1,2,3} { f(doThing)}// or for k := range []int{1,2,3} { f(doThing)}// orfor range []int{1,2,3} { f(doThing)}
Iterating over the data with range is the most common for statement, and the expression on the right of range is called the 范围表达式
array, array pointer, slice, string, map, and channel. Because you want to assign a value, the left-hand operand (that is, the iteration variable) must be addressable, or it is an expression of the map subscript. If the iteration variable is a channel, then only one iteration variable is allowed, except that the iteration variable can have one or two.
A range expression evaluates only once before starting the loop, with one exception: if the range expression is an array or pointer to an array, at most one iteration variable exists, only the length of the range expression is evaluated, and if the length is constant, the range expression itself will not be evaluated.
For each iteration, the left function call evaluates. For each iteration, if the corresponding iteration variable exists, the iteration value is generated as follows:
Range expression 1st value 2nd valuearray or slice a [n]E, *[n]E, or []E index i int a[i] Estring s string type index i int see below runemap m map[K]V key k K m[k] Vchannel c chan E, <-chan E element e E
- For arrays, array pointers, or shard value A, the subscript iteration value is generated in ascending order, starting with 0. In a special scenario where only one iteration parameter exists, the range loop generates an iteration value of 0 to Len (a) instead of an index to an array or to a shard. For a nil Shard, the number of iterations is 0.
- For a string type, the range clause iterates through each Unicode code point in the string, starting with subscript 0. In successive iterations, the subscript value is the subscript for the first byte of the next Utf-8 code point, and the second value type is Rune, which is the corresponding code point. If an iteration encounters an illegal Unicode sequence, the second value is 0xFFFD, which is the substitution character for Unicode, and then the next iteration will only advance one byte.
- The order of iterations in map is unspecified, and there is no guarantee that two iterations will be the same. If the map element is deleted during the iteration, the corresponding iteration value will not be generated again. If the map element is inserted in the iteration, the element may or may be skipped during the iteration, but the iteration value of each element appears at most once. If the map is nil, then the number of iterations is 0.
- For pipelines, the iteration value is the next value in the Send to pipeline, unless the pipeline is closed. If the pipe is nil, the range expression is always blocked.
The iteration value is assigned to the corresponding iteration variable, as if it were an assignment statement.
Iteration variables can be declared with a short variable (: =). In this case, their type is set to the type of the corresponding iteration value, their fields are to the end of the for statement, and they are reused in each iteration. If the iteration variables are declared outside the for statement, their values are the values of the last iteration after they are executed.
var testdata *struct {a *[7]int}for i, _ := range testdata.a {// testdata.a is never evaluated; len(testdata.a) is constant// i ranges from 0 to 6f(i)}var a [10]stringfor i, s := range a {// type of i is int// type of s is string// s == a[i]g(i, s)}var key stringvar val interface {} // value type of m is assignable to valm := map[string]int{"mon":0, "tue":1, "wed":2, "thu":3, "fri":4, "sat":5, "sun":6}for key, val = range m {h(key, val)}// key == last map key encountered in iteration// val == map[key]var ch chan Work = producer()for w := range ch {doWork(w)}// empty a channelfor range ch {}
Internal implementation of the for statement-array
Golang for statements, for different formats will be compiled by the compiler into a different form, if you want to see Golang compiler and related data structure of the source code, data structure is good, but the compiler is written in C + +, I C + + is a weak chicken, here only to say the array inside the implementation.
// The loop we generate:// len_temp := len(range)// range_temp := range// for index_temp = 0; index_temp < len_temp; index_temp++ {// value_temp = range_temp[index_temp]// index = index_temp// value = value_temp// original body// }// 例如代码: array := [2]int{1,2}for k,v := range array { f(k,v)}// 会被编译成: len_temp := len(array)range_temp := arrayfor index_temp = 0; index_temp < len_temp; index_temp++ { value_temp = range_temp[index_temp] k = index_temp v = value_temp f(k,v)}
So like traversing an array, the last generated code is much like a traversal in C, and there are two temporary variables that are index_temp
value_temp
reused throughout the traversal. So it will cause a problem with the beginning of question 2 (the detailed answer will be behind).
Answer questions
How many expression formats are there in a for statement?
This question should be very simple, the above specification has the answer, a total of 3 kinds:
Condition = Expression .ForClause = [ InitStmt ] ";" [ Condition ] ";" [ PostStmt ] .RangeClause = [ ExpressionList "=" | IdentifierList ":=" ] "range" Expression .
What is the temporary variable in the for statement? (Why sometimes all values are equal to the last element after the assignment is traversed)
Let's look at this example:
var a = make([]*int, 3)for k, v := range []int{1, 2, 3} { a[k] = &v}for i := range a { fmt.Println(*a[i])}// result: // 3 // 3 // 3
By the internal implementation of the for statement-array can know that even the short declaration of the variable, in the For loop is also reused, here v
is always the same zero variable, so the &v
resulting address has been the same, if you do not believe, you can print the address, And the last variable in the address is equal to the last loop, so the result is 3.
What are the supported data types after range?
Total 5, array, array pointer, slice, string, map and channel, respectively
-
Range String Type How do I get the rune type?
This problem also has a solution in the for specification, for string types, in successive iterations, the subscript value is the subscript of the first byte of the next Utf-8 code point, and the second value type is rune. If an iteration encounters an illegal Unicode sequence, the second value is 0xFFFD, which is the substitution character for Unicode, and then the next iteration will only advance one byte.
In fact, after reading this sentence, I do not understand, of course, this sentence tells us that the second value type that is traversed by string is rune, but why is the rune type, not a string or other type? After watching Rob Pike write blogstrings, Bytes, runes and characters in go to understand the point, first need to know Rune
is the alias of int32
, And the string literal in the go language always holds a valid UTF-8 sequence. The UTF-8 uses 4 bytes to represent the Unicode character set. So the Go designer uses Rune to represent the encoding of a single character, which can be done to accommodate the represented Unicode character. For example:
s: = ' Chinese ab ' FMT. Println ("Len of S:", Len (s)) for index, Runevalue: = Range S {fmt. Printf ("% #U starts at byte position%d\n", Runevalue, index)}//result//len of s:8//u+6c49 ' Han ' starts at byte position 0//u+8bed ' language ' starts at byte position 3//u+0061 ' a ' starts at byte position 6//u+0062 ' B ' starts at byte position 7
According to the results, the length of S is 8 bytes, a man occupies 3 bytes, an English letter occupies a byte, and the program Go program is how to know that the man accounted for 3 bytes, and the English letter occupies a byte, you need to know the concept of Utf-8 code point, here do not delve into the , it is OK to know that go is based on the Utf-8 code point to know how many bytes the character occupies.
- What happens when the
-
traverses slice to add or delete data? The
is implemented by the internal implementation of the for statement-array can know that the length of the obtained slice is only executed once outside the loop, which determines the number of traversal times, regardless of how you change in the loop. However, the evaluation of an index is evaluated in each iteration, and if an element is changed and the element has not been traversed, the resulting traversal value is changed. Deleting an element is also a case of changing the element. The
Adds elements to the slice, changes the elements contained in the slice, but does not change the number of iterations.
a2: = []int{0, 1, 2, 3, 4}for i, V: = Range A2 {fmt. Println (i, v) if i = = 0 {a2 = append (A2, 6)}}//result//0 0//1 1//2 2//3 3//4 4
in slice Deleting an element can delete the element, but does not change the number of traversal times.
//Delete only the element 1, without changing the slice length a2: = []int{0, 1, 2, 3, 4}for i, V: = Range A2 {fmt. Println (i, v) if i = = 0 {copy (a2[1:], a2[2:])}}//result//0 0//1 2//2 3//3 4//4 4//Delete the element 1 and change the slice length a 2: = []int{0, 1, 2, 3, 4}for i, V: = Range A2 {fmt. Println (i, v) if i = = 0 {copy (a2[1:], A2[2:]) a2 = A2[:len (A2)-2]//The Len of A2 is set to 3, but does not affect temporary slice-range_temp }}//result//0 0//1 2//2 3//3 4//4 4
What happens when you add or delete data while traversing a map?
The specification also has the answer, the map element is deleted during the iteration, then the corresponding iteration value will not be generated again. If the map element is inserted in the iteration, the element may be generated during the iteration or may be skipped.
Delete an element in a traverse
m := map[int]int{1: 1, 2: 2, 3: 3, 4: 4, 5: 5}del := falsefor k, v := range m { fmt.Println(k, v) if !del { delete(m, 2) del = true }}// result// 4 4// 5 5// 1 1// 3 3
Add elements to the traversal and perform several more times to see the results
m := map[int]int{1: 1, 2: 2, 3: 3, 4: 4, 5: 5}add := falsefor k, v := range m { fmt.Println(k, v) if !add { m[6] = 6 m[7] = 7 add = true }}// result1// 1 1// 2 2// 3 3// 4 4// 5 5// 6 6// result2// 1 1// 2 2// 3 3// 4 4// 5 5// 6 6// 7 7
Deleting an element in a map traversal will delete the element and affect the number of traversal times, and the addition of elements to the traversal can be an uncontrolled occurrence, sometimes traversing to new elements, sometimes not. Specific reasons for the next analysis.
Reference
Https://golang.org/ref/spec#For_statements
Https://github.com/golang/go/wiki/Range
Https://blog.golang.org/strings