Go语言利用goroutine实现递归

Recursion And Tail Calls In Go这篇文章讲了用goroutine实现函数递归调用:这样做可以避免过多函数调用引起的堆栈空间的不断增大,感觉很巧妙。以下是例子代码:

package main
import "fmt"

func recursiveCall(product int, num int, ch chan int)  {
    product += num

    if num == 1 {
        ch <- product
        return
    }

    go recursiveCall(product, num - 1, ch)
}

func main()  {
    ch := make(chan int)
    go recursiveCall(0, 4, ch)
    product := <-ch
    fmt.Printf("Product is %d\n", product)
}  

执行结果如下:

Product is 10

 

Go语言的string和byte slice之间的转换

以下摘自The Go Programming Language

A string contains an array of bytes that, once created, is immutable. By contrast, the elements of a byte slice can be freely modified.

Strings can be converted to byte slices and back again:
s := “abc”
b := []byte(s)
s2 := string(b)

Conceptually, the []byte(s) conversion allocates a new byte array holding a copy of the bytes of s, and yields a slice that references the entirety of that array. An optimizing compiler may be able to avoid the allocation and copying in some cases, but in general copying is required to ensure that the bytes of s remain unchanged even if those of b are subsequently modified. The conversion from byte slice back to string with string(b) also makes a copy, to ensure immutability of the resulting string s2.

由于Go语言中字符串是不可修改的,因此如果要修改其中内容,就要把其转化成byte slice。此外,byte slice也可以转化成字符串。这两种转化都需要分配一块新的内存,然后进行内容拷贝。

 

Go语言的浮点数

以下摘自The Go Programming Language

A float32 provides approximately six decimal digits of precision, whereas a float64 provides about 15 digits; float64 should be preferred for most purposes because float32 computations accumulate error rapidly unless one is quite careful, and the smallest positive integer that cannot be exactly represented as a float32 is not large:

var f float32 = 16777216 // 1 << 24
fmt.Println(f == f+1) // “true”!

因为float32计算时累计错误很快,所以大多数时候推荐使用float64

The math package has functions for creating and detecting the special values defined by IEEE 754: the positive and negative infinities, which represent numbers of excessive magnitude and the result of division by zero; and NaN (“not a number”), the result of such mathematically dubious operations as 0/0 or Sqrt(-1).

var z float64
fmt.Println(z, -z, 1/z, -1/z, z/z) // “0 -0 +Inf -Inf NaN”

要注意正负0,正负无穷和NaN

The function math.IsNaN tests whether its argument is a not-a-number value, and math.NaN returns such a value. It’s tempting to use NaN as a sentinel value in a numeric computation, but testing whether a specific computational result is equal to NaN is fraught with peril because any comparison with NaN always yields false except !=, which is always the negation of ==:

nan := math.NaN()
fmt.Println(nan != nan, nan == nan, nan < nan, nan > nan) // “true false false false”

要留心数值和NaN比较时的返回值。

 

Go语言中使用fmt.Printf的小技巧

以下摘自The Go Programming Language

When printing numbers using the fmt package, we can control the radix and format with the %d, %o, and %x verbs, as shown in this example:

o := 0666
fmt.Printf(“%d %[1]o %#[1]o\n”, o) // “438 666 0666”
x := int64(0xdeadbeef)
fmt.Printf(“%d %[1]x %#[1]x %#[1]X\n”, x)
// Output:
// 3735928559 deadbeef 0xdeadbeef 0XDEADBEEF

Note the use of two fmt tricks. Usually a Printf format string containing multiple % verbs would require the same number of extra operands, but the [1] “adverbs” after % tell Printf to use the first operand over and over again. Second, the # adverb for %o or %x or %X tells Printf to emit a 0 or 0x or 0X prefix respectively.

Rune literals are written as a character within single quotes. The simplest example is an ASCII character like ‘a’, but it’s possible to write any Unicode code point either directly or with numeric escapes, as we will see shortly.

Runes are printed with %c, or with %q if quoting is desired: ascii := ‘a’
unicode := ‘ ‘
newline := ‘\n’
fmt.Printf(“%d %[1]c %[1]q\n”, ascii) // “97 a ‘a'”
fmt.Printf(“%d %[1]c %[1]q\n”, unicode) // “22269 ‘ ‘”
fmt.Printf(“%d %[1]q\n”, newline) // “10 ‘\n'”

总结一下,“%[1]”还是格式化第一个参数;“%#”会打印出数值的前缀;而“%q”会加上引号。举例如下:

package main

import "fmt"

func main() {
    var c rune = '楠'
    fmt.Printf("%c %[1]d %#[1]x %[1]q", c)
}

执行结果:

楠 26976 0x6960 '楠'

 

Go语言的bit clear操作

以下摘自The Go Programming Language

The &^ operator is bit clear (AND NOT): in the expression z = x &^ y, each bit of z is 0 if the corresponding bit of y is 1; otherwise it equals the corresponding bit of x.

z = x &^ y运算相当于先把y取反(针对y的每个bit0变成11变成0),然后再和x进行&运算。参考下例:

package main
import "fmt"

func main(){
    var x uint8 = 1
    var y uint8 = 1 << 2

    fmt.Printf("%08b\n", x &^ y);

}  

执行结果如下:

00000001

 

Go语言的比较运算符

以下摘自The Go Programming Language

Two integers of the same type may be compared using the binary comparison operators below; the type of a comparison expression is a boolean.

== equal to
!= not equal to
< less than
<= less than or equal to
greater than
= greater than or equal to

In fact, all values of basic type—booleans, numbers, and strings—are comparable, meaning that two values of the same type may be compared using the == and != operators. Furthermore, integers, floating-point numbers, and strings are ordered by the comparison operators. The values of many other types are not comparable, and no other types are ordered.

 

Go语言的int,uint和uintptr类型

以下摘自The Go Programming Language

There are also two types called just int and uint that are the natural or most efficient size for signed and unsigned integers on a particular platform; int is by far the most widely used numeric type. Both these types have the same size, either 32 or 64 bits, but one must not make assumptions about which; different compilers may make different choices even on identical hardware.

Finally, there is an unsigned integer type uintptr, whose width is not specified but is sufficient to hold all the bits of a pointer value. The uintptr type is used only for low-level programming, such as at the boundary of a Go program with a C library or an operating system.

Regardless of their size, int, uint, and uintptr are different types from their explicitly sized siblings. Thus int is not the same type as int32, even if the natural size of integers is 32 bits, and an explicit conversion is required to use an int value where an int32 is needed, and vice versa.

另外关于intuint类型的使用:

Although Go provides unsigned numbers and arithmetic, we tend to use the signed int form even for quantities that can’t be negative, such as the length of an array, though uint might seem a more obvious choice. Indeed, the built-in len function returns a signed int, as in this loop which announces prize medals in reverse order:

medals := []string{“gold”, “silver”, “bronze”}
for i := len(medals) – 1; i >= 0; i– {
fmt.Println(medals[i]) // “bronze”, “silver”, “gold”
}

The alternative would be calamitous. If len returned an unsigned number, then i too would be a uint, and the condition i >= 0 would always be true by definition. After the third iteration, in which i == 0, the i– statement would cause i to become not −1, but the maximum uint value, and the evaluation of medals[i] would fail at run time, or panic, by attempting to access an element outside the bounds of the slice.

For this reason, unsigned numbers tend to be used only when their bitwise operators or peculiar arithmetic operators are required, as when implementing bit sets, parsing binary file formats, or for hashing and cryptography. They are typically not used for merely non-negative quantities.

多数时候,在Golang中应该使用int类型,即使有些变量不可能为负数。否则可能会出现严重的问题(就像上面的例子)。而uint仅仅是用在位运算,hash运算等等少数情况中。

 

Go语言的数据类型

以下摘自The Go Programming Language

Go’s types fall into four categories: basic types, aggregate types, reference types, and interface types. Basic types, include numbers, strings, and booleans. Aggregate types—arrays and structs —form more complicated data types by combining values of several simpler ones. Reference types are a diverse group that includes pointers, slices, maps, functions, and channels, but what they have in common is that they all refer to program variables or state indirectly, so that the effect of an operation applied to one reference is observed by all copies of that reference.

 

Go语言中变量的scope

以下摘自The Go Programming Language

A syntactic block is a sequence of statements enclosed in braces like those that surround the body of a function or loop. A name declared inside a syntactic block is not visible outside that block. The block encloses its declarations and determines their scope. We can generalize this notion of blocks to include other groupings of declarations that are not explicitly surrounded by braces in the source code; we’ll call them all lexical blocks. There is a lexical block for the entire source code, called the universe block; for each package; for each file; for each for, if, and switch statement; for each case in a switch or select statement; and, of course, for each explicit syntactic block.

A declaration’s lexical block determines its scope, which may be large or small. The declarations of built-in types, functions, and constants like int, len, and true are in the universe block and can be referred to throughout the entire program. Declarations outside any function, that is, at package level, can be referred to from any file in the same package. Imported packages, are declared at the file level, so they can be referred to from the same file, but not from another file in the same package without another import. Many declarations, like that of the variables in the function, are local, so they can be referred to only from within the same function or perhaps just a part of it.

The scope of a control-flow label, as used by break, continue, and goto statements, is the entire enclosing function.

需要注意universe block的定义:Go语言的built-in类型,函数和常量的scope都属于universe block