Go学习笔记
练习 6.1 mult_returnval.go
编写一个函数,接收两个整数,然后返回它们的和、积与差。编写两个版本,一个是非命名返回值,一个是命名返回值。
package main
import (
"fmt";
//"strings"
)
func fmm(a ,b int)(int, int ,int ){
return a+b,a*b,a-b
}
func mm(a ,b int)(sum,ji,minus int){
sum=a+b
ji=a*b
minus=a-b
return
}
func main() {
a,b,c:=fmm(1,2)
fmt.Print(a,b,c,"\n")
a,b,c=mm(1,2)
fmt.Print(a,b,c,"\n")
}
练习 6.2 error_returnval.go
编写一个名字为 MySqrt() 的函数,计算一个 float64 类型浮点数的平方根,如果参数是一个负数的话将返回一个错误。编写两个版本,一个是非命名返回值,一个是命名返回值。
package main
import (
"fmt";
//"strings"
)
func abs(a float64 )float64{
if a>0{
return a
}else{
return -a
}
}
func MySqrt_fmm(a float64)(float64,bool){
if(a<0){
return 0,false
}else{
var start float64 =0
var end float64 =a
var mid float64 =(start+end)/2
for (abs(mid*mid-a))>0.001{
if mid*mid>a{
end=mid
mid=(start+end)/2
}else{
start=mid
mid=(start+end)/2
}
}
return mid,true
}
}
func MySqrt_mm(a float64)(mid float64, ok bool){
if(a<0){
mid =0
ok=false
return mid,ok
}else{
var start float64 =0
var end float64 =a
mid =(start+end)/2
for (abs(mid*mid-a))>0.001{
if mid*mid>a{
end=mid
mid=(start+end)/2
}else{
start=mid
mid=(start+end)/2
}
}
ok=true
return mid,ok
}
}
func main() {
var shu float64=5.0
a,b:=MySqrt_fmm(shu)
if b{
fmt.Print(a,b,"\n")
}
c,d:=MySqrt_fmm(shu)
if d{
fmt.Print(c,d,"\n")
}
}
传递变长参数
如果函数的最后一个参数是采用 …type 的形式,那么这个函数就可以处理一个变长的参数,这个长度可以为 0,这样的函数称为变参函数。
func myFunc(a, b, arg ...int) {}
示例函数和调用:
func Greeting(prefix string, who ...string)
Greeting("hello:", "Joe", "Anna", "Eileen")
在 Greeting() 函数中,变量 who 的值为 []string{“Joe”, “Anna”, “Eileen”}。
练习 6.3 varargs.go
写一个函数,该函数接受一个变长参数并对每个元素进行换行打印。
package main
import (
"fmt"
)
func print(juzi ...string) (ok bool) {
if len(juzi) == 0 {
ok = false
} else {
for i := 0; i < len(juzi); i++ {
fmt.Printf(juzi[i], "\n")
}
ok = true
}
return
}
func main() {
print("fdfs", "adasda", "adasdasd", "dadasdad")
}
(等学到切片了用切片当参数试一下)
defer
关键字 defer 允许我们推迟到函数返回之前(或任意位置执行 return 语句之后)一刻才执行某个语句或函数(为什么要在返回之后才执行这些语句?因为 return 语句同样可以包含一些操作,而不是单纯地返回某个值)。
当有多个 defer 行为被注册时,它们会以逆序执行(类似栈,即后进先出):
例子1:
func f() {
for i := 0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
}
上面的代码将会输出:4 3 2 1 0。
例子2:
package main
import "fmt"
func trace(s string) { fmt.Println("entering:", s) }
func untrace(s string) { fmt.Println("leaving:", s) }
func a() {
trace("a")
defer untrace("a")
fmt.Println("in a")
}
func b() {
trace("b")
defer untrace("b")
fmt.Println("in b")
a()
}
func main() {
b()
}
entering: b
in b
entering: a
in a
leaving: a
leaving: b
内置函数
递归函数
练习 6.4 fibonacci2.go
重写本节中生成斐波那契数列的程序并返回两个命名返回值(详见第 6.2 节),即数列中的位置和对应的值,例如 5 与 4,89 与 10。
package main
import (
"fmt"
)
func fibonacci2(vua_1, idx_1, vua_2, idx_2 int) (vua_r, idx_r int) {
if idx_2 == 2 {
idx_r = idx_2 + 1
vua_r = vua_2 + vua_1
fmt.Print(idx_1, ":", vua_1, "\n")
fmt.Print(idx_2, ":", vua_2, "\n")
fibonacci2(vua_2, idx_2, vua_r, idx_r)
return
} else if idx_2 < 10 {
idx_r = idx_2 + 1
vua_r = vua_2 + vua_1
fmt.Print(idx_2, ":", vua_2, "\n")
fibonacci2(vua_2, idx_2, vua_r, idx_r)
return
} else {
return
}
}
func main() {
fibonacci2(1, 1, 1, 2)
}
练习 6.5 10to1_recursive.go
使用递归函数从 10 打印到 1。
package main
import "fmt"
func pri1210(i int) {
if i <= 10 {
pri1210(i + 1)
fmt.Print(i)
return
}
}
func main() {
pri1210(1)
}
练习 6.6 factorial.go
实现一个输出前 30 个整数的阶乘的程序。
n 的阶乘定义为:n! = n * (n-1)!, 0! = 1,因此它非常适合使用递归函数来实现。
然后,使用命名返回值来实现这个程序的第二个版本。
特别注意的是:使用 int 类型最多只能计算到 12 的阶乘,因为一般情况下 int 类型的大小为 32 位,继续计算会导致溢出错误。那么,如何才能解决这个问题呢?
package main
import (
"fmt"
)
func jiecheng(i int64, idx int64) {
if idx <= 30 {
fmt.Print(idx, ":", i, "\n")
idx = idx + 1
i = i * idx
jiecheng(i, idx)
return
}
}
func main() {
jiecheng(1, 1)
}
(学到big包尝试一下目前只能求的前20个的阶乘)
将函数作为参数
练习 6.7 strings_map.go
包 strings 中的 Map() 函数和 strings.IndexFunc() 一样都是非常好的使用例子。请学习它的源代码并基于该函数书写一个程序,要求将指定文本内的所有非 ASCII 字符替换成问号 ‘?’ 或空格 ’ '。您需要怎么做才能删除这些字符呢?
package main
import (
"fmt"
)
func pan(rune1 rune) bool {
if rune1 <= 255 && rune1 >= 0 {
return true
} else {
return false
}
}
func ss(pan func(int32) bool, s string) string {
ss := []rune(s)
for i, c := range ss {
if pan(c) {
continue
} else {
ss[i] = '?'
}
}
res := string(ss)
return res
}
func main() {
s := "asdas阿松大短裤sadasd"
k := ss(pan, s)
fmt.Print(k)
}
(数据类型这块不熟悉,晕晕晕…)
匿名函数(闭包)
匿名函数同样被称之为闭包(函数式语言的术语):它们被允许调用定义在其它环境下的变量。闭包可使得某个函数捕捉到一些外部状态,例如:函数被创建时的状态。另一种表示方式为:一个闭包继承了函数所声明时的作用域。这种状态(作用域内的变量)都被共享到闭包的环境中,因此这些变量可以在闭包中被操作,直到被销毁,
练习 6.8
在 main() 函数中写一个用于打印 Hello World 字符串的匿名函数并赋值给变量 fv,然后调用该函数并打印变量 fv 的类型。
package main
import (
"fmt"
"reflect"
)
func main() {
fv := func() { fmt.Print("hello world\n") }
fv()
fmt.Print(reflect.TypeOf(fv), "\n")
fmt.Print(fv)
}
fv是一个地址指向匿名函数
函数作为返回值
练习 6.9 fibonacci_closure
不使用递归但使用闭包改写第 6.6 节中的斐波那契数列程序。
package main
func fibonacci_closure() func(a int) int {
var x int
var y int
return func(a int) int {
if a == 1 {
x = 1
return x
} else if a == 2 {
y = 1
return y
} else {
temp := y
y = x + y
x = temp
return y
}
}
}
func main() {
y := fibonacci_closure()
for i := 1; i <= 10; i++ {
x := y(i)
print(x, "\n")
}
}
使用闭包调试
当您在分析和调试复杂的程序时,无数个函数在不同的代码文件中相互调用,如果这时候能够准确地知道哪个文件中的具体哪个函数正在执行,对于调试是十分有帮助的。您可以使用 runtime 或 log 包中的特殊函数来实现这样的功能。包 runtime 中的函数 Caller() 提供了相应的信息,因此可以在需要的时候实现一个 where() 闭包函数来打印函数执行的位置
where := func() {
_, file, line, _ := runtime.Caller(1)
log.Printf("%s:%d", file, line)
}
where()
// some code
where()
// some more code
where()
您也可以设置 log 包中的 flag 参数来实现:
log.SetFlags(log.Llongfile)
log.Print("")
或使用一个更加简短版本的 where() 函数:
var where = log.Print
func func1() {
where()
... some code
where()
... some code
where()
}
计算函数执行时间
有时候,能够知道一个计算执行消耗的时间是非常有意义的,尤其是在对比和基准测试中。最简单的一个办法就是在计算开始之前设置一个起始时间,再记录计算结束时的结束时间,最后计算它们的差值,就是这个计算所消耗的时间。想要实现这样的做法,可以使用 time 包中的 Now() 和 Sub() 函数:
start := time.Now()
longCalculation()
end := time.Now()
delta := end.Sub(start)
fmt.Printf("longCalculation took this amount of time: %s\n", delta)