Go语言规格说明书 之 内建函数(Built-in functions)
go version go1.11 windows/amd64
本文为阅读Go语言中文官网的规则说明书(https://golang.google.cn/ref/spec)而做的笔记,介绍Go语言的 内建函数(Built-in functions)。
规格说明书中的目录如下:
Built-in functions
-Close
-Length and capacity
-Allocation
-Making slices, maps and channels
-Appending to and copying slices
-Deletion of map elements
-Manipulating complex numbers
-Handling panics
-Bootstrapping
在规格说明书中,介绍了15个内建函数,如下所示:
close, len, cap, new, make, append, copy, delete,
complex, real, imag, panic, recover,
print, println
下面简要介绍各个函数:
close
关闭信道(channel)。
在没有更多数据 要 发送 的时候关闭信道。
close(channel)
也就是说,channel必须是 只发 或 双向类型的才可以,否则错误。
发送 或 关闭 一个已经关闭的 信道 会出现 运行时错误(run-time panic)。
关闭 nil 信道 也会导致 运行时错误。
在调用close函数,并且之前发送的数据都已被接收到 之后,接收操作 会 返回 信道类型的0值,且不会阻塞,,多个值的接收操作 会返回 一个接收值 和 信道是否关闭的标志。
// 单个值接收操作
v1 := <-ch
v2 = <-ch // 多(两)个值接收操作
x, ok = <-ch
x, ok := <-ch
var x, ok = <-ch
var x, ok T = <-ch
len,cap
这两个函数接收各种类型的参数,返回一个int型的结果。
len函数返回参数的 长度、元素个数,cap返回参数的 容量。
官文介绍:
Call Argument type Result len(s) string type string length in bytes
[n]T, *[n]T array length (== n)
[]T slice length
map[K]T map length (number of defined keys)
chan T number of elements queued in channel buffer cap(s) [n]T, *[n]T array length (== n)
[]T slice capacity
chan T channel buffer capacity
从官文介绍可以看出,len函数 的参数可以为 字符串、数组类型、数组指针、分片、映射类型、信道——在信道缓存中排队的元素数量,
cap函数 的参数可以为 数组、数组指针、分片、信道——缓存容量大小。
分片类型 的容量是指 分配给它的 底层数组 的元素的数量。在任何时候,下面的公式是成立的:
0 <= len(s) <= cap(s)
值为 nil 的 分片、映射、信道 的长度为0,值为 nil 的分片的容量为 0。
如果 s 是 字符串常量,那么,表达式 len(s) 也是常量;
如果 s 是 数组 或者 指向数组的指针,那么,表达式 len(s) 和 cap(s) 也是常量,并且 s 的元素类型不是 接收信道 或 函数类型,在这种情况下,s 是无法被估值的(evaluated);
除了上面的情况外,调用 len 和 cap 得到的都不是常量,而且s是可以估值的(翻译的有些不准确,这个evaluated到底是什么意思?)。
官文示例:
const (
c1 = imag(2i) // imag(2i) = 2.0 is a constant
c2 = len([10]float64{2}) // [10]float64{2} contains no function calls
c3 = len([10]float64{c1}) // [10]float64{c1} contains no function calls
c4 = len([10]float64{imag(2i)}) // imag(2i) is a constant and no function call is issued
c5 = len([10]float64{imag(z)}) // invalid: imag(z) is a (non-constant) function call
)
var z complex128
new
new函数 在学习接口类型时遇到了,使用new,可以新建一个类型的实例。
new函数 属于 Allocation(分配) 小节,分配 什么呢?内存空间了。
官文翻译:
在 运行时,给 变量 分配一个 存储空间,这个空间大小由 变量类型决定,并且 返回 一个 变量类型的指针 指向分配的空间。
在分配完存储空间后,再进行初始化——分配完毕后是0值。
疑问,不能在分配时进行初始化吗?需要试验!
type S1 struct {
int
float32
} var sv2 = new(S1{23, 32.0}) // 错误:S1 literal is not a type
fmt.Println(sv2)
fmt.Println(sv2.int, sv2.float32)
看来不能在调用new函数时赋值!或者俺的方法不对!
更改代码后测试:
var sv2 = new(S1)
sv2.int = 23
sv2.float32 = 32.0
fmt.Println(sv2)
fmt.Println(sv2.int, sv2.float32) // 测试结果
// 注意第一行的 & 符号!
// 返回的 sv2 是一个 指针
&{23 32}
23 32
官文示例:
type S struct { a int; b float64 }
new(S)
make
make函数 属于 Making slices, maps and channels 一节的唯一函数,用于常见 分片、映射和信道。
make函数 的第一个参数为 类型T,可以选择跟着一个表达式的指明类型的列表(原文:optionally followed by a type-specific list of expressions,翻译存在问题)。
返回 类型T,而不是 *T,,空间初始化可以参考initial values。
官文中介绍的用法:
Call Type T Result make(T, n) slice slice of type T with length n and capacity n // 第二个参数n表示长度,没有第三个参数时,n也表示容量
make(T, n, m) slice slice of type T with length n and capacity m // 第二个参数n表示长度,第三个参数m表示容量 make(T) map map of type T // 映射,但没有分配初始空间
make(T, n) map map of type T with initial space for approximately n elements // 分配了初始空间的映射,可以容纳n个元素,但可以自动扩展(需确认) make(T) channel unbuffered channel of type T // 无缓冲信道
make(T, n) channel buffered channel of type T, buffer size n // 缓冲区大小为 n(字节吗?)的信道,关于信道的资料,自己还需dig
每一个n、m都必须是 整型(integer type,各种整型吗?),或者一个 无类型的常量。一个常量大小参数不能是非负数,并且可以被类型int的值表示,,如果是无类型的常量,会被分配类型int。
n不能大于m。
如果n在运行时 为 负数 或 大于m,会产生一个运行时错误。
官文示例——合法、非法使用的都有:
s := make([]int, 10, 100) // slice with len(s) == 10, cap(s) == 100
s := make([]int, 1e3) // slice with len(s) == cap(s) == 1000
s := make([]int, 1<<63) // illegal: len(s) is not representable by a value of type int // 1<<63超过类型int的范围了
s := make([]int, 10, 0) // illegal: len(s) > cap(s)
c := make(chan int, 10) // channel with a buffer size of 10
m := make(map[string]int, 100) // map with initial space for approximately 100 elements
调用指定了类型和参数n的make函数创建映射时,会创建一个可以包含n个元素的初始化空间,但是,空间大小是和编译器的实现相关的(implementation-dependent)——这个大小是无法统一。
append,copy
这两个函数用来操作分片。从函数名可知,append用来给分片添加元素,copy用来从分片拷贝元素,拷贝到哪里呢?拷贝到分片中。
对于这两个函数,其返回结果是和 参数指向的内存是否覆盖(whether the memory referenced by the arguments overlaps) 没有关系的。
可变参函数append添加0到多个到分片,并返回 结果分片。
参数x中的值得类型T,必须是分片S的元素类型,但有一个特例,第一个参数为 []byte类型,第二个参数为string类型——但其结尾添加三个英文句号(...),这种情况表示将添加字符串的字节数组到第一个参数。
append(s S, x ...T) S // T is the element type of S
如果分片s的容量不足于包含新添加的值,appen将分配新的、足够大的 底层数组 来满足 存在的分片和新增值 的空间,,否则,appen函数将使用 底层数组。
疑问,前面讲到,append会返回新的分片,那么,这个分片和旧的分片是什么关系呢?是在旧的分片上操作的吗?需要dig!之前自己操作时,是需要把append的返回值 赋值给 旧分片的变量的。
官文示例:
s0 := []int{0, 0}
s1 := append(s0, 2) // append a single element s1 == []int{0, 0, 2}
s2 := append(s1, 3, 5, 7) // append multiple elements s2 == []int{0, 0, 2, 3, 5, 7}
s3 := append(s2, s0...) // append a slice s3 == []int{0, 0, 2, 3, 5, 7, 0, 0}
s4 := append(s3[3:6], s3[2:]...) // append overlapping slice s4 == []int{3, 5, 7, 2, 3, 5, 7, 0, 0}
// 下面这个很有意思!用到了 空接口,分片可以包含 任何类型 的数据了
var t []interface{}
t = append(t, 42, 3.1415, "foo") // t == []interface{}{42, 3.1415, "foo"} var b []byte
b = append(b, "bar"...) // append string contents b == []byte{'b', 'a', 'r' }
copy函数从 源分片拷贝若干元素到目的分片,并返回拷贝的元素的数量。源分片和目的分片的元素类型T必须相同,并可以被赋值给[]T。
拷贝的元素的数量是 len(src)、len(dst) 中的最小值。
和append函数类似,copy函数也存在特例,目的分片为 []byte类型,源分片为字符串类型(string),这表示将字符串的字节数组拷贝到字符分片中。注意,特例不需要三个英文句号(...),这和append函数不同。
copy函数签名:
copy(dst, src []T) int
copy(dst []byte, src string) int
copy函数官文示例:
var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7}
var s = make([]int, 6)
var b = make([]byte, 5)
n1 := copy(s, a[0:]) // n1 == 6, s == []int{0, 1, 2, 3, 4, 5}
n2 := copy(s, s[2:]) // n2 == 4, s == []int{2, 3, 4, 5, 4, 5}
n3 := copy(b, "Hello, World!") // n3 == 5, b == []byte("Hello")
delete
删除一个映射 m 中的键值为 k 的元素。k 的类型 必须是 可以赋值(assignable,这个不是很清楚什么意思,类型相同 或 结果的类型相同) 给 映射 m 的键类型的。
delete(m, k) // remove element m[k] from map m
如果映射 m 的值为 nil,或者,m[k] 不存在,那么,调用函数 delete 将什么也不做(no-op)。
complex,real,imag
这三个函数属于 操作复数(Manipulating complex numbers) 这一节。
大概意思:
complex函数 根据参数的 浮点类型的 实部、虚部 建立复数,real函数 获取一个复数的 实部,imag函数 获取一个复数的 虚部。
官文的三个函数签名:
complex(realPart, imaginaryPart floatT) complexT
real(complexT) floatT
imag(complexT) floatT
complex的两个参数必须是相同的浮点类型——float32、float64,float32生成复数类型为complex64,float64生产的复数类型为complex128。
如果一个参数是 没有类型的常量(untyped constant),它的类型就被转换为另一个参数的类型;
如果两个参数都是 没有类型的常量,它们必须 不是复数、或者它们的虚部为0,并且返回的是一个 没有类型的复数(特别)。
对于real、imag函数,参数必须是复数类型,返回值的类型由参数的类型决定,对应关系:complex64-->float32,complex128-->float64。
如果参数时一个 没有类型的常量,那么,参数必须是数,并且返回值是 没有类型的浮点常量(特别)。
real、imag函数 一起 形成了 complex函数 的反函数,对于一个复数类型Z的值z,下面的公式是成立的:
z == Z(complex(real(z), imag(z)))
如果这些函数的操作数都是 常量,那么,返回值也是常量。
官文示例:
var a = complex(2, -2) // complex128
const b = complex(1.0, -1.4) // untyped complex constant 1 - 1.4i
x := float32(math.Cos(math.Pi/2)) // float32
var c64 = complex(5, -x) // complex64
var s uint = complex(1, 0) // untyped complex constant 1 + 0i can be converted to uint
_ = complex(1, 2<<s) // illegal: 2 assumes floating-point type, cannot shift // 整数才可以做 位运算
var rl = real(c64) // float32
var im = imag(a) // float64
const c = imag(b) // untyped constant -1.4
_ = imag(3 << s) // illegal: 3 assumes complex type, cannot shift // 整数才可以做 位运算
panic,recover
这两个函数分别用于 报告 和 处理 运行时错误 或 程序自定义的错误条件,签名如下:
func panic(interface{})
func recover() interface{}
当然,panic函数 就是 报告一个错误,recover函数 就是负责处理的函数。
特别提醒,要读懂这一节,需要理解 Go语言中 的 Defer statements(延迟语句?)。
下面是官文翻译:
在执行 函数F 的时候,显式调用panic函数或者发生运行时错误(run-time panic)都会 终断函数F的执行,但是,任何被 函数F 延迟的函数 会立即被执行。
也就是说,函数F中发生错误了,如果没有 延迟函数,那么,函数F 被终断,如果有延迟函数,则执行完延迟函数后再终断。
前面讲到 函数F 的延迟函数被调用,所有的延迟函数执行完毕后,,接下来,函数F 的调用者 的延迟函数 开始执行,,直到goroutine的顶级函数的延迟函数被执行。
看到了吧,发生了错误,就一直是 延迟函数 在执行,直到goroutine的顶级函数的延迟函数被执行。那么,什么事goroutine呢?goroutine的顶级函数的延迟函数被执行 之后怎么样呢?销毁goroutine?
在这个时刻,程序被终止,错误条件被报告,同时被报告的还有函数panic的参数的值。这个终止序列被称为 panicking(翻译为什么好呢?惊险追踪?错误追踪?)。
官文示例——调用panic函数:
panic(42)
panic("unreachable")
panic(Error("cannot parse"))
上面讲了panic函数,用于 显示制造错误,在错误发生后,延迟函数开始执行,直到goroutine的顶级函数的延迟函数被执行完毕。
注意,延迟函数 在没有发生错误时也会执行,时机为 函数返回前(有返回return语句的) 和 函数结束前。
从上面也看到了,如果不阻止panic的扩散,程序(goroutine?这个自己还不完全清楚)会被终止!那么,怎么阻止呢?这就需要 recover函数 了!
recover函数 使用一段程序 去管理一个panicking goroutine的行为。
设想一下,函数G 延迟了 函数D 的执行,函数D中调用了 recover函数。
在同一个goroutine中,函数G发生了panic(错误),此时,被函数G延迟的函数开始执行——其中包括函数D,当执行到函数D时,函数D 中调用recover函数 的返回值 是传递到 调用panic函数中的值。
如果 函数D 正常返回,也没有产生新的panic,panicking这个过程就结束了!
在这种情况下,调用函数G到调用函数panic(发生错误)期间的函数状态就被忽略了,并且继续程序的正常执行。
前面到了函数D被执行了, 返回了,panicking结束了,此时,在函数D之前被函数G 延迟的 函数开始执行——如果有,函数G的执行被终止,返回它的调用者。
以上,便是执行recover函数的“内幕”!调用recover且没有产生新的panic就表示错误被处理了。程序还是在函数G中执行,但执行的是函数G的延迟函数,执行完延迟函数了,就返回到它的调用者。
前面提到 recover函数 有返回值,值为panic函数的参数。那么,recover函数调用后什么时候其返回值为 nil 呢?下面几种情况之一满足即返回 nil:
-panic函数的参数为 nil (空吗?)
-goroutine没有发生panicking——就是没错的时候调用recover恢复;
-recover函数不是被延迟函数直接调用的——这是什么情况?还需dig!
官文示例:protect函数 调用了 参数中的 函数g,并延迟了一个函数 用于处理 调用函数g 引发的panic(run-time panics)。
func protect(g func()) {
defer func() { // 延迟函数定义,关键字 defer
log.Println("done") // Println executes normally even if there is a panic
if x := recover(); x != nil { // 调用recover:如果发生错误,返回值 x 就不是 nil,此时,处理错误,只是打印一条信息
log.Printf("run time panic: %v", x)
}
}()
log.Println("start")
g()
}
几乎理解了,还需要熟悉 goroutine,以及阅读和练习更多Go代码才行啊!
print,println
目前的Go语言实现提供了一些在引导时有用的内建函数,文档为了完整性会介绍这些函数,但是,不保证它们会存在于Go语言中,而且它们不会返回结果。
比如,本节的print、println两个函数,都用来输出 参数。
官文函数说明:
Function Behavior print prints all arguments; formatting of arguments is implementation-specific
println like print but prints spaces between arguments and a newline at the end
实现(所谓的实现,是指,各个软件开发组织 根据 这份规格说明书 来开发Go语言编译器的实现吧)要求:
print、pringln不需要接受所有的参数类型,但是,打印 布尔、数值、字符串类型 是必须支持的。
后记
又是小半个下午加晚上的一两个小时,这篇博文可是花了5个小时左右啊!
缺少一些示例,自己的实践,不够完美,但是呢,大部分知识点是讲清楚了,不清楚的也是有标记的。
Go语言规格说明书 之 内建函数(Built-in functions)的更多相关文章
- Go语言规格说明书 之 通道 发送语句(send) 和 接收操作符(receive)
go version go1.11 windows/amd64 本文为阅读Go语言中文官网的规则说明书(https://golang.google.cn/ref/spec)而做的笔记,介绍Go语言的 ...
- Go语言规格说明书 之 通道类型(Channel types)
go version go1.11 windows/amd64 本文为阅读Go语言中文官网的规则说明书(https://golang.google.cn/ref/spec)而做的笔记,介绍Go语言的 ...
- Go语言规格说明书 之 词汇元素(Lexical elements)
go version go1.11 windows/amd64 本文为阅读Go语言中文官网的规则说明书(https://golang.google.cn/ref/spec)而做的笔记,完整的介绍Go语 ...
- Go语言规格说明书 之 Go语句(Go statements)
go version go1.11 windows/amd64 本文为阅读Go语言中文官网的规则说明书(https://golang.google.cn/ref/spec)而做的笔记,介绍Go语言的 ...
- Go语言规格说明书 之 类型(Types)
go version go1.11 windows/amd64 本文为阅读Go语言中文官网的规则说明书(https://golang.google.cn/ref/spec)而做的笔记,完整的介绍Go语 ...
- Go语言规格说明书 之 select语句(Select statements)
go version go1.11 windows/amd64 本文为阅读Go语言中文官网的规则说明书(https://golang.google.cn/ref/spec)而做的笔记,介绍Go语言的 ...
- Go语言规格说明书 之 接口类型(Interface types)
go version go1.11 windows/amd64 本文为阅读Go语言中文官网的规则说明书(https://golang.google.cn/ref/spec)而做的笔记,介绍Go语言的 ...
- Go语言规格说明书 之 结构体类型(Struct types)
go version go1.11 windows/amd64 本文为阅读Go语言中文官网的规则说明书(https://golang.google.cn/ref/spec)而做的笔记,介绍Go语言的 ...
- Go语言规格说明书 之 变量声明(Variable/Short variable declarations)
go version go1.11 windows/amd64 本文为阅读Go语言中文官网的规则说明书(https://golang.google.cn/ref/spec)而做的笔记,完整的介绍Go语 ...
随机推荐
- 受限玻尔兹曼机(Restricted Boltzmann Machine,RBM)
这篇写的主要是翻译网上一篇关于受限玻尔兹曼机的tutorial,看了那篇博文之后感觉算法方面讲的很清楚,自己收获很大,这里写下来作为学习之用. 原文网址为:http://imonad.com/rbm/ ...
- PHP的内存限制 Allowed memory size of 134217728 bytes exhausted (tried to allocate 1099 bytes) in
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 1099 bytes) in Fa ...
- 配置GitLab Push 自动触发Jenkins构建
配置GitLab Push 自动触发Jenkins构建 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 本篇博客是对之前的笔记:https://www.cnblogs.com/yin ...
- sublime js头部代码多行注释
安装 DocBlockr 插件,在写完function()的时候,在函数上面输入: /** + tab键(或回车键,Atom里不用另外安装插件,直接在函数的上面输入:/** + 回车键 即可).
- Java高并发秒杀API之高并发优化
---恢复内容开始--- 第1章 秒杀系统高并发优化分析 1.为什么要单独获得系统时间 访问cdn这些静态资源不用请求系统服务器 而CDN上没有系统时间,需要单独获取,获取系统时间不用优化,只是n ...
- Uva439:BFS题目总结
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <cstring> #include <cstd ...
- C# Winform中慎用Application.DoEvents
private void Add() { ; i < ; i++) { Button button = new Button(); button.Width = ; button.Height ...
- Linux安装mysql过程(转+完善)
http://blog.csdn.net/jerome_s/article/details/52883234yum 安装MySQL 1. 检查安装情况 查看有没有安装过: ...
- Java——Struts2 之国际化 struts.custom.i18n.resources=globalMessages
1.在src下 建立 struts.properties 文件,内容为:struts.custom.i18n.resources=globalMessages struts.custom.i18n.r ...
- android 加载图片
package mydemo.mycom.demo2; import android.graphics.Bitmap; import android.graphics.BitmapFactory; i ...