https://www.jianshu.com/p/b91c4400d4b2
(1)本次声明与已声明的 v 处于同一作用域中(若 v 已在外层作用域中声明过,则此次声明 会创建一个新的变量 §)
(2)在初始化中与其类型相应的值才能赋予 v,且
(3)在此次声明中至少另有一个变量是新声明的。
// 如同 C 的 for 循环
for init; condition; post { }
// 如同 C 的 while 循环
for condition { }
// 如同 C 的 for(;;) 循环
for { }
若你想遍历数组、切片、字符串或者映射,或从信道中读取消息, range 子句能够帮你轻松 实现循环。
for key, value := range oldMap {
newMap[key] = value
}
func shouldEscape(c byte) bool {
switch c {
case ' ', '?', '&', '=', '#', '+', '%':
return true
}
return false
}
*T
的值。用 Go 的术语来说,它返回一个指针, 该指针指向新分配的,类型为 T 的 零值。*T
)的一个已初始化 (而非置零)的值。 出现这种用 差异的原因在于,这三种类型本质上为引用数据类型,它们在使用前必须初始化。 例如,切 片是一个具有三项内容的描述符,包含一个指向(数组内部)数据的指针、长度以及容量, 在这三项被初始化之前,该切片为 nil。对于切片、映射和信道,make 用于初始化其内部的 数据结构并准备好将要使用的值。例如, make([]int, 10, 100)
func (file `*`File) Read(buf []byte) (n int, err error)
type Transform [3][3]float64 // A 3x3 array, really an array of arrays.
type LinesOfText [][]byte // A slice of byte slices.
由于切片长度是可变的,因此其内部可能拥有多个不同长度的切片。在我们的 LinesOfText 例 子中,这是种常见的情况:每行都有其自己的长度。
text := LinesOfText{
[]byte("Now is the time"),
[]byte("for all good gophers"),
[]byte("to bring some fun to the party."),
}
(1)首先是一次一行的:
// 分配顶层切片。
picture := make([][]uint8, YSize) // 每 y 个单元一行。
// 遍历行,为每一行都分配切片
for i := range picture {
picture[i] = make([]uint8, XSize)
}
(2)现在是一次分配,对行进行切片:
// 分配顶层切片,和前面一样。
picture := make([][]uint8, YSize) // 每 y 个单元一行。
// 分配一个大的切片来保存所有像素
pixels := make([]uint8, XSize*YSize) // 拥有类型 []uint8,尽管图片是 [][]uint8.
// 遍历行,从剩余像素切片的前面切出每行来。
for i := range picture {
picture[i], pixels = pixels[:XSize], pixels[XSize:]
}
type T struct {
a int
b float64
c string
}
t := &T{ 7, -2.35, "abc\tdef" }
fmt.Printf("%v\n", t)
fmt.Printf("%+v\n", t)
fmt.Printf("%#v\n", t)
fmt.Printf("%#v\n", timeZone)
// prints 将打印
&{7 -2.35 abc def}
&{a:7 b:-2.35 c:abc def}
&main.T{a:7, b:-2.35, c:"abc\tdef"}
map[string] int{"CST":-21600, "PST":-28800, "EST":-18000, "UTC":0, "MST":-25200}
type MyString string func (m MyString) String() string { return fmt.Sprintf("MyString=%s", m) // 错误:会无限递归
}
type ByteSlice []byte
func (slice ByteSlice) Append(data []byte) []byte {
// 主体和前面相同。
}
其实我们做得更好。若我们将函数修改为与标准 Write 类似的方法,就像这样,
func (p *ByteSlice) Write(data []byte) (n int, err error) {
slice := *p
// 依旧和前面相同。
*p = slice
return len(data), nil
}
那么类型 *ByteSlice
就满足了标准的 io.Writer 接口,这将非常实用。 例如,我们可以通过 打印将内容写入。
var b ByteSlice
fmt.Fprintf(&b, "This hour has %d days\n", 7)
*ByteSlice
才满足 io.Writer。以指针或值为接收者 的区别在于:值方法可通过指针和值调用, 而指针方法只能通过指针来调用。type Sequence []int
// Methods required by sort.Interface.
// sort.Interface 所需的方法。
func (s Sequence) Len() int {
return len(s)
}
func (s Sequence) Less(i, j int) bool {
return s[i] < s[j]
}
func (s Sequence) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// Method for printing - sorts the elements before printing.
// 用于打印的方法 - 在打印前对元素进行排序。
func (s Sequence) String() string {
sort.Sort(s)
str := "["
for i, elem := range s {
if i > 0 {
str += " "
}
str += fmt.Sprint(elem)
}
return str + "]"
}
str, ok := value.(string)
if ok
{
fmt.Printf("string value is: %q\n", str)
} else {
fmt.Printf("value is not a string\n")
}
if _, err := os.Stat(path); os.IsNotExist(err) {
fmt.Printf("%s does not exist\n", path)
}
package main
import (
"fmt"
"io"
"log"
"os"
)
var _ = fmt.Printf // For debugging; delete when done. // 用于调试,结束时删除。
var _ io.Reader // For debugging; delete when done. // 用于调试,结束时删除。
func main() {
fd, err := os.Open("test.go")
if err != nil {
log.Fatal(err)
}
// TODO: use fd.
_ = fd
}
import _ "net/http/pprof"
这种导入格式能明确表示该包是为其副作用而导入的,因为没有其它使用该包的可能: 在此 文件中,它没有名字。(若它有名字而我们没有使用,编译器就会拒绝该程序。)
*log.Logger
, 可以直接写作 job.Logger。若我们想精炼 Logger 的方法时, 这会非 常有用。不要通过共享内存来通信,而应通过通信来共享内存。
go list.Sort() // 并发运行 list.Sort,无需等它结束。
ci := make(chan int) // 整数类型的无缓冲信道
cj := make(chan int, 0) // 整数类型的无缓冲信道
cs := make(chan *os.File, 100) // 指向文件指针的带缓冲信道
c := make(chan int) // 分配一个信道
// 在 Go 程中启动排序。当它完成后,在信道上发送信号。
go func() {
list.Sort()
c <- 1 // 发送信号,什么值无所谓。
}()
doSomethingForAWhile()
<-c // 等待排序结束,丢弃发来的值。
var sem = make(chan int, MaxOutstanding)
func handle(r *Request) {
sem <- 1 // 等待活动队列清空。
process(r) // 可能需要很长时间。
<-sem // 完成;使下一个请求可以运行。
}
func Serve(queue chan *Request) {
for {
req := <-queue
go handle(req) // 无需等待 handle 结束。
}
}
func Serve(queue chan *Request) {
for req := range queue {
sem <- 1
go func() {
process(req) // 这儿有 Bug,解释见下。
<-sem
}()
}
}
func Serve(queue chan *Request) {
for req := range queue {
sem <- 1
go func(req *Request) {
process(req)
<-sem
}(req)
}
}
func Serve(queue chan *Request) {
for req := range queue {
req := req // 为该 Go 程创建 req 的新实例。
sem <- 1
go func() {
process(req)
<-sem
}()
}
}
req := req
。但在 Go 中这样做是合法且惯用的。你用相同的名字获得了该变量的一个新的版本, 以此来 局部地刻意屏蔽循环变量,使它对每个 Go 程保持唯一。func handle(queue chan *Request) {
for r := range queue {
process(r)
}
}
func Serve(clientRequests chan *Request, quit chan bool) {
// 启动处理程序
for i := 0; i < MaxOutstanding; i++ {
go handle(clientRequests)
}
<-quit // 等待通知退出。
}
type Request struct {
args []int
f func([]int) int
resultChan chan int
}
客户端提供了一个函数及其实参,此外在请求对象中还有个接收应答的信道
func sum(a []int) (s int) {
for _, v := range a {
s += v
}
return
}
request := &Request{[]int{3, 4, 5}, sum, make(chan int)}
// 发送请求
clientRequests <- request
// 等待回应
fmt.Printf("answer: %d\n", <-request.resultChan)
type Vector []float64
// 将此操应用至 v[i], v[i+1] ... 直到 v[n-1]
func (v Vector) DoSome(i, n int, u Vector, c chan int) {
for ; i < n; i++ {
v[i] += u.Op(v[i])
}
c <- 1
// 发信号表示这一块计算完成。
}
const NCPU = 4 // CPU 核心数
func (v Vector) DoAll(u Vector) {
c := make(chan int, NCPU)
// 缓冲区是可选的,但明显用上更好
for i := 0; i < NCPU; i++ {
go v.DoSome(i*len(v)/NCPU, (i+1)*len(v)/NCPU, u, c)
}
// 排空信道。
for i := 0; i < NCPU; i++ {
<-c // 等待任务完成
}
// 一切完成。
}
var freeList = make(chan *Buffer, 100)
var serverChan = make(chan *Buffer)
func client() {
for {
var b *Buffer
// 若缓冲区可用就用它,不可用就分配个新的。
select {
case b = <-freeList:
// 获取一个,不做别的。
default:
// 非空闲,因此分配一个新的。
b = new(Buffer)
}
load(b) // 从网络中读取下一条消息。
serverChan <- b // 发送至服务器。
}
}
func server() {
for {
b := <-serverChan // 等待工作。
process(b) // 若缓冲区有空间就重用它。
select {
case freeList <- b:
// 将缓冲区放大空闲列表中,不做别的。
default:
// 空闲列表已满,保持就好。
}
}
}
// 用牛顿法计算立方根的一个玩具实现。
func CubeRoot(x float64) float64 {
z := x/3 // 任意初始值
for i := 0; i < 1e6; i++ {
prevz := z
z -= (z*z*z-x) / (3*z*z)
if veryClose(z, prevz) {
return z
}
}
// 一百万次迭代并未收敛,事情出错了。
panic(fmt.Sprintf("CubeRoot(%g) did not converge", x))
}
var user = os.Getenv("USER")
func init() {
if user == "" {
panic("no value for $USER")
}
}
func server(workChan <-chan *Work) {
for work := range workChan {
go safelyDo(work)
}
}
func safelyDo(work *Work) {
defer func() {
if err := recover(); err != nil {
log.Println("work failed:", err)
}
}()
do(work)
}
// Error 是解析错误的类型,它满足 error 接口。
type Error string
func (e Error) Error() string {
return string(e)
}
// error 是 *Regexp 的方法,它通过用一个 Error 触发 Panic 来报告解析错误。
func (regexp *Regexp) error(err string) {
panic(Error(err))
}
// Compile 返回该正则表达式解析后的表示。
func Compile(str string) (regexp *Regexp, err error) {
regexp = new(Regexp)
// doParse will panic if there is a parse error.
defer func() {
if e := recover(); e != nil {
regexp = nil // 清理返回值。
err = e.(Error) // 若它不是解析错误,将重新触发 Panic。
}
}()
return regexp.doParse(str), nil
}
if pos == 0 {
re.error("'*' illegal at start of expression")
}
package main
import (
"flag"
"html/template"
"log"
"net/http"
)
var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18
var templ = template.Must(template.New("qr").Parse(templateStr))
func main() {
flag.Parse()
http.Handle("/", http.HandlerFunc(QR))
err := http.ListenAndServe(*addr, nil)
if err != nil {
log.Fatal("ListenAndServe:", err)
}
}
func QR(w http.ResponseWriter, req *http.Request) {
templ.Execute(w, req.FormValue("s"))
}
const templateStr = `
<html>
<head>
<title>QR Link Generator</title>
</head>
<body>
{{if .}}
<img src="http://chart.apis.google.com/chart?chs=300x300&cht=qr&choe=UTF-8&chl={{.}}"
/>
<br>
{{.}}
<br>
<br>
{{end}}
<form action="/" name=f method="GET">
<input maxLength=1024 size=70 name=s value="" title="Text to QR Encode">
<input type=submit value="Show QR" name=qr>
</form>
</body>
</html>
`
领取专属 10元无门槛券
私享最新 技术干货