diff --git a/.gitignore b/.gitignore
index dfebd51a..b2eb862a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,7 @@
pkg/*
*.html
*.exe
+<<<<<<< HEAD
+=======
+
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
diff --git a/.gitignore.orig b/.gitignore.orig
new file mode 100644
index 00000000..b2eb862a
--- /dev/null
+++ b/.gitignore.orig
@@ -0,0 +1,8 @@
+*~
+pkg/*
+*.html
+*.exe
+<<<<<<< HEAD
+=======
+
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
diff --git a/README.md b/README.md
index 11537fa0..39e807e8 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,8 @@
# Multiple Language Versions
+<<<<<<< HEAD
+=======
+* [English](en/)
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
* [French](fr/)
* [Spanish](es/)
* [中文](zh/)
@@ -29,6 +33,11 @@ BBS:[http://golanghome.com/](http://golanghome.com/)
- [polaris](https://github.com/polaris1119)(review书)
- [雨痕](https://github.com/qyuhen)(review第二章)
+<<<<<<< HEAD
+=======
+Translator:
+ - [LarryBattle](https://github.com/LarryBattle)
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
## License
Book License: [CC BY-SA 3.0 License](http://creativecommons.org/licenses/by-sa/3.0/)
diff --git a/README.md.orig b/README.md.orig
new file mode 100644
index 00000000..af091edb
--- /dev/null
+++ b/README.md.orig
@@ -0,0 +1,46 @@
+# Multiple Language Versions
+<<<<<<< HEAD
+=======
+* [English](en/)
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
+* [French](fr/)
+* [Spanish](es/)
+* [中文](zh/)
+* [日本語](ja/)
+* [Turkish](tr/)
+* [Português - Brasil](pt-br/)
+* [German](de/)
+* [Русский](ru/)
+
+# Donate
+
+AliPay:
+
+English Donate:[donate](http://beego.me/donate)
+
+## Community
+QQ群:148647580
+
+BBS:[http://golanghome.com/](http://golanghome.com/)
+
+## Acknowledgments
+
+ - [四月份平民](https://plus.google.com/110445767383269817959) (review代码)
+ - [Hong Ruiqi](https://github.com/hongruiqi) (review代码)
+ - [BianJiang](https://github.com/border) (编写go开发工具Vim和Emacs的设置)
+ - [Oling Cat](https://github.com/OlingCat)(review代码)
+ - [Wenlei Wu](mailto:spadesacn@gmail.com)(提供一些图片展示)
+ - [polaris](https://github.com/polaris1119)(review书)
+ - [雨痕](https://github.com/qyuhen)(review第二章)
+
+<<<<<<< HEAD
+=======
+Translator:
+ - [LarryBattle](https://github.com/LarryBattle)
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
+## License
+Book License: [CC BY-SA 3.0 License](http://creativecommons.org/licenses/by-sa/3.0/)
+
+Code License: [BSD 3-Clause License]()
+
+
diff --git a/en/13.5.md b/en/13.5.md
index fa086c64..c11d0bd8 100644
--- a/en/13.5.md
+++ b/en/13.5.md
@@ -127,7 +127,11 @@ func (this *EditController) Get() {
id, _ := strconv.Atoi(this.Ctx.Params[":id"])
this.Data["Post"] = models.GetBlog(id)
this.Layout = "layout.tpl"
+<<<<<<< HEAD
this.TplNames = "new.tpl"
+=======
+ this.TplNames = "edit.tpl"
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
}
func (this *EditController) Post() {
@@ -150,9 +154,17 @@ type DeleteController struct {
}
func (this *DeleteController) Get() {
+<<<<<<< HEAD
id, _ := strconv.Atoi(this.Ctx.Params[":id"])
this.Data["Post"] = models.DelBlog(id)
this.Ctx.Redirect(302, "/")
+=======
+ id, _ := strconv.Atoi(this.Ctx.Input.Params[":id"])
+ blog := models.GetBlog(id)
+ this.Data["Post"] = blog
+ models.DelBlog(blog)
+ this.Ctx.Redirect(302, "/")
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
}
```
diff --git a/en/13.5.md.orig b/en/13.5.md.orig
new file mode 100644
index 00000000..c11d0bd8
--- /dev/null
+++ b/en/13.5.md.orig
@@ -0,0 +1,307 @@
+# 13.5 Adding, deleting and updating blogs
+
+We've already introduced the entire concept behind the Beego framework through examples and pseudo-code. This section will describe how to implement a blogging system using Beego, including the ability to browse, add, modify and delete blog posts.
+
+## Blog directory
+
+Our blog's directory structure can be seen below:
+
+```
+/main.go
+/views:
+ /view.tpl
+ /new.tpl
+ /layout.tpl
+ /index.tpl
+ /edit.tpl
+/models/model.go
+/controllers:
+ /index.go
+ /view.go
+ /new.go
+ /delete.go
+ /edit.go
+```
+
+
+## Blog routing
+
+Our blog's main routing rules are as follows:
+
+```
+//Show blog Home
+beego.RegisterController("/", &controllers.IndexController{})
+//View blog details
+beego.RegisterController("/view/: id([0-9]+)", &controllers.ViewController{})
+//Create blog Bowen
+beego.RegisterController("/new", &controllers.NewController{})
+//Delete Bowen
+beego.RegisterController("/delete/: id([0-9]+)", &controllers.DeleteController{})
+//Edit Bowen
+beego.RegisterController("/edit/: id([0-9]+)", &controllers.EditController{})
+```
+
+
+## Database structure
+
+A trivial database table to store basic blog information:
+
+```
+CREATE TABLE entries (
+ id INT AUTO_INCREMENT,
+ title TEXT,
+ content TEXT,
+ created DATETIME,
+ primary key (id)
+);
+```
+
+## Controller
+
+IndexController:
+
+```
+type IndexController struct {
+ beego.Controller
+}
+
+func (this *IndexController) Get() {
+ this.Data["blogs"] = models.GetAll()
+ this.Layout = "layout.tpl"
+ this.TplNames = "index.tpl"
+}
+```
+
+
+ViewController:
+
+```
+type ViewController struct {
+ beego.Controller
+}
+
+func (this *ViewController) Get() {
+ inputs := this.Input()
+ id, _ := strconv.Atoi(this.Ctx.Params[":id"])
+ this.Data["Post"] = models.GetBlog(id)
+ this.Layout = "layout.tpl"
+ this.TplNames = "view.tpl"
+}
+```
+
+
+NewController
+
+
+```
+type NewController struct {
+ beego.Controller
+}
+
+func (this *NewController) Get() {
+ this.Layout = "layout.tpl"
+ this.TplNames = "new.tpl"
+}
+
+func (this *NewController) Post() {
+ inputs := this.Input()
+ var blog models.Blog
+ blog.Title = inputs.Get("title")
+ blog.Content = inputs.Get("content")
+ blog.Created = time.Now()
+ models.SaveBlog(blog)
+ this.Ctx.Redirect(302, "/")
+}
+```
+
+
+EditController
+
+```
+type EditController struct {
+ beego.Controller
+}
+
+func (this *EditController) Get() {
+ inputs := this.Input()
+ id, _ := strconv.Atoi(this.Ctx.Params[":id"])
+ this.Data["Post"] = models.GetBlog(id)
+ this.Layout = "layout.tpl"
+<<<<<<< HEAD
+ this.TplNames = "new.tpl"
+=======
+ this.TplNames = "edit.tpl"
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
+}
+
+func (this *EditController) Post() {
+ inputs := this.Input()
+ var blog models.Blog
+ blog.Id, _ = strconv.Atoi(inputs.Get("id"))
+ blog.Title = inputs.Get("title")
+ blog.Content = inputs.Get("content")
+ blog.Created = time.Now()
+ models.SaveBlog(blog)
+ this.Ctx.Redirect(302, "/")
+}
+```
+
+DeleteController
+
+```
+type DeleteController struct {
+ beego.Controller
+}
+
+func (this *DeleteController) Get() {
+<<<<<<< HEAD
+ id, _ := strconv.Atoi(this.Ctx.Params[":id"])
+ this.Data["Post"] = models.DelBlog(id)
+ this.Ctx.Redirect(302, "/")
+=======
+ id, _ := strconv.Atoi(this.Ctx.Input.Params[":id"])
+ blog := models.GetBlog(id)
+ this.Data["Post"] = blog
+ models.DelBlog(blog)
+ this.Ctx.Redirect(302, "/")
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
+}
+```
+
+## Model layer
+
+```
+package models
+
+import (
+ "database/sql"
+ "github.com/astaxie/beedb"
+ _ "github.com/ziutek/mymysql/godrv"
+ "time"
+)
+
+type Blog struct {
+ Id int `PK`
+ Title string
+ Content string
+ Created time.Time
+}
+
+func GetLink() beedb.Model {
+ db, err := sql.Open("mymysql", "blog/astaxie/123456")
+ if err != nil {
+ panic(err)
+ }
+ orm := beedb.New(db)
+ return orm
+}
+
+func GetAll() (blogs []Blog) {
+ db := GetLink()
+ db.FindAll(&blogs)
+ return
+}
+
+func GetBlog(id int) (blog Blog) {
+ db := GetLink()
+ db.Where("id=?", id).Find(&blog)
+ return
+}
+
+func SaveBlog(blog Blog) (bg Blog) {
+ db := GetLink()
+ db.Save(&blog)
+ return bg
+}
+
+func DelBlog(blog Blog) {
+ db := GetLink()
+ db.Delete(&blog)
+ return
+}
+```
+
+## View layer
+
+layout.tpl
+
+```
+
+
+ My Blog
+
+
+
+
+
+
+{{.LayoutContent}}
+
+
+
+```
+
+index.tpl
+
+```
+Blog posts
+
+
+```
+
+view.tpl
+
+```
+{{.Post.Title}}
+{{.Post.Created}}
+
+{{.Post.Content}}
+```
+
+new.tpl
+
+```
+New Blog Post
+
+```
+
+edit.tpl
+
+```
+Edit {{.Post.Title}}
+
+New Blog Post
+
+```
+
+## Links
+
+- [Directory](preface.md)
+- Previous section: [Logs and configurations](13.4.md)
+- Next section: [Summary](13.6.md)
diff --git a/en/README.md b/en/README.md
index 5799e226..b9720496 100644
--- a/en/README.md
+++ b/en/README.md
@@ -1,7 +1,10 @@
+<<<<<<< HEAD
<<<<<<< 2b65ffe75a66a23ea074aa13de82d219774d01bf
# Go Web 编程
Go web编程是因为我喜欢Web编程,所以写了这本书,希望大家喜欢
=======
+=======
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
***Build Web Application with Golang***
======================================
@@ -39,6 +42,7 @@ BBS:[http://golanghome.com/](http://golanghome.com/)
This book is licensed under the [CC BY-SA 3.0 License](http://creativecommons.org/licenses/by-sa/3.0/),
the code is licensed under a [BSD 3-Clause License](), unless otherwise specified.
+<<<<<<< HEAD
<<<<<<< de3c5bdaa45425ed37306a45067d55eba7dcedc2
### Get Started
@@ -47,3 +51,5 @@ the code is licensed under a [BSD 3-Clause License](>>>>>> add english version
=======
>>>>>>> update the readme
+=======
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
diff --git a/en/README.md.orig b/en/README.md.orig
new file mode 100644
index 00000000..b9720496
--- /dev/null
+++ b/en/README.md.orig
@@ -0,0 +1,55 @@
+<<<<<<< HEAD
+<<<<<<< 2b65ffe75a66a23ea074aa13de82d219774d01bf
+# Go Web 编程
+Go web编程是因为我喜欢Web编程,所以写了这本书,希望大家喜欢
+=======
+=======
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
+***Build Web Application with Golang***
+======================================
+
+### Purpose
+
+Because I'm interested in web application development, I used my free time to write this book as an open source version. It doesn't mean that I have a very good ability to build web applications; I would like to share what I've done with Go in building web applications.
+
+- For those of you who are working with PHP/Python/Ruby, you will learn how to build a web application with Go.
+- For those of you who are working with C/C++, you will know how the web works.
+
+I believe the purpose of studying is sharing with others. The happiest thing in my life is sharing everything I've known with more people.
+
+# Donate
+
+AliPay:
+
+English Donate:[donate](http://beego.me/donate)
+
+## Community
+QQ群:386056972
+
+BBS:[http://golanghome.com/](http://golanghome.com/)
+
+### Acknowledgments
+
+ - [四月份平民 April Citizen](https://plus.google.com/110445767383269817959) (review code)
+ - [洪瑞琦 Hong Ruiqi](https://github.com/hongruiqi) (review code)
+ - [边 疆 BianJiang](https://github.com/border) (write the configurations about Vim and Emacs for Go development)
+ - [欧林猫 Oling Cat](https://github.com/OlingCat)(review code)
+ - [吴文磊 Wenlei Wu](mailto:spadesacn@gmail.com)(provide some pictures)
+ - [北极星 Polaris](https://github.com/polaris1119)(review whole book)
+ - [雨 痕 Rain Trail](https://github.com/qyuhen)(review chapter 2 and 3)
+
+### License
+
+This book is licensed under the [CC BY-SA 3.0 License](http://creativecommons.org/licenses/by-sa/3.0/),
+the code is licensed under a [BSD 3-Clause License](), unless otherwise specified.
+<<<<<<< HEAD
+<<<<<<< de3c5bdaa45425ed37306a45067d55eba7dcedc2
+
+### Get Started
+
+[Index](./eBook/preface.md)
+>>>>>>> add english version
+=======
+>>>>>>> update the readme
+=======
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
diff --git a/en/images/1.1.cmd.png b/en/images/1.1.cmd.png
new file mode 100644
index 00000000..0df2504b
Binary files /dev/null and b/en/images/1.1.cmd.png differ
diff --git a/en/images/1.1.linux.png b/en/images/1.1.linux.png
new file mode 100644
index 00000000..539563d9
Binary files /dev/null and b/en/images/1.1.linux.png differ
diff --git a/en/images/1.1.mac.png b/en/images/1.1.mac.png
new file mode 100644
index 00000000..ad2ceed6
Binary files /dev/null and b/en/images/1.1.mac.png differ
diff --git a/en/images/1.3.go.png b/en/images/1.3.go.png
new file mode 100644
index 00000000..8155b2d5
Binary files /dev/null and b/en/images/1.3.go.png differ
diff --git a/en/images/1.4.eclipse1.png b/en/images/1.4.eclipse1.png
new file mode 100644
index 00000000..5b2f10d8
Binary files /dev/null and b/en/images/1.4.eclipse1.png differ
diff --git a/en/images/1.4.eclipse2.png b/en/images/1.4.eclipse2.png
new file mode 100644
index 00000000..55931f33
Binary files /dev/null and b/en/images/1.4.eclipse2.png differ
diff --git a/en/images/1.4.eclipse3.png b/en/images/1.4.eclipse3.png
new file mode 100644
index 00000000..3c7bd634
Binary files /dev/null and b/en/images/1.4.eclipse3.png differ
diff --git a/en/images/1.4.eclipse4.png b/en/images/1.4.eclipse4.png
new file mode 100644
index 00000000..d4ee77af
Binary files /dev/null and b/en/images/1.4.eclipse4.png differ
diff --git a/en/images/1.4.eclipse5.png b/en/images/1.4.eclipse5.png
new file mode 100644
index 00000000..8a89555d
Binary files /dev/null and b/en/images/1.4.eclipse5.png differ
diff --git a/en/images/1.4.eclipse6.png b/en/images/1.4.eclipse6.png
new file mode 100644
index 00000000..7771ec2e
Binary files /dev/null and b/en/images/1.4.eclipse6.png differ
diff --git a/en/images/1.4.emacs.png b/en/images/1.4.emacs.png
new file mode 100644
index 00000000..3dd6845f
Binary files /dev/null and b/en/images/1.4.emacs.png differ
diff --git a/en/images/1.4.idea1.png b/en/images/1.4.idea1.png
new file mode 100644
index 00000000..87d2e51e
Binary files /dev/null and b/en/images/1.4.idea1.png differ
diff --git a/en/images/1.4.idea2.png b/en/images/1.4.idea2.png
new file mode 100644
index 00000000..8059b20f
Binary files /dev/null and b/en/images/1.4.idea2.png differ
diff --git a/en/images/1.4.idea3.png b/en/images/1.4.idea3.png
new file mode 100644
index 00000000..574e1d74
Binary files /dev/null and b/en/images/1.4.idea3.png differ
diff --git a/en/images/1.4.idea4.png b/en/images/1.4.idea4.png
new file mode 100644
index 00000000..f6e5138d
Binary files /dev/null and b/en/images/1.4.idea4.png differ
diff --git a/en/images/1.4.idea5.png b/en/images/1.4.idea5.png
new file mode 100644
index 00000000..7784a611
Binary files /dev/null and b/en/images/1.4.idea5.png differ
diff --git a/en/images/1.4.liteide.png b/en/images/1.4.liteide.png
new file mode 100644
index 00000000..f28d755e
Binary files /dev/null and b/en/images/1.4.liteide.png differ
diff --git a/en/images/1.4.sublime1.png b/en/images/1.4.sublime1.png
new file mode 100644
index 00000000..60f2b3db
Binary files /dev/null and b/en/images/1.4.sublime1.png differ
diff --git a/en/images/1.4.sublime2.png b/en/images/1.4.sublime2.png
new file mode 100644
index 00000000..a888636f
Binary files /dev/null and b/en/images/1.4.sublime2.png differ
diff --git a/en/images/1.4.sublime3.png b/en/images/1.4.sublime3.png
new file mode 100644
index 00000000..8b2eb3dc
Binary files /dev/null and b/en/images/1.4.sublime3.png differ
diff --git a/en/images/1.4.sublime4.png b/en/images/1.4.sublime4.png
new file mode 100644
index 00000000..b0fef624
Binary files /dev/null and b/en/images/1.4.sublime4.png differ
diff --git a/en/images/1.4.vim.png b/en/images/1.4.vim.png
new file mode 100644
index 00000000..27a15c05
Binary files /dev/null and b/en/images/1.4.vim.png differ
diff --git a/en/images/13.1.gopath.png b/en/images/13.1.gopath.png
new file mode 100644
index 00000000..c948437a
Binary files /dev/null and b/en/images/13.1.gopath.png differ
diff --git a/en/images/13.1.gopath2.png b/en/images/13.1.gopath2.png
new file mode 100644
index 00000000..450b4104
Binary files /dev/null and b/en/images/13.1.gopath2.png differ
diff --git a/en/images/13.4.beego.png b/en/images/13.4.beego.png
new file mode 100644
index 00000000..96a9d274
Binary files /dev/null and b/en/images/13.4.beego.png differ
diff --git a/en/images/14.1.bootstrap.png b/en/images/14.1.bootstrap.png
new file mode 100644
index 00000000..13ab7f82
Binary files /dev/null and b/en/images/14.1.bootstrap.png differ
diff --git a/en/images/14.1.bootstrap2.png b/en/images/14.1.bootstrap2.png
new file mode 100644
index 00000000..753ffc79
Binary files /dev/null and b/en/images/14.1.bootstrap2.png differ
diff --git a/en/images/14.1.bootstrap3.png b/en/images/14.1.bootstrap3.png
new file mode 100644
index 00000000..460b8183
Binary files /dev/null and b/en/images/14.1.bootstrap3.png differ
diff --git a/en/images/14.4.github.png b/en/images/14.4.github.png
new file mode 100644
index 00000000..4da4c6f5
Binary files /dev/null and b/en/images/14.4.github.png differ
diff --git a/en/images/14.4.github2.png b/en/images/14.4.github2.png
new file mode 100644
index 00000000..c3ae04bf
Binary files /dev/null and b/en/images/14.4.github2.png differ
diff --git a/en/images/14.4.github3.png b/en/images/14.4.github3.png
new file mode 100644
index 00000000..e98768c3
Binary files /dev/null and b/en/images/14.4.github3.png differ
diff --git a/en/images/14.6.pprof.png b/en/images/14.6.pprof.png
new file mode 100644
index 00000000..532f1a76
Binary files /dev/null and b/en/images/14.6.pprof.png differ
diff --git a/en/images/14.6.pprof2.png b/en/images/14.6.pprof2.png
new file mode 100644
index 00000000..610c93a4
Binary files /dev/null and b/en/images/14.6.pprof2.png differ
diff --git a/en/images/14.6.pprof3.png b/en/images/14.6.pprof3.png
new file mode 100644
index 00000000..a3b9f7ba
Binary files /dev/null and b/en/images/14.6.pprof3.png differ
diff --git a/en/images/2.2.array.png b/en/images/2.2.array.png
new file mode 100644
index 00000000..5560023b
Binary files /dev/null and b/en/images/2.2.array.png differ
diff --git a/en/images/2.2.basic.png b/en/images/2.2.basic.png
new file mode 100644
index 00000000..9bac6a0f
Binary files /dev/null and b/en/images/2.2.basic.png differ
diff --git a/en/images/2.2.makenew.png b/en/images/2.2.makenew.png
new file mode 100644
index 00000000..00f74179
Binary files /dev/null and b/en/images/2.2.makenew.png differ
diff --git a/en/images/2.2.slice.png b/en/images/2.2.slice.png
new file mode 100644
index 00000000..119f2141
Binary files /dev/null and b/en/images/2.2.slice.png differ
diff --git a/en/images/2.2.slice2.png b/en/images/2.2.slice2.png
new file mode 100644
index 00000000..0729a1bf
Binary files /dev/null and b/en/images/2.2.slice2.png differ
diff --git a/en/images/2.3.init.png b/en/images/2.3.init.png
new file mode 100644
index 00000000..abe7cfad
Binary files /dev/null and b/en/images/2.3.init.png differ
diff --git a/en/images/2.4.student_struct.png b/en/images/2.4.student_struct.png
new file mode 100644
index 00000000..7c4f87ac
Binary files /dev/null and b/en/images/2.4.student_struct.png differ
diff --git a/en/images/2.5.rect_func_without_receiver.png b/en/images/2.5.rect_func_without_receiver.png
new file mode 100644
index 00000000..b4b571fd
Binary files /dev/null and b/en/images/2.5.rect_func_without_receiver.png differ
diff --git a/en/images/2.5.shapes_func_with_receiver_cp.png b/en/images/2.5.shapes_func_with_receiver_cp.png
new file mode 100644
index 00000000..2d26a01d
Binary files /dev/null and b/en/images/2.5.shapes_func_with_receiver_cp.png differ
diff --git a/en/images/2.5.shapes_func_without_receiver.png b/en/images/2.5.shapes_func_without_receiver.png
new file mode 100644
index 00000000..112f56fc
Binary files /dev/null and b/en/images/2.5.shapes_func_without_receiver.png differ
diff --git a/en/images/3.1.dns2.png b/en/images/3.1.dns2.png
new file mode 100644
index 00000000..f432edf3
Binary files /dev/null and b/en/images/3.1.dns2.png differ
diff --git a/en/images/3.1.dns_hierachy.png b/en/images/3.1.dns_hierachy.png
new file mode 100644
index 00000000..8dfeb232
Binary files /dev/null and b/en/images/3.1.dns_hierachy.png differ
diff --git a/en/images/3.1.dns_inquery.png b/en/images/3.1.dns_inquery.png
new file mode 100644
index 00000000..b95d952e
Binary files /dev/null and b/en/images/3.1.dns_inquery.png differ
diff --git a/en/images/3.1.http.png b/en/images/3.1.http.png
new file mode 100644
index 00000000..25108bf3
Binary files /dev/null and b/en/images/3.1.http.png differ
diff --git a/en/images/3.1.httpPOST.png b/en/images/3.1.httpPOST.png
new file mode 100644
index 00000000..31d02020
Binary files /dev/null and b/en/images/3.1.httpPOST.png differ
diff --git a/en/images/3.1.response.png b/en/images/3.1.response.png
new file mode 100644
index 00000000..978de790
Binary files /dev/null and b/en/images/3.1.response.png differ
diff --git a/en/images/3.1.web.png b/en/images/3.1.web.png
new file mode 100644
index 00000000..5b98b5dc
Binary files /dev/null and b/en/images/3.1.web.png differ
diff --git a/en/images/3.1.web2.png b/en/images/3.1.web2.png
new file mode 100644
index 00000000..a604c217
Binary files /dev/null and b/en/images/3.1.web2.png differ
diff --git a/en/images/3.2.goweb.png b/en/images/3.2.goweb.png
new file mode 100644
index 00000000..d6a53829
Binary files /dev/null and b/en/images/3.2.goweb.png differ
diff --git a/en/images/3.3.http.png b/en/images/3.3.http.png
new file mode 100644
index 00000000..40137e33
Binary files /dev/null and b/en/images/3.3.http.png differ
diff --git a/en/images/3.3.illustrator.png b/en/images/3.3.illustrator.png
new file mode 100644
index 00000000..8159b8bb
Binary files /dev/null and b/en/images/3.3.illustrator.png differ
diff --git a/en/images/4.1.login.png b/en/images/4.1.login.png
new file mode 100644
index 00000000..dfca39df
Binary files /dev/null and b/en/images/4.1.login.png differ
diff --git a/en/images/4.1.slice.png b/en/images/4.1.slice.png
new file mode 100644
index 00000000..3405c147
Binary files /dev/null and b/en/images/4.1.slice.png differ
diff --git a/en/images/4.3.escape.png b/en/images/4.3.escape.png
new file mode 100644
index 00000000..76ce1245
Binary files /dev/null and b/en/images/4.3.escape.png differ
diff --git a/en/images/4.4.token.png b/en/images/4.4.token.png
new file mode 100644
index 00000000..b52cc1d1
Binary files /dev/null and b/en/images/4.4.token.png differ
diff --git a/en/images/4.5.upload.png b/en/images/4.5.upload.png
new file mode 100644
index 00000000..e5766e38
Binary files /dev/null and b/en/images/4.5.upload.png differ
diff --git a/en/images/4.5.upload2.png b/en/images/4.5.upload2.png
new file mode 100644
index 00000000..06460446
Binary files /dev/null and b/en/images/4.5.upload2.png differ
diff --git a/en/images/5.6.mongodb.png b/en/images/5.6.mongodb.png
new file mode 100644
index 00000000..6161fbe5
Binary files /dev/null and b/en/images/5.6.mongodb.png differ
diff --git a/en/images/6.1.cookie.png b/en/images/6.1.cookie.png
new file mode 100644
index 00000000..b94559cf
Binary files /dev/null and b/en/images/6.1.cookie.png differ
diff --git a/en/images/6.1.cookie2.png b/en/images/6.1.cookie2.png
new file mode 100644
index 00000000..2888e392
Binary files /dev/null and b/en/images/6.1.cookie2.png differ
diff --git a/en/images/6.1.session.png b/en/images/6.1.session.png
new file mode 100644
index 00000000..f538b8f5
Binary files /dev/null and b/en/images/6.1.session.png differ
diff --git a/en/images/6.4.cookie.png b/en/images/6.4.cookie.png
new file mode 100644
index 00000000..0dad93f1
Binary files /dev/null and b/en/images/6.4.cookie.png differ
diff --git a/en/images/6.4.hijack.png b/en/images/6.4.hijack.png
new file mode 100644
index 00000000..5ab0753f
Binary files /dev/null and b/en/images/6.4.hijack.png differ
diff --git a/en/images/6.4.hijacksuccess.png b/en/images/6.4.hijacksuccess.png
new file mode 100644
index 00000000..57f48d41
Binary files /dev/null and b/en/images/6.4.hijacksuccess.png differ
diff --git a/en/images/6.4.setcookie.png b/en/images/6.4.setcookie.png
new file mode 100644
index 00000000..dbb52170
Binary files /dev/null and b/en/images/6.4.setcookie.png differ
diff --git a/en/images/7.4.template.png b/en/images/7.4.template.png
new file mode 100644
index 00000000..195e24fb
Binary files /dev/null and b/en/images/7.4.template.png differ
diff --git a/en/images/8.1.socket.png b/en/images/8.1.socket.png
new file mode 100644
index 00000000..93dd544d
Binary files /dev/null and b/en/images/8.1.socket.png differ
diff --git a/en/images/8.2.websocket.png b/en/images/8.2.websocket.png
new file mode 100644
index 00000000..b293c653
Binary files /dev/null and b/en/images/8.2.websocket.png differ
diff --git a/en/images/8.2.websocket2.png b/en/images/8.2.websocket2.png
new file mode 100644
index 00000000..b744c634
Binary files /dev/null and b/en/images/8.2.websocket2.png differ
diff --git a/en/images/8.2.websocket3.png b/en/images/8.2.websocket3.png
new file mode 100644
index 00000000..ee769c16
Binary files /dev/null and b/en/images/8.2.websocket3.png differ
diff --git a/en/images/8.3.rest.png b/en/images/8.3.rest.png
new file mode 100644
index 00000000..0c1e5b54
Binary files /dev/null and b/en/images/8.3.rest.png differ
diff --git a/en/images/8.3.rest2.png b/en/images/8.3.rest2.png
new file mode 100644
index 00000000..b43c0804
Binary files /dev/null and b/en/images/8.3.rest2.png differ
diff --git a/en/images/8.3.rest3.png b/en/images/8.3.rest3.png
new file mode 100644
index 00000000..1f62b505
Binary files /dev/null and b/en/images/8.3.rest3.png differ
diff --git a/en/images/8.4.rpc.png b/en/images/8.4.rpc.png
new file mode 100644
index 00000000..0b3fcfe2
Binary files /dev/null and b/en/images/8.4.rpc.png differ
diff --git a/en/images/9.1.csrf.png b/en/images/9.1.csrf.png
new file mode 100644
index 00000000..54d3a434
Binary files /dev/null and b/en/images/9.1.csrf.png differ
diff --git a/en/images/cover.png b/en/images/cover.png
new file mode 100644
index 00000000..22bfab44
Binary files /dev/null and b/en/images/cover.png differ
diff --git a/en/images/ebook.jpg b/en/images/ebook.jpg
new file mode 100644
index 00000000..b2f3710d
Binary files /dev/null and b/en/images/ebook.jpg differ
diff --git a/en/images/navi1.png b/en/images/navi1.png
new file mode 100644
index 00000000..92a7669e
Binary files /dev/null and b/en/images/navi1.png differ
diff --git a/en/images/navi10.png b/en/images/navi10.png
new file mode 100644
index 00000000..94935e27
Binary files /dev/null and b/en/images/navi10.png differ
diff --git a/en/images/navi11.png b/en/images/navi11.png
new file mode 100644
index 00000000..8eb93cb1
Binary files /dev/null and b/en/images/navi11.png differ
diff --git a/en/images/navi12.png b/en/images/navi12.png
new file mode 100644
index 00000000..5bdbadfa
Binary files /dev/null and b/en/images/navi12.png differ
diff --git a/en/images/navi13.png b/en/images/navi13.png
new file mode 100644
index 00000000..c797033b
Binary files /dev/null and b/en/images/navi13.png differ
diff --git a/en/images/navi14.png b/en/images/navi14.png
new file mode 100644
index 00000000..3d9d38cc
Binary files /dev/null and b/en/images/navi14.png differ
diff --git a/en/images/navi2.png b/en/images/navi2.png
new file mode 100644
index 00000000..d18526ac
Binary files /dev/null and b/en/images/navi2.png differ
diff --git a/en/images/navi3.png b/en/images/navi3.png
new file mode 100644
index 00000000..23495893
Binary files /dev/null and b/en/images/navi3.png differ
diff --git a/en/images/navi4.png b/en/images/navi4.png
new file mode 100644
index 00000000..1b4df73a
Binary files /dev/null and b/en/images/navi4.png differ
diff --git a/en/images/navi5.png b/en/images/navi5.png
new file mode 100644
index 00000000..55788152
Binary files /dev/null and b/en/images/navi5.png differ
diff --git a/en/images/navi6.png b/en/images/navi6.png
new file mode 100644
index 00000000..74cab817
Binary files /dev/null and b/en/images/navi6.png differ
diff --git a/en/images/navi7.png b/en/images/navi7.png
new file mode 100644
index 00000000..2e1c974e
Binary files /dev/null and b/en/images/navi7.png differ
diff --git a/en/images/navi8.png b/en/images/navi8.png
new file mode 100644
index 00000000..7a52d84e
Binary files /dev/null and b/en/images/navi8.png differ
diff --git a/en/images/navi9.png b/en/images/navi9.png
new file mode 100644
index 00000000..7692e113
Binary files /dev/null and b/en/images/navi9.png differ
diff --git a/en/images/polling.png b/en/images/polling.png
new file mode 100644
index 00000000..8bd128ec
Binary files /dev/null and b/en/images/polling.png differ
diff --git a/ja/01.0.md b/ja/01.0.md
index 835e342a..b59fd295 100644
--- a/ja/01.0.md
+++ b/ja/01.0.md
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
# 1 GOの環境設定
Goの世界へようこそ、さっそく初めてみましょう!
@@ -21,3 +22,28 @@ Goはコンパイラ型言語の一種です。インタプリタ型言語の軽
## links
* [目次]()
* 次へ: [Goのインストール](<01.1.md>)
+=======
+# 1 GOの環境設定
+
+Goの世界へようこそ、さっそく初めてみましょう!
+
+Goは新しい言語です、並列処理、ガベージコレクションを備え、軽快にコンパイルできる言語です。以下のような特徴を持っています:
+
+- 一台のコンピュータ上であっという間に大型のGoプログラムをコンパイルすることができます。
+- Goはソフトウェアの構造にモデルを与えます。分析をより簡単にこなすことができ、ファイルやライブラリのincludeといったCスタイルの書き出しにありがちな部分を大幅に省くことができます。
+- Goは静的型付け言語です。型に階層の概念が無いのでユーザはその関係に気をとられることもなく、典型的なオブジェクト指向言語よりももっとライトに感じるくらいです。
+- Goは完全なガベージコレクションタイプの言語です。また、基本的な並列処理とネットワークをサポートしています。
+- Goはマルチプロセッサ対応のソフトウェアを作成できるようデザインされています。
+
+Goはコンパイラ型言語の一種です。インタプリタ型言語の軽い身のこなしと動的型付け言語の開発効率、それに静的型付け言語の安全性を兼ね備えています。また、今風のネットワークとマルチプロセッサもサポートしています。これらを実現する為には、表現力豊かで且つ軽いクラスシステム・並列処理とガベージコレクション・厳格な依存定義などを言語レベルで満たしていなければなりません。どれもライブラリやツールでは解決しきれないものです。Goはその要望に応えます。
+
+この章ではGoのインストール方法と設定についてご紹介します。
+
+## 目次
+
+
+
+## links
+ * [目次]()
+ * 次へ: [Goのインストール](<01.1.md>)
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
diff --git a/ja/01.0.md.orig b/ja/01.0.md.orig
new file mode 100644
index 00000000..b59fd295
--- /dev/null
+++ b/ja/01.0.md.orig
@@ -0,0 +1,49 @@
+<<<<<<< HEAD
+# 1 GOの環境設定
+
+Goの世界へようこそ、さっそく初めてみましょう!
+
+Goは新しい言語です、並列処理、ガベージコレクションを備え、軽快にプログラムできる言語です。以下のような特徴を持っています:
+
+- 一台のコンピュータ上であっという間に大型のGoプログラムを作り出すことができます。
+- Goはソフトウェアの構造にモデルを提供します。分析をより簡単にこなせ、Cスタイルの頭にありがちなファイルとライブラリのincludeの大部分を省くことができます。
+- Goは静的型付け言語です。型には階層がありません。このためユーザは型の定義の関係に時間をとられることなく、典型的なオブジェクト指向言語よりももっとライトに感じるくらいです。
+- Goは完全にガベージコレクションタイプの言語です。また、並列処理とネットワークを基本的にサポートしています。
+- Goはマルチプロセッサ対応のソフトウェアを作成できるようデザインされています。
+
+Goはコンパイラ型言語の一種です。インタプリタ型言語の軽い身のこなしと動的型付け言語の開発効率、それに静的型付け言語の安全性を兼ね備えています。また、モダンにネットワークとマルチプロセッサもサポートしています。これらの目標を達成するには、解決しなければならない言語上の問題がいくつかあります:表現力豊かだけれども軽いシステム、並列処理とガベージコレクション、厳格な依存定義などです。これらはライブラリやツール無しにはうまくいきません。Goもその要望に応えます。
+
+この章ではGoのインストール方法と設定について述べます。
+
+## 目次
+
+
+
+## links
+ * [目次]()
+ * 次へ: [Goのインストール](<01.1.md>)
+=======
+# 1 GOの環境設定
+
+Goの世界へようこそ、さっそく初めてみましょう!
+
+Goは新しい言語です、並列処理、ガベージコレクションを備え、軽快にコンパイルできる言語です。以下のような特徴を持っています:
+
+- 一台のコンピュータ上であっという間に大型のGoプログラムをコンパイルすることができます。
+- Goはソフトウェアの構造にモデルを与えます。分析をより簡単にこなすことができ、ファイルやライブラリのincludeといったCスタイルの書き出しにありがちな部分を大幅に省くことができます。
+- Goは静的型付け言語です。型に階層の概念が無いのでユーザはその関係に気をとられることもなく、典型的なオブジェクト指向言語よりももっとライトに感じるくらいです。
+- Goは完全なガベージコレクションタイプの言語です。また、基本的な並列処理とネットワークをサポートしています。
+- Goはマルチプロセッサ対応のソフトウェアを作成できるようデザインされています。
+
+Goはコンパイラ型言語の一種です。インタプリタ型言語の軽い身のこなしと動的型付け言語の開発効率、それに静的型付け言語の安全性を兼ね備えています。また、今風のネットワークとマルチプロセッサもサポートしています。これらを実現する為には、表現力豊かで且つ軽いクラスシステム・並列処理とガベージコレクション・厳格な依存定義などを言語レベルで満たしていなければなりません。どれもライブラリやツールでは解決しきれないものです。Goはその要望に応えます。
+
+この章ではGoのインストール方法と設定についてご紹介します。
+
+## 目次
+
+
+
+## links
+ * [目次]()
+ * 次へ: [Goのインストール](<01.1.md>)
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
diff --git a/ja/01.1.md b/ja/01.1.md
index 456b2d88..f4e57f04 100644
--- a/ja/01.1.md
+++ b/ja/01.1.md
@@ -1,6 +1,7 @@
# 1.1 Goのインストール
## 3つのインストール方法
+<<<<<<< HEAD
Goにはいくつものインストール方法があります。どれでも好きなのを選んでかまいません。ここでは3つのよくあるインストール方法をご紹介しましょう:
- ソースコードのインストール:標準的なインストール方法です。Unix系システムをよく使うユーザ、特に開発者にとってはお馴染みの方法です。
@@ -23,11 +24,33 @@ Goは[Mercurial][hg]を使ってバージョン管理を行います、まずMer
もしGoのインストールディレクトリが`$GO_INSTALL_DIR`だったとすると
hg clone -u release https://code.google.com/p/go
+=======
+Goにはいくつものインストール方法があります。どれでも好きなのを選んでかまいません。ここでは3つのよくあるインストール方法をご紹介します:
+
+- ソースコードのインストール:標準的なインストール方法です。Unix系システムをよく使うユーザ、特に開発者であれば、設定を好みに合わせて変更できます。
+- 標準パッケージのインストール:Goは便利なインストールパッケージを用意しています。Windows, Linux, Macなどのシステムをサポートしています。とりあえずさっとインストールするにはうってつけでしょう。システムのbit数に対応したインストールパッケージをダウンロードして、"Next"をたどるだけでインストールできます。 **おすすめ**
+- サードパーティツールによるインストール:現在便利なサードパーティパッケージも多くあります。たとえばUbuntuのapt-get、Macのhomebrewなどです。これらのシステムに慣れたユーザにはぴったりのインストール方法です。
+
+最後に同じシステムの中で異なるバージョンのGoをインストールする場合は、[GVM](https://github.com/moovweb/gvm)が参考になります。どうすればよいか分からない場合一番うまくやれます。
+
+## Goソースコードのインストール
+GoのソースコードにはPlan 9 CとAT&Tコンパイラを使って書かれている部分があります。もしソースコードからインストールしたい場合は、あらかじめCのコンパイルツールをインストールしておく必要があります。
+
+Macでは、Xcodeに適切なコンパイラが含まれています。
+
+Unixでは、gccなどのツールをインストールする必要があります。例えばUbuntuではターミナルで`sudo apt-get install gcc libc6-dev`を実行することでコンパイラをインストールすることができます。
+
+Windowsでは、MinGWをインストールする必要があります。その後MinGWでgccをインストールして、適切な環境変数を設定します。
+
+直接オフィシャルサイトから[ソースコードをダウンロード](http://golang.org/dl/)できます。対応する`goVERSION.src.tar.gz`のファイルをダウンロードし、`$HOME`ディレクトリに解凍してから以下のコマンドを実行します。
+
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
cd go/src
./all.bash
all.bashを実行後"ALL TESTS PASSED"が表示されると、インストール成功です。
+<<<<<<< HEAD
上記はUnixスタイルのコマンドです、Windowsではインストール方法は似ており、all.batを実行するだけです。コンパイラはMinGWのgccを使います。
その後環境変数をいくつか設定します、
@@ -59,6 +82,46 @@ Goインストールの次はシステムのbit数の判断ですので、この
WindowsシステムのユーザはWin+Rを押してcmdを実行してください。`systeminfo`と入力してエンターキーを押します。少しするとシステムの情報が現れます。"システムの種類"の一行に、"x64-based PC"と表示されていれば、64bitシステムです。もし"X86-based PC"とあれば、32bitシステムです。
Macユーザは直接64bit版を使用することをおすすめします。なぜなら、GoがサポートしているMac OS Xのバージョンはすでに32bitプロセッサをサポートしていないからです。
+=======
+上記はUnixスタイルのコマンドです、Windowsもインストール方法は似ており、`all.bat`を実行するだけです。コンパイラはMinGWのgccを使います。
+
+もしMacまたはUnixユーザであればいくつかの環境変数を設定する必要があります。再起動しても有効にしたい場合は以下のコマンドを`.bashrc`や`.zsh`に書いておきます。
+
+ export GOPATH=$HOME/gopath
+ export PATH=$PATH:$HOME/go/bin:$GOPATH/bin
+
+ファイルに書き込んだ場合は、`bash .bashrc`や`bash .zshrc`を実行してすぐに設定を有効にします。
+
+Windowsシステムの場合は、環境変数を設定する必要があります。pathにgoが存在するディレクトリを追加し、gopath変数を設定します。
+
+設定が終わり、コマンドプロンプトで`go`を入力すると、下図のような画面が表示されるはずです。
+
+
+
+図1.1 ソースコードインストール後Goコマンドを実行
+
+GoのUsage情報が表示されれば、Goのインストールは成功です:もしこのコマンドが存在しない場合は、PATH環境変数のなかにGoのインストールディレクトリが含まれているか確認してください。
+
+> GOPATHについては以降の章で詳しくご説明します
+
+## Go標準パッケージのインストール
+
+Goはさまざまなプラットホームでインストールパッケージを提供しています、これらのパッケージはデフォルトで以下のディレクトリにインストールします:/usr/local/go(Windows:c:\Go)。当然これらのインストール場所を変更することもできます、ただし変更後はあなたの環境変数を以下のように設定する必要があります:
+
+ export GOROOT=$HOME/go
+ export GOPATH=$HOME/gopath
+ export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
+
+これらのコマンドはMacやUnixユーザであれば`.bashrc`や`.zshrc`ファイルに入れておくべきでしょう。Windowsユーザであれば当然環境変数に入れておきます。
+
+### 自分の操作しているシステムが32bitか64bitか判断する方法
+
+Goのインストールにはオペレーティングシステムのbit数を判断する必要があるので、この章では先に自分のシステムの種類を確認しましょう。
+
+WindowsのユーザはWin+Rを押してcmdを実行してください。`systeminfo`と入力してエンターキーを押します。しばらくするとシステムの情報が表示されます。"システムの種類"の一行に"x64-based PC"と表示されていれば64bitシステムです。もし"X86-based PC"とあれば、32bitシステムです。
+
+Macユーザは直接64bit版を使用することをおすすめします。GoがサポートしているMac OS Xのバージョンは、すでに32bitプロセッサをサポートしていないためです。
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
LinuxユーザはTerminalで`arch`(すなわち、`uname -a`)を実行することでシステムの情報を確かめることができます。
@@ -72,6 +135,7 @@ LinuxユーザはTerminalで`arch`(すなわち、`uname -a`)を実行するこ
### Mac インストール
+<<<<<<< HEAD
[ダウンロードURL][downlink]に接続し、32bitシステムはgo1.0.3.darwin-386.pkgをダウンロードします。64bitシステムであればgo1.0.3.darwin-amd64.pkgをダウンロードします。ファイルをダブルクリックし、すべてデフォルトで「次へ」ボタンをクリックします。これでgoはあなたのシステムにインストールされました。デフォルトでPATHの中に適切な`~/go/bin`が追加されています。このとき端末を開き、`go`と入力します。
インストール成功の画像があらわれるとインストール成功です。
@@ -85,6 +149,21 @@ LinuxユーザはTerminalで`arch`(すなわち、`uname -a`)を実行するこ
今後はGoがインストールされたディレクトリを`$GO_INSTALL_DIR`と仮定します。
`tar.gz`をインストールディレクトリに解凍します:`tar zxvf go1.0.3.linux-amd64.tar.gz -C $GO_INSTALL_DIR`
+=======
+[ダウンロードURL][downlink]にアクセスし、32bitシステムはgo1.4.2.darwin-386-osx10.8.pkgをダウンロードします。64bitシステムであればgo1.4.2.darwin-amd64-osx10.8.pkgをダウンロードします。ファイルをダブルクリックし、すべてデフォルトで「次へ」ボタンをクリックします。これでgoはあなたのシステムにインストールされました。デフォルトでPATHの中に適切な`~/go/bin`が追加されています。端末を開いて`go`と入力します。
+
+インストール成功の画像が表示されればインストール成功です。
+
+もしgoのUsage情報が表示した場合は、goはすでにインストールされています。もしこのコマンドが存在しないと表示した場合は、自分のPATH環境変数の中にgoのインストールディレクトリが含まれているか確認してください。
+
+### Linux インストール
+
+[ダウンロードURL][downlink]にアクセスし、32bitシステムであればgo1.4.2.linux-386.tar.gzを、64bitシステムであればgo1.2.2.linux-amd64.tar.gzをダウンロードします。
+
+以下ではGoがインストールされたディレクトリを`$GO_INSTALL_DIR`と仮定します。
+
+`tar.gz`をインストールディレクトリに解凍します:`tar zxvf go1.4.2.linux-amd64.tar.gz -C $GO_INSTALL_DIR`
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
PATHを設定します。`export PATH=$PATH:$GO_INSTALL_DIR/go/bin`
@@ -92,6 +171,7 @@ PATHを設定します。`export PATH=$PATH:$GO_INSTALL_DIR/go/bin`

+<<<<<<< HEAD
図1.2 Linuxシステムでインストールに成功したあとgoを実行して表示される情報
もしgoのUsage情報が現れた場合は、goはすでにインストールされています。もしこのコマンドが存在しないと出てきた場合は、自分のPATH環境変数の中にgoのインストールディレクトリが含まれているか確認してください。
@@ -105,6 +185,21 @@ PATHを設定します。`export PATH=$PATH:$GO_INSTALL_DIR/go/bin`
「ファイル名を指定して実行」に `cmd` を入力することでコマンドラインツールを開きます。プロンプトで`go`と入力することで Usage 情報が確認できるか確かめることができます。`cd %GOROOT%` を入力すると、Go のインストールフォルダに入れるか確認できます。どちらも成功していれば、インストールに成功しています。
インストールに成功していなければ、環境変数 Path と GOROOT の値を確認してください。もし存在しなければアンインストールの上再インストールし、存在していればコンピュータを再起動し、上の手順を再度お試しください。
+=======
+図1.2 Linuxシステムでインストールに成功したあとgoを実行した時に表示する情報
+
+もしgoのUsage情報が表示された場合は、goはすでにインストールされています。もしこのコマンドが存在しないと出てきた場合は、自分のPATH環境変数の中にgoのインストールディレクトリが含まれているか確認してください。
+
+### Windows インストール ###
+
+[Google Code ダウンロードページ][downlink]にアクセスし、32bit の場合は名前に windows-386 を含む msi パッケージを、64bit であれば名前に windows-amd64 を含むものをダウンロードします。ダウンロード後実行しますが、デフォルトのインストールフォルダである C:\Go\を変更してはいけません。他の場所にインストールしてしまうと、あなたが書いた Go コードが実行できなくなってしまうかもしれません。インストールが終わるとデフォルトで環境変数 Path に Go のインストールフォルダの下にある bin フォルダ `C:\Go\bin\` が追加され、Go のインストールフォルダである `C:\Go\` の値が環境変数 GOROOT に追加されます。
+
+** インストールが成功しているか確認する **
+
+「ファイル名を指定して実行」に `cmd` を入力し、コマンドラインツールを開きます。プロンプトで`go`と入力することで Usage 情報が確認できるか確かめることができます。`cd %GOROOT%` を入力すると、Go のインストールフォルダに入れるか確認できます。どちらも成功していれば、インストールに成功しています。
+
+インストールに成功していなければ、環境変数 Path と GOROOT の値を確認してください。もし存在しなければアンインストールの上再インストールし、存在していればコンピュータを再起動し、上の手順を再度試してください。
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
## サードパーティツールのインストール
@@ -115,6 +210,7 @@ gvmはサードパーティが開発したGoのバージョン管理ツールで
インストールが完了したあと、goをインストールすることができます:
+<<<<<<< HEAD
gvm install go1.1
gvm use go1.1
@@ -122,6 +218,15 @@ gvmはサードパーティが開発したGoのバージョン管理ツールで
gvm use go1.1 --default
上のコマンドを実行したあと、GOPATH、GOROOTなどの環境変数は自動的に設定されます。これで、直接利用することができます。
+=======
+ gvm install go1.4.2
+ gvm use go1.4.2
+
+下のコマンドで、毎回gvm useをコールする手間を省くことができます:
+ gvm use go1.4.2 --default
+
+上のコマンドを実行したあと、GOPATH、GOROOTなどの環境変数が自動的に設定されます。これで、直接利用することができます。
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
### apt-get
Ubuntuは現在最も多く利用されているLinuxデスクトップシステムです。`apt-get`コマンドでソフトウェア・パッケージを管理します。下のコマンドでGoをインストールすることができます、今後のため`git`と`mercurial`もインストールしておくべきでしょう:
@@ -132,7 +237,11 @@ Ubuntuは現在最も多く利用されているLinuxデスクトップシステ
sudo apt-get install golang-stable git-core mercurial
### homebrew
+<<<<<<< HEAD
homebrewはMacシステムで現在最も使用されているソフトウェア管理ツールです。現在Goをサポートしており、以下のコマンドでGoを直接インストールすることができます。今後のため`git`と`mercurial`もインストールしておくべきでしょう:
+=======
+homebrewはMacで現在最も使用されているソフトウェア管理ツールです。現在Goをサポートしており、以下のコマンドでGoを直接インストールすることができます。今後のため`git`と`mercurial`もインストールしておくべきでしょう:
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
brew update && brew upgrade
brew install go
@@ -145,5 +254,9 @@ homebrewはMacシステムで現在最も使用されているソフトウェア
* 前へ: [Goの環境設定](<01.0.md>)
* 次へ: [GOPATHとワーキングディレクトリ](<01.2.md>)
+<<<<<<< HEAD
[downlink]: http://code.google.com/p/go/downloads/list "Goインストールパッケージダウンロード"
[hg]: http://mercurial.selenic.com/downloads/ "Mercurialダウンロード"
+=======
+[downlink]:http://golang.org/dl/ "Goインストールパッケージダウンロード"
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
diff --git a/ja/01.3.md b/ja/01.3.md
index ee84f996..91d36414 100644
--- a/ja/01.3.md
+++ b/ja/01.3.md
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
# 1.3 Goのコマンド
## Goのコマンド
@@ -114,3 +115,209 @@
* [目次]()
* 前へ: [GOPATHとワーキングディレクトリ](<01.2.md>)
* 次へ: [Goの開発ツール](<01.4.md>)
+=======
+# 1.3 Goのコマンド
+
+## Goのコマンド
+
+ Go言語は完全なコマンド操作ツールセットを持つ言語です。コマンドラインで`go`を実行することでそれらを確認することができます:
+
+ 
+
+図1.3 Goコマンドで詳細情報を表示
+
+ これらのコマンドは我々が普段コードを書いている時に非常に役立つものです。次に普段使用するコマンドを理解していきましょう。
+
+## go build
+
+ このコマンドは主にソースコードのコンパイルに用いられます。パッケージのコンパイル作業中、もし必要であれば、同時に関連パッケージもコンパイルすることができます。
+
+ - もし普通のパッケージであれば、我々が1.2章で書いた`mypath`パッケージのように、`go build`を実行したあと、何のファイルも生成しません。もし`$GOPATH/pkg`の下に対応するファイルを生成する必要があれば、`go install`を実行してください。
+
+ - もしそれが`main`パッケージであれば、`go build`を実行したあと、カレントディレクトリの下に実行可能ファイルが生成されます。もし`$GOPATH/bin`の下に対応するファイルを生成する必要があれば、`go install`を実行するか、`go build- o パス/a.exe`を実行してください。
+
+ - もしあるプロジェクトディレクトリに複数のファイルがある場合で、単一のファイルのみコンパイルしたい場合は、`go build`を実行する際にファイル名を追加することができます。例えば`go build a.go`です。`go build`コマンドはデフォルトでカレントディレクトリにある全てのgoファイルをコンパイルしようと試みます。
+
+ - コンパイル後に出力されるファイル名を指定することもできます。1.2章の`mathapp`アプリケーションでは`go build -o astaxie.exe`と指定できます。デフォルトはpackage名(mainパッケージではない)になるか、ソースファイルのファイル名(mainパッケージ)になります。
+
+ (注:実際はpackage名は[Go言語の規格](https://golang.org/ref/spec)においてコード中の"package"に続く名前になります。この名前はファイル名と異なっていても構いません。デフォルトで生成される実行可能ファイル名はディレクトリ名。
+
+ - go buildはディレクトリ内の"\_"または"."ではじまるgoファイルを無視します。
+
+ - もしあなたのソースコードが異なるオペレーティングシステムに対応する場合は異なる処理が必要となります。ですので異なるオペレーティングシステムの名称にもとづいてファイルを命名することができます。例えば配列を読み込むプログラムがあったとして、異なるオペレーティングシステムに対して以下のようなソースファイルがあるかもしれません。
+
+ array_linux.go
+ array_darwin.go
+ array_windows.go
+ array_freebsd.go
+
+ `go build`の際、システム名の末尾のファイルから選択的にコンパイルすることができます(Linux、Darwin、Windows、Freebsd)
+
+引数の紹介
+
+- `-o` 出力するファイル名を指定します。パスが含まれていても構いません。例えば `go build -o a/b/c`
+- `-i` パッケージをインストールします。コンパイル+`go install`
+- `-a` すでに最新であるパッケージを全て更新します。ただし標準パッケージには適用されません。
+- `-n` 実行が必要なコンパイルコマンドを出力します。ただし、実行はされません。これにより低レイヤーで一体何が実行されているのかを簡単に知る事ができます。
+- `-p n` マルチプロセスで実行可能なコンパイル数を指定します。デフォルトはCPU数です。
+- `-race` コンパイルを行う際にレースコンディションの自動検出を行います。64bitマシンでのみ対応しています。
+- `-v` 現在コンパイル中のパッケージ名を出力します。
+- `-work` コンパイル時の一時ディレクトリ名を出力し、すでに存在する場合は削除しなくなります。
+- `-x` 実行しているコマンドを出力します。`-n`の結果とよく似ていますが、この場合は実行します。
+- `-ccflags 'arg list'` オプションを5c, 6c, 8cに渡してコールします。
+- `-compiler name` コンパイラを指定します。gccgoか、またはgcです。
+- `-gccgoflags 'arg list'` オプションをgccgoリンカに渡してコールします。
+- `-gcflags 'arg list'` オプションを5g, 6g, 8gに渡してコールします
+- `-installsuffix suffix` デフォルトのインストールパッケージと区別するため、このサフィックスを利用して依存するパッケージをインストールします。`-race`をオプションに指定した場合はデフォルトで`-installsuffix race`が有効になっています。`-n`コマンドで確かめることができますよ。
+- `-ldflags 'flag list'` オプションを5l, 6l, 8lに渡してコールします。
+- `-tags 'tag list'` コンパイル時にこれらのtagをつけることができます。tagの詳細な制限事項に関しては [Build Constraints](http://golang.org/pkg/go/build/) を参考にして下さい。
+
+## go clean
+
+ このコマンドは現在のソースコードパッケージと関連するソースパッケージのなかでコンパイラが生成したファイルを取り除く操作を行います。これらのファイルはすなわち:
+
+ _obj/ 旧objectディレクトリ、MakeFilesが作成する。
+ _test/ 旧testディレクトリ,Makefilesが作成する。
+ _testmain.go 旧gotestファイル,Makefilesが作成する。
+ test.out 旧testログ,Makefilesが作成する。
+ build.out 旧testログ,Makefilesが作成する。
+ *.[568ao] objectファイル,Makefilesが作成する。
+
+ DIR(.exe) go buildが作成する。
+ DIR.test(.exe) go test -cが作成する。
+ MAINFILE(.exe) go build MAINFILE.goが作成する。
+ *.so SWIG によって生成される。
+
+ 私は基本的にこのコマンドを使ってコンパイルファイルを掃除します。ローカルでテストを行う場合これらのコンパイルファイルはシステムと関係があるだけで、コードの管理には必要ありません。
+
+ $ go clean -i -n
+ cd /Users/astaxie/develop/gopath/src/mathapp
+ rm -f mathapp mathapp.exe mathapp.test mathapp.test.exe app app.exe
+ rm -f /Users/astaxie/develop/gopath/bin/mathapp
+
+引数紹介
+
+- `-i` go installがインストールするファイル等の、関係するインストールパッケージと実行可能ファイルを取り除きます。
+- `-n` 実行する必要のある削除コマンドを出力します。ただし実行はされません。これにより低レイヤで何が実行されているのかを簡単に知ることができます。
+- `-r` importによってインポートされたパッケージを再帰的に削除します。
+- `-x` 実行される詳細なコマンドを出力します。`-n`出力の実行版です。
+
+## go fmt
+
+ 読者にC/C++の経験があればご存知かもしれませんが、コードにK&Rスタイルを選択するかANSIスタイルを選択するかは常に論争となっていました。goでは、コードに標準のスタイルがあります。すでに培われた習慣やその他が原因となって我々は常にANSIスタイルまたはその他のより自分にあったスタイルでコードを書いて来ました。これは他の人がコードを閲覧する際に不必要な負担を与えます。そのためgoはコードのスタイルを強制し(例えば左大括弧はかならず行末に置く)、このスタイルに従わなければコンパイルが通りません。整形の時間の節約するため、goツールは`go fmt`コマンドを提供しています。これはあなたの書いたコードを整形するのに役立ちます。あなたの書いたコードは標準のスタイルに修正されますが、我々は普段このコマンドを使いません。なぜなら開発ツールには一般的に保存時に自動的に整形を行ってくれるからです。この機能は実際には低レイヤでは`go fmt`を呼んでいます。この次の章で2つのツールをご紹介しましょう。この2つのツールはどれもファイルを保存する際に`go fmt`機能を自動化させます。
+
+go fmtコマンドを使うにあたって実際にはgofmtがコールますが、-wオプションが必要になります。さもなければ、整形結果はファイルに書き込まれません。gofmt -w -l src、ですべての項目を整形することができます。
+
+go fmtはgofmtの上位レイヤーのパッケージされたコマンドです。より個人的なフォーマットスタイルが欲しい場合は [gofmt](http://golang.org/cmd/gofmt/) を参考にしてください。
+
+gofmtの引数紹介
+
+- `-l` フォーマットする必要のあるファイルを表示します。
+- `-w` 修正された内容を標準出力に書き出すのではなく、直接そのままファイルに書き込みます。
+- `-r` “a[b:len(a)] -> a[b:]”のような重複したルールを追加します。大量に変換を行う際に便利です。
+- `-s` ファイルのソースコードを簡素化します。
+- `-d` ファイルに書き込まず、フォーマット前後のdiffを表示します。デフォルトはfalseです。
+- `-e` 全ての文法エラーを標準出力に書き出します。もしこのラベルを使わなかった場合は異なる10行のエラーまでしか表示しません。
+- `-cpuprofile` テストモードをサポートします。対応するするcpufile指定のファイルに書き出します。
+
+## go get
+
+ このコマンドは動的にリモートコードパッケージを取得するために用いられます。現在BitBucket、GitHub、Google CodeとLaunchpadをサポートしています。このコマンドは内部で実際には2ステップの操作に分かれます:第1ステップはソースコードパッケージのダウンロード、第2ステップは`go install`の実行です。ソースコードパッケージのダウンロードを行うgoツールは異なるドメインにしたがって自動的に異なるコードツールを用います。対応関係は以下の通りです:
+
+ BitBucket (Mercurial Git)
+ GitHub (Git)
+ Google Code Project Hosting (Git, Mercurial, Subversion)
+ Launchpad (Bazaar)
+
+ そのため、`go get`を正常に動作させるためには、あらかじめ適切なソースコード管理ツールがインストールされていると同時にこれらのコマンドがあなたのPATHに入っていなければなりません。実は`go get`はカスタムドメインの機能をサポートしています。具体的な内容は`go help remote`を参照ください。
+
+引数紹介:
+
+- `-d` ダウンロードするだけでインストールしません。
+- `-f` `-u`オプションを与えた時だけ有効になります。`-u`オプションはimportの中の各パッケージが既に取得されているかを検証しなくなります。ローカルにforkしたパッケージに対して特に便利です。
+- `-fix` ソースコードをダウンロードするとまずfixを実行してから他の事を行うようになります。
+- `-t` テストを実行する為に必要となるパッケージも同時にダウンロードします。
+- `-u` パッケージとその依存パッケージをネットワークから強制的に更新します。
+- `-v` 実行しているコマンドを表示します。
+
+## go install
+
+ このコマンドは実際には内部で2ステップの操作に分かれます。第1ステップはリザルトファイルの生成(実行可能ファイルまたはaパッケージ)、第2ステップはコンパイルし終わった結果を`$GOPATH/pkg`または`$GOPATH/bin`に移動する操作です。
+
+引数は`go build`のコンパイルオプションをサポートしています。みなさんは`-v`オプションだけ覚えていただければ結構です。これにより低レイヤーの実行状況をいつでも確認することができます。
+
+## go test
+
+ このコマンドを実行すると、ソースコードディレクトリ以下の`*_test.go`ファイルが自動的にロードされ、テスト用の実行可能ファイルが生成/実行されます。出力される情報は以下のようなものになります
+
+ ok archive/tar 0.011s
+ FAIL archive/zip 0.022s
+ ok compress/gzip 0.033s
+ ...
+
+ デフォルトの状態で、オプションを追加する必要はありません。自動的にあなたのソースコードパッケージ以下のすべてのtestファイルがテストされます。もちろんオプションを追加しても構いません。詳細は`go help testflag`を確認してください。
+
+ここでは良く使われるオプションについてご紹介します:
+
+- `-bench regexp` 指定したbenchmarksを実行します。例えば `-bench=.`
+- `-cover` テストカバー率を起動します。
+- `-run regexp` regexpにマッチする関数だけを実行します。例えば `-run=Array` とすることで名前がArrayから始まる関数だけを実行します。
+- `-v` テストの詳細なコマンドを表示します。
+
+## go tool
+`go tool`にはいくつものコマンドがあります。ここでは2つだけご紹介します。fixと vetです。
+
+- `go tool fix .` は以前の古いバージョンを新しいバージョンに修復します。例えば、go1以前の古いバージョンのコードをgo1に焼き直したり、APIを変化させるといったことです。
+- `go tool vet directory|files` はカレントディレクトリのコードが正しいコードであるか分析するために使用されます。例えばfmt.Printfをコールする際のオプションが正しくなかったり、関数の途中でreturnされたことによって到達不可能なコードが残っていないかといった事です。
+
+## go generate
+このコマンドはGo1.4になって初めてデザインされました。コンパイル前にある種のコードを自動で生成する目的に使用されます。`go generate`と`go build`は全くことなるコマンドです。ソースコード中の特殊なコメントをを分析することで、対応するコマンドを実行します。これらのコマンドは明確に何の依存も存在しません。この機能を使用する場合には必ず次の事を念頭に置いてください。`go generate`はあなたの為に存在します。あなたのパッケージを使用する誰かの為のものではありません。これはある一定のコードを生成するためにあります。
+
+簡単な例をご紹介します。例えば我々が度々`yacc`を使ってコードを生成していたとしましょう。その場合以下のようなコマンドをいつも使用することになります:
+
+ go tool yacc -o gopher.go -p parser gopher.y
+
+-o は出力するファイル名を指定します。-pはパッケージ名を指定します。これは単独のコマンドであり、もし`go generate`によってこのコマンドを実行する場合は当然ディレクトリの任意の`xxx.go`ファイルの任意の位置に以下のコメントを一行追加します。
+
+ //go:generate go tool yacc -o gopher.go -p parser gopher.y
+
+注意すべきは、`//go:generate`に空白が含まれていない点です。これは固定のフォーマットで、ソースファイルを舐める時はこのフォーマットに従って判断されます。
+
+これにより以下のようなコマンドによって、生成・コンパイル・テストを行うことができます。もし`gopher.y`ファイルに修正が発生した場合は、再度`go generate`を実行することでファイルを上書きすればよいことになります。
+
+ $ go generate
+ $ go build
+ $ go test
+
+
+## godoc
+
+Go1.2バージョンより以前は`go doc`コマンドがサポートされていましたが、今後は全てgodocコマンドに移されました。このようにインストールします`go get golang.org/x/tools/cmd/godoc`
+
+多くの人がgoにはサードパーティのドキュメントが必要無いと謳っています。なぜなら例えばchmハンドブックのように(もっとも私はすでに[chmマニュアル](https://github.com/astaxie/godoc)を作っていますが)、この中にはとても強力なドキュメントツールが含まれているからです。
+
+ どのように対応するpackageのドキュメントを確認すればよいでしょうか?
+ 例えばbuiltinパッケージであれば、`go doc builtin`と実行します。
+ もしhttpパッケージであれば、`go doc net/http`と実行してください。
+ パッケージの中の関数を確認する場合は`godoc fmt Printf`としてください。
+ 対応するコードを確認する場合は、`godoc -src fmt Printf`とします。
+
+ コマンドラインでコマンドを実行します。 godoc -http=:ポート番号 例えば`godoc -http=:8080`として、ブラウザで`127.0.0.1:8080`を開くと、golang.orgのローカルのcopy版を見ることができます。これを通してpkgドキュメントなどの他の内容を確認することができます。もしあなたがGOPATHを設定されていれば、pkgカテゴリの中で、標準パッケージのドキュメントのみならず、ローカルの`GOPATH`のすべての項目に関連するドキュメントをリストアップすることができます。これはグレートファイアーウォールの中にいるユーザにとっては非常にありがたい選択です。
+
+## その他のコマンド
+
+ goは他にも様々なツールを提供しています。例えば以下のツール
+
+ go version はgoの現在のバージョンを確認します。
+ go env は現在のgoの環境変数を確認します。
+ go list は現在インストールされている全てのpackageをリストアップします。
+ go run はGoプログラムのコンパイルと実行を行います。
+
+これらのツールはまだ多くのオプションがあり、ひとつひとつはご紹介しませんが、ユーザは`go help コマンド`で更に詳しいヘルプ情報を取得することができます。
+
+
+## links
+ * [目次]()
+ * 前へ: [GOPATHとワーキングディレクトリ](<01.2.md>)
+ * 次へ: [Goの開発ツール](<01.4.md>)
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
diff --git a/ja/04.0.md b/ja/04.0.md
index abe37f7f..2d353dc4 100644
--- a/ja/04.0.md
+++ b/ja/04.0.md
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
# 4 フォーム
フォームは我々が普段Webアプリケーションを書く時によく使うツールです。フォームを通して便利にユーザにサーバとデータをやり取りさせることができます。以前にWeb開発をしたことのあるユーザにとってはフォームはとてもお馴染みのものです。しかしC/C++のプログラマからすると、少々ばかり門外漢かもしれません。フォームとは一体何でしょうか?
@@ -23,3 +24,30 @@ HTTPプロトコルはステートレスなプロトコルです。ではどの
* [目次]()
* 前へ: [第三章まとめ](<03.5.md>)
* 次へ: [フォームの入力を処理する](<04.1.md>)
+=======
+# 4 フォーム
+
+フォームは普段Webアプリケーションを書く時によく使われるツールです。フォームを通して便利にユーザにサーバとデータをやり取りさせることができます。以前にWeb開発をしたことのあるユーザにとってはフォームはとてもお馴染みのものです。しかしC/C++のプログラマからすると少々取っ付きにくいかもしれません。フォームとは一体何でしょうか?
+
+フォームは表の要素を含むエリアです。フォームの要素はユーザがフォームの中で(例えば、テキストフィールド、コンボボックス、チェックボックス、セレクトボックス等です。)情報を入力する要素です。フォームはフォームタグ(\
+
+Goではformの処理に簡単な方法が既に用意されています。Requestの中にformを専門に処理するものがあり、簡単にWeb開発に利用できます。4.1節の中でGoがどのようにフォームの入力を処理するかご説明します。いかなるユーザの入力も信用はできないので、これらの入力に対し検証を行う必要があります。4.2節では一般的にどのように検証を行うか、細かいデモンストレーションを行います。
+
+HTTPプロトコルはステートレスなプロトコルです。ではどのようにして一人のユーザを同定するのでしょうか?また、フォームが複数回送信されてしまわないように保証するにはどうするのでしょうか?4.3と4.4節ではcookie(cookieはクライアントに保存される情報です。HTTP Headerを通してサーバーとやり取りされます)等をより詳しくご紹介します。
+
+フォームにはもうひとつファイルをアップロードできるという大きな機能があります。Goはファイルのアップロードをどのように処理しているのでしょうか?大きなファイルをアップロードする際効率よく処理するにはどうすればよいでしょうか?4.5節ではGoによるファイルのアップロード処理の知識を一緒に勉強します。
+
+## 目次
+
+
+## links
+ * [目次]()
+ * 前へ: [第三章まとめ](<03.5.md>)
+ * 次へ: [フォームの入力を処理する](<04.1.md>)
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
diff --git a/ja/04.4.md b/ja/04.4.md
index 047bca31..4965432e 100644
--- a/ja/04.4.md
+++ b/ja/04.4.md
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
# 4.4 フォームの複数回送信の防止
以前どこかのBBSやブログでご覧になったかもしれませんが、一つのスレや文章の後でいくつもの重複が記録されていることがあります。これらの大多数はユーザが複数回書き込みフォームを送信してしまったことによるものです。様々な原因で、ユーザはよくフォームを複数回送信してしまいます。通常はマウスの誤操作によるもので、送信ボタンをダブルクリックしてしまったり、一旦送信した内容を再度修正しようとして、ブラウザの戻るボタンを押した後に次へボタンではなくまた送信ボタンを押してしまうことによるものです。当然、故意によるものもあります。- - 例えばネット上のアンケート調査やくじ引きにおいて重複して投票するなどです。では、どのようにしてユーザが同じ内容のフォームの送信を行うことを効果的に防げるのでしょうか?
@@ -56,3 +57,63 @@ tokenはすでに出力値を持っていますので、連続してページを
* [目次]()
* 前へ: [クロスサイトスクリプティングの予防](<04.3.md>)
* 次へ: [ファイルのアップロード処理](<04.5.md>)
+=======
+# 4.4 フォームの複数回送信の防止
+
+これまでにどこかのBBSやブログでご覧になったことがあるかもしれませんが、一つのスレッドや文章の後でいくつもの重複が記録されていることがあります。これらの大多数はユーザが複数回書き込みフォームを送信してしまったことによるものです。様々な原因でユーザはよくフォームを複数回送信してしまいます。通常はマウスの誤操作によるもので、送信ボタンをダブルクリックしてしまったり、一旦送信した内容を再度修正しようとして、ブラウザの戻るボタンを押した後に次へボタンではなくまた送信ボタンを押してしまうことによるものです。当然、故意によるものもあります。- - 例えばネット上のアンケート調査やくじ引きにおいて重複して投票するなどです。では、どのようにしてユーザが同じ内容のフォームの送信を行うことを効果的に防げるのでしょうか?
+
+解決方法はフォームの中にユニークな値を持ったhiddenフィールドを追加することです。フォームを検証する際、このユニークな値を持ったフォームがすでに送信されているかどうか検証します。もしすでに送信されていれば、二回目の送信を拒絶します。そうでなければフォームに対して処理ロジックを行います。また、もしAjax形式で送信するフォームだった場合、フォームが送信された後javascriptによってフォームの送信ボタンを禁止します。
+
+4.2節の例を改良してみましょう:
+
+ サッカー
+ バスケットボール
+ テニス
+ ユーザ名:
+ パスワード:
+
+
+
+テンプレートの中に`token`というhiddenフィールドを追加しました。この値にはMD5(タイムスタンプ)によってユニークな値を割り当てます。この値をサーバに保存することで(sessionによるコントロールは6章でどのように保存するか解説します)フォームが送信される際の判定に使うことができます。
+
+ func login(w http.ResponseWriter, r *http.Request) {
+ fmt.Println("method:", r.Method) //リクエストを受け取る方法
+ if r.Method == "GET" {
+ crutime := time.Now().Unix()
+ h := md5.New()
+ io.WriteString(h, strconv.FormatInt(crutime, 10))
+ token := fmt.Sprintf("%x", h.Sum(nil))
+
+ t, _ := template.ParseFiles("login.gtpl")
+ t.Execute(w, token)
+ } else {
+ //リクエストはログインデータです。ログインのロジックを実行して判断します。
+ r.ParseForm()
+ token := r.Form.Get("token")
+ if token != "" {
+ //tokenの合法性を検証します。
+ } else {
+ //tokenが存在しなければエラーを出します。
+ }
+ fmt.Println("username length:", len(r.Form["username"][0]))
+ fmt.Println("username:", template.HTMLEscapeString(r.Form.Get("username"))) //サーバ側に出力します。
+ fmt.Println("password:", template.HTMLEscapeString(r.Form.Get("password")))
+ template.HTMLEscape(w, []byte(r.Form.Get("username"))) //クライアントに出力します。
+ }
+ }
+
+出力されるページのソースは以下の通り:
+
+
+
+図4.4 tokenを追加した後クライアントが出力するソース情報
+
+tokenはすでに出力値を持っていますので連続してページを更新することができます。この値が次々と変化するのがお分かりいただけるかと思います。このように毎回formが表示される時にユニークになるよう保証します。ユーザが送信するフォームは唯一性が保持されます。
+
+この解決方法は悪意の無い攻撃に対しても防止することができます。また悪意のあるユーザに対してもしばらく効果があります。その後、ユーザーにこの悪意のある動機を捨てさせることができなかった場合は更に複雑な作業が必要となります。
+
+## links
+ * [目次]()
+ * 前へ: [クロスサイトスクリプティングの予防](<04.3.md>)
+ * 次へ: [ファイルのアップロード処理](<04.5.md>)
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
diff --git a/ja/09.6.md b/ja/09.6.md
index 615cf2cd..1f0a0ff7 100644
--- a/ja/09.6.md
+++ b/ja/09.6.md
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
# 9.6 データを暗号化/復号する
前の節でどのようにしてパスワードを保存するかご紹介しました。しかしあるときには、慎重に扱うべきデータを暗号化して保存し、将来のあるときにいつでもそれらを復元したい場合があります。この時双方向暗号化アルゴリズムを使って我々の要求を満たさなければなりません。
@@ -120,3 +121,90 @@ Go言語の`crypto`では双方向暗号の高度な暗号化/復号パッケー
* [目次]()
* 前へ: [パスワードの保存](<09.5.md>)
* 次へ: [まとめ](<09.7.md>)
+=======
+# 9.6 データを暗号化/復号する
+前の節では安全なパスワードの保存の仕方を説明してきました。しかしあるときには、既にデータベースに保存されている、プライバシーに関わる暗号化されたデータを修正する必要があるかもしれません。データを復号することが必要な時は、既に述べた1方向ハッシュ関数の代わりに、対称鍵暗号アルゴリズムを使うべきです。
+
+## 高度な暗号化/復号
+
+Go言語の`crypto`では対称鍵暗号アルゴリズムをサポートしています。二種類の高度暗号化モジュールがあります。
+
+- `crypto/aes`パッケージ:AES(Advanced Encryption Standard)は、Rijndael暗号化アルゴリズムとも呼ばれます。アメリカの連邦政府が採用しているブロック暗号の標準です。
+- `crypto/des`パッケージ:DES(Data Encryption Standard)は対称鍵暗号の標準です。これは現在最も広く使用されている鍵システムです。特に金融データのセキュリティの保護で使われています。かつてアメリカ連邦政府の暗号化のスタンダードでしたがすでにAESにとってかわられています。
+
+これら2つのアルゴリズムは使用方法が似ていますので、以下ではaesパッケージを使ってどのようにこれらのパッケージを使うのかを説明していきたいと思います。
+
+ package main
+
+ import (
+ "crypto/aes"
+ "crypto/cipher"
+ "fmt"
+ "os"
+ )
+
+ var commonIV = []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}
+
+ func main() {
+ // 暗号化したい文字列
+ plaintext := []byte("My name is Astaxie")
+ // 暗号化された文字列を渡すと、plaintは渡された文字列になります。
+ if len(os.Args) > 1 {
+ plaintext = []byte(os.Args[1])
+ }
+
+ // aesの暗号化文字列
+ key_text := "astaxie12798akljzmknm.ahkjkljl;k"
+ if len(os.Args) > 2 {
+ key_text = os.Args[2]
+ }
+
+ fmt.Println(len(key_text))
+
+ // 暗号化アルゴリズムaesを作成
+ c, err := aes.NewCipher([]byte(key_text))
+ if err != nil {
+ fmt.Printf("Error: NewCipher(%d bytes) = %s", len(key_text), err)
+ os.Exit(-1)
+ }
+
+ // 暗号化文字列
+ cfb := cipher.NewCFBEncrypter(c, commonIV)
+ ciphertext := make([]byte, len(plaintext))
+ cfb.XORKeyStream(ciphertext, plaintext)
+ fmt.Printf("%s=>%x\n", plaintext, ciphertext)
+
+ // 復号文字列
+ cfbdec := cipher.NewCFBDecrypter(c, commonIV)
+ plaintextCopy := make([]byte, len(plaintext))
+ cfbdec.XORKeyStream(plaintextCopy, ciphertext)
+ fmt.Printf("%x=>%s\n", ciphertext, plaintextCopy)
+ }
+
+
+上では`aes.NewCipher`(引数keyはかならず16、24または32桁の[]byteとなります。それぞれAES-128, AES-192とAES-256アルゴリズムに対応します。)関数をコールすると`cipher.Block`インターフェースを返します。このインターフェースは3つの機能を実現します:
+
+ type Block interface {
+ // BlockSize returns the cipher's block size.
+ BlockSize() int
+
+ // Encrypt encrypts the first block in src into dst.
+ // Dst and src may point at the same memory.
+ Encrypt(dst, src []byte)
+
+ // Decrypt decrypts the first block in src into dst.
+ // Dst and src may point at the same memory.
+ Decrypt(dst, src []byte)
+ }
+
+この3つの関数は暗号化/復号操作を実装します。詳細な操作はGoのドキュメントをご覧ください。
+
+## まとめ
+この節ではいくつかの暗号化アルゴリズムを紹介しました。これらのアルゴリズムは、Webアプリケーションにおける暗号化/復号の必要に応じて異なる方法で使用することができます。基本的な安全性のみを必要とするアプリケーションではAESアルゴリズムを使うことが勧められます。
+
+
+## links
+ * [目次]()
+ * 前へ: [パスワードの保存](<09.5.md>)
+ * 次へ: [まとめ](<09.7.md>)
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
diff --git a/ja/README.md b/ja/README.md
index c06d764d..f9f1afb4 100644
--- a/ja/README.md
+++ b/ja/README.md
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
<<<<<<< 380a8ee74c41759d8189ad553423467994187253
# Go Web プログラミング
Webプログラミングが好きでGo webプログラミングを書きました。皆さんに気にいってもらえれば幸いです。
@@ -5,3 +6,7 @@ Webプログラミングが好きでGo webプログラミングを書きまし
# Go Web プログラミング
Webプログラミングが好きでGo webプログラミングを書きました。皆さんに気にいってもらえれば幸いです。
>>>>>>> update the structure for gitbook
+=======
+# Go Web プログラミング
+Webプログラミングが好きでGo webプログラミングを書きました。皆さんに気にいってもらえれば幸いです。
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
diff --git a/ja/images/alipay.png b/ja/images/alipay.png
new file mode 100644
index 00000000..2bac3531
Binary files /dev/null and b/ja/images/alipay.png differ
diff --git a/pt-br/code/src/apps/ch.1.2/main.go b/pt-br/code/src/apps/ch.1.2/main.go
index e840a1f0..6ead84e6 100644
--- a/pt-br/code/src/apps/ch.1.2/main.go
+++ b/pt-br/code/src/apps/ch.1.2/main.go
@@ -1,7 +1,14 @@
+<<<<<<< HEAD
// Código de exemplo para o Capítulo 1.2 do "Build Web Application with Golang"
// Propósito: Execute este arquivo para verificar se o seu workspace está corretamente configurado.
// Para executar, navegue até o diretório onde ele estiver salvo e digite no console `go run main.go`
// Se o texto "Hello World" não aparecer, então configure seu ambiente novamente.
+=======
+// Example code for Chapter 1.2 from "Build Web Application with Golang"
+// Purpose: Run this file to check if your workspace is setup correctly.
+// To run, navigate to the current directory in a console and type `go run main.go`
+// If the text "Hello World" isn't shown, then setup your workspace again.
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
package main
import (
diff --git a/ru/04.6.md b/ru/04.6.md
index cf270cb9..d97c7bd0 100644
--- a/ru/04.6.md
+++ b/ru/04.6.md
@@ -1,6 +1,12 @@
+<<<<<<< HEAD
# 4.6 Итоги главы
В этой главе мы изучили основные моменты того, как работать с данными в Go, посредством нескольких примеров, таких как обработка входа пользователей и загрузка файлов. Мы также заострили внимание на том, что проверка данных крайне важна для безопасности сайта, а также посвятили одну секцию тому, как фильтровать входные данные посредством регулярных выражений.
+=======
+# 4.6 Итоги раздела
+
+В этом разделе мы изучили основные моменты того, как работать с данными в Go, посредством нескольких примеров, таких как обработка входа пользователей и загрузка файлов. Мы также заострили внимание на том, что проверка данных крайне важна для безопасности сайта, а также посвятили одну секцию тому, как фильтровать входные данные посредством регулярных выражений.
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
Я надеюсь, что теперь Вы больше знаете о процессе коммуникации между клиентом и сервером.
diff --git a/ru/preface.md b/ru/preface.md
index 074031e2..76103dae 100644
--- a/ru/preface.md
+++ b/ru/preface.md
@@ -13,11 +13,19 @@
- 2.6. [Интерфейсы](02.6.md)
- 2.7. [Многопоточность](02.7.md)
- 2.8. [Итоги раздела](02.8.md)
+<<<<<<< HEAD
- 3.[Основы Веба](03.0.md)
- 3.1. [Принципы работы веб](03.1.md)
- 3.2. [Создание простого веб-сервера](03.2.md)
- 3.3. [Как Go работает с веб](03.3.md)
- 3.4. [Внутренний мир пакета http](03.4.md)
+=======
+- 3.[Web foundation](03.0.md)
+ - 3.1. [Web working principles](03.1.md)
+ - 3.2. [Build a simple web server](03.2.md)
+ - 3.3. [How Go works with web](03.3.md)
+ - 3.4. [Get into http package](03.4.md)
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
- 3.5. [Итоги раздела](03.5.md)
- 4.[Пользовательские формы](04.0.md)
- 4.1. [Работа с формами](04.1.md)
@@ -26,7 +34,11 @@
- 4.4. [Дублирование отправки](04.4.md)
- 4.5. [Загрузка файлов](04.5.md)
- 4.6. [Итоги раздела](04.6.md)
+<<<<<<< HEAD
- 5.[Базы данных](05.0.md)
+=======
+- 5.[Database](05.0.md)
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
- 5.1. [database/sql interface](05.1.md)
- 5.2. [MySQL](05.2.md)
- 5.3. [SQLite](05.3.md)
diff --git a/tr/02.3.md b/tr/02.3.md
index fa1a0fc0..75d173c4 100644
--- a/tr/02.3.md
+++ b/tr/02.3.md
@@ -13,18 +13,31 @@ Akış kontrolü programcılıkta en büyük icattır. Onun sayesinde, basit kon
`if` şartları için Go da paranteze gerek yoktur.
if x > 10 {
+<<<<<<< HEAD
fmt.Println("x 10'dan büyüktür")
} else {
fmt.Println("x 10'dan küçük veya eşittir")
+=======
+ fmt.Println("x is greater than 10")
+ } else {
+ fmt.Println("x is less than or equal to 10")
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
}
Go da çok işe yarar bir `if` kullanımı, şart ifadesinden önce değişken tanımlama yapmaktır. Bu değişkenin kapsamı sadece `if` blok alanı içerisinde geçerlidir.
// x'i tanımla, ve 10 dan büyük olup olmadığını kontrol et
+<<<<<<< HEAD
if x := islenenDeger(); x > 10 {
fmt.Println("x 10'dan büyüktür")
} else {
fmt.Println("x 10'dan küçüktür")
+=======
+ if x := computedValue(); x > 10 {
+ fmt.Println("x is greater than 10")
+ } else {
+ fmt.Println("x is less than 10")
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
}
// Bu satır derlenemez
@@ -32,12 +45,21 @@ Go da çok işe yarar bir `if` kullanımı, şart ifadesinden önce değişken t
Birden fazla şart için `if-else` kullanın.
+<<<<<<< HEAD
if sayı == 3 {
fmt.Println("Sayı 3'e eşittir")
} else if integer < 3 {
fmt.Println("Sayı 3'ten küçüktür")
} else {
fmt.Println("Sayı 3'ten büyüktür")
+=======
+ if integer == 3 {
+ fmt.Println("The integer is equal to 3")
+ } else if integer < 3 {
+ fmt.Println("The integer is less than 3")
+ } else {
+ fmt.Println("The integer is greater than 3")
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
}
### goto
@@ -46,10 +68,17 @@ Go da `goto` terimi mevcuttur fakat kullanırken dikkatli olun. `goto` programı
func myFunc() {
i := 0
+<<<<<<< HEAD
Buraya: // label sonuna ":" koyulur
fmt.Println(i)
i++
goto Buraya // "Buraya" labeline git
+=======
+ Here: // label ends with ":"
+ fmt.Println(i)
+ i++
+ goto Here // jump to label "Here"
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
}
Label'ın adı büyük-küçük harfe duyarlıdır.
@@ -58,11 +87,19 @@ Label'ın adı büyük-küçük harfe duyarlıdır.
`for` Go da bulunan en güçlü kontrol lojiğidir. Datayı döngüsel olarak ve tekrarlı işlemlerle okuyabilir, `while` döngüsü gibi.
+<<<<<<< HEAD
for ifade1; ifade2; ifade3 {
//...
}
`ifade1`, `ifade2` ve `ifade3` birer ifade olmak üzere, `ifade1` ve `ifade3` değişken tanımlama veya bir fonksiyondan dönen değer olabilirken, `ifade2` ise kontrol ifadesidir. `ifade1` ifadesi döngüye girmeden önce bir kere işlenecektir. `ifade3` ise her döngü sonunda işlenir.
+=======
+ for expression1; expression2; expression3 {
+ //...
+ }
+
+`expression1`, `expression2` ve `expression3` birer ifade olmak üzere, `expression1` ve `expression3` değişken tanımlama veya bir fonksiyondan dönen return olabilirken, `expression2` ise kontrol ifadesidir. `expression1` ifadesi döngüye girmeden önce bir kere işlenecektir. `expression3` ise her döngü sonunda işlenir.
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
Örnekler düz yazıdan daha yararlı olacaktır.
@@ -70,6 +107,7 @@ Label'ın adı büyük-küçük harfe duyarlıdır.
import "fmt"
func main(){
+<<<<<<< HEAD
toplam := 0;
for index:=0; index < 10 ; index++ {
toplam += index
@@ -85,43 +123,85 @@ Bazen birden fazla atama yapmak gerekir fakat Go bunun için kullanılacak bir `
toplam := 1
for ; toplam < 1000; {
toplam += toplam
+=======
+ sum := 0;
+ for index:=0; index < 10 ; index++ {
+ sum += index
+ }
+ fmt.Println("sum is equal to ", sum)
+ }
+ // Print:sum is equal to 45
+
+Bazen birden fazla atama yapmak gerekir fakat Go bunun için kullanılacak bir `,` operatörü yoktur. Biz de `i, j = i + 1, j - 1` gibi paralel atamalar yaparız.
+
+İhtiyacımız yoksa `expression1` ve `expression1` ifadelerini çıkarabiliriz.
+
+ sum := 1
+ for ; sum < 1000; {
+ sum += sum
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
}
Hatta `;` bile çıkarılabilir. Tanıdık geldi mi? Evet, tamamen `while` gibi oldu.
+<<<<<<< HEAD
toplam := 1
for toplam < 1000 {
toplam += toplam
+=======
+ sum := 1
+ for sum < 1000 {
+ sum += sum
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
}
Döngülerde `break` ve `continue` adında iki önemli işlem vardır. `break` döngüden çıkartır ve `continue` o anki tekrarı atlar ve sonraki tekrara geçer. Eğer birden fazla iç içe döngüleriniz varsa `break` ile labelları kullabilirsiniz.
for index := 10; index>0; index-- {
if index == 5{
+<<<<<<< HEAD
break // veya continue
}
fmt.Println(index)
}
// break varsa yazılanlar: 10、9、8、7、6
// continue varsa yazılanlar: 10、9、8、7、6、4、3、2、1
+=======
+ break // or continue
+ }
+ fmt.Println(index)
+ }
+ // break prints 10、9、8、7、6
+ // continue prints 10、9、8、7、6、4、3、2、1
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
`for` döngüsü `range` kullanıldığında `slice` ve `map` de bulunan datayı okuyabilir.
for k,v:=range map {
+<<<<<<< HEAD
fmt.Println("map'in k anahtarı:", k)
fmt.Println("map'in k anahtarındaki değeri:", v)
+=======
+ fmt.Println("map's key:",k)
+ fmt.Println("map's val:",v)
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
}
Go da birden fazla değer return yapılabildiği ve kullanılmayan bir değişken olduğunda derleyici hata verdiği için, kullanmak istemediğiniz değişkenler için `_` kullanabilirsiniz.
for _, v := range map{
+<<<<<<< HEAD
fmt.Println("map'in değeri:", v)
+=======
+ fmt.Println("map's val:", v)
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
}
### switch
Bazı durumlarda çok fazla `if-else` kullandığınızı farkedebilirsiniz. Bu programı okumayı zorlaştırır ve gelecekte bakım yapmayı zorlaştırabilir. Bu durumda problemi çözmek için `switch` kullanmak mükemmeldir.
+<<<<<<< HEAD
switch anaIfade {
case ifade1:
// eşleşme olursa işlenecek kod
@@ -131,6 +211,17 @@ Bazı durumlarda çok fazla `if-else` kullandığınızı farkedebilirsiniz. Bu
// eşleşme olursa işlenecek kod
default:
// eşleşme olmazsa işlenecek kod
+=======
+ switch sExpr {
+ case expr1:
+ some instructions
+ case expr2:
+ some other instructions
+ case expr3:
+ some other instructions
+ default:
+ other code
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
}
`sExpr`, `expr1`, `expr2`, ve `expr3` ifadelerinin türleri aynı olmalıdır. `switch` çok esnektir. Şartlar sabit olmak zorunda değildir ve şart sağlanana kadar yukarıdan aşağıya doğru çalışır. Eğer `switch` in ardından bir ifade gelmiyorsa `true` olarak görülür.
@@ -138,6 +229,7 @@ Bazı durumlarda çok fazla `if-else` kullandığınızı farkedebilirsiniz. Bu
i := 10
switch i {
case 1:
+<<<<<<< HEAD
fmt.Println("i 1'e eşittir")
case 2, 3, 4:
fmt.Println("i 2, 3 veya 4'e eşittir")
@@ -145,10 +237,20 @@ Bazı durumlarda çok fazla `if-else` kullandığınızı farkedebilirsiniz. Bu
fmt.Println("i 10'a eşittir")
default:
fmt.Println("Tek bildiğim i nin bir sayi olduğu")
+=======
+ fmt.Println("i is equal to 1")
+ case 2, 3, 4:
+ fmt.Println("i is equal to 2, 3 or 4")
+ case 10:
+ fmt.Println("i is equal to 10")
+ default:
+ fmt.Println("All I know is that i is an integer")
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
}
Beşinci satırdaki gibi `case` içinde birden fazla değer olabilir. `case` sonlarına `break` eklemeye gerek yoktur, şart sağlanıp işlem yapıldıktan sonra çıkacaktır. Eğer çıkmasını istemiyorsanız `fallthrough` ifadesini kullanarak bir sonraki şarta devam edebilirsiniz.
+<<<<<<< HEAD
sayı := 6
switch sayı {
case 4:
@@ -185,14 +287,59 @@ Programın sonucu şu olacaktır.
// fonksiyon gövdesi
// birden fazla return
return dönüş1, dönüş2
+=======
+ integer := 6
+ switch integer {
+ case 4:
+ fmt.Println("integer <= 4")
+ fallthrough
+ case 5:
+ fmt.Println("integer <= 5")
+ fallthrough
+ case 6:
+ fmt.Println("integer <= 6")
+ fallthrough
+ case 7:
+ fmt.Println("integer <= 7")
+ fallthrough
+ case 8:
+ fmt.Println("integer <= 8")
+ fallthrough
+ default:
+ fmt.Println("default case")
+ }
+
+This program prints the following information.
+
+ integer <= 6
+ integer <= 7
+ integer <= 8
+ default case
+
+## Functions
+
+`func` terimini kullanarak bir fonksiyon tanımlayın.
+
+ func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
+ // function body
+ // multi-value return
+ return value1, value2
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
}
Yukarıdaki örnekten tahmin edebileceğiniz üzere aşağıda açıklamaları bulunur.
+<<<<<<< HEAD
- `fonksiyonAdı` adlı foonksiyonu tanımlamak için `func` terimini kullanın.
- Fonksiyonlar sıfır veya daha fazla parametreye sahip olabilir. Parametrenin türü adından sonra gelir ve birden fazla parametre varsa `,` ile ayrılır.
- Fonksiyonlar birden fazla değer döndürebilirler.
- Bu örnekte `çıkış1` ve `çıkış2` adında iki değer döndürülmüş. Bunlara ad vermek zorunda değilsiniz, türünü yazmanız yeterli.
+=======
+- `funcName` adlı foonksiyonu tanımlamak için `func` terimini kullanın.
+- Fonksiyonlar sıfır veya daha fazla parametreye sahip olabilir. Parametrenin türü adından sonra gelir ve birden fazla parametre varsa `,` ile ayrılır.
+- Fonksiyonlar birden fazla değer döndürebilirler.
+- Bu örnekte `output1` ve `output2` adında iki değer döndürülmüş. Bunlara ad vermek zorunda değilsiniz, türünü yazmanız yeterli.
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
- Eğer sadece bir değer döndürecekseniz parantez olmadan yazmalısınız.
- Eğer en az bir değer döndürüyorsanız, fonksiyonun içinde istediğiniz yerde `return` terimini kullanmalısınız.
@@ -201,7 +348,11 @@ Yukarıdaki örnekten tahmin edebileceğiniz üzere aşağıda açıklamaları b
package main
import "fmt"
+<<<<<<< HEAD
// a ile b arasından büyük olanı döndür
+=======
+ // return greater value between a and b
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
func max(a, b int) int {
if a > b {
return a
@@ -214,12 +365,21 @@ Yukarıdaki örnekten tahmin edebileceğiniz üzere aşağıda açıklamaları b
y := 4
z := 5
+<<<<<<< HEAD
max_xy := max(x, y) // max(x, y) fonskiyonunu çağır
max_xz := max(x, z) // max(x, z) fonksiyonunu çağır
fmt.Printf("max(%d, %d) = %d\n", x, y, max_xy)
fmt.Printf("max(%d, %d) = %d\n", x, z, max_xz)
fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // burada da fonksiyon çağırıldı
+=======
+ max_xy := max(x, y) // call function max(x, y)
+ max_xz := max(x, z) // call function max(x, z)
+
+ fmt.Printf("max(%d, %d) = %d\n", x, y, max_xy)
+ fmt.Printf("max(%d, %d) = %d\n", x, z, max_xz)
+ fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // call function here
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
}
Yukarıdaki örnek fonksiyon `max` da iki aynı tür parametre `int` olduğu için bir tane yazmak yeterli olur. Yani `a int, b int` yerine `a, b int` kullanılır. Birden fazla parametre için de aynı kural geçerlidir. Farkettiyseniz `max` fonksiyonu sadece bir değer döndürür ve zorunda olmadığımız için o değere bir isim vermedik, bu kısa halini kullandık.
@@ -233,8 +393,13 @@ Alttaki örnekte bunu kullanalım.
package main
import "fmt"
+<<<<<<< HEAD
// A+B ve A*B nin sonuçlarını döndür
func toplaVeCarp(A, B int) (int, int) {
+=======
+ // return results of A + B and A * B
+ func SumAndProduct(A, B int) (int, int) {
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
return A+B, A*B
}
@@ -242,17 +407,30 @@ Alttaki örnekte bunu kullanalım.
x := 3
y := 4
+<<<<<<< HEAD
xARTIy, xCARPIy := SumAndProduct(x, y)
fmt.Printf("%d + %d = %d\n", x, y, xARTIy)
fmt.Printf("%d * %d = %d\n", x, y, xCARPIy)
+=======
+ xPLUSy, xTIMESy := SumAndProduct(x, y)
+
+ fmt.Printf("%d + %d = %d\n", x, y, xPLUSy)
+ fmt.Printf("%d * %d = %d\n", x, y, xTIMESy)
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
}
Üstteki fonksiyon isimsiz iki değer döndürür -isterseniz isim verebilirsiniz. Eğer isimlendirseydik, `return` yazıp isimlerini yazmamız yeterdi. Çünkü fonksiyonun içinde tanımlılar. Şuna dikkat etmelisiniz ki eğer fonksiyonu başka bir pakette kullanacaksanız (fonksiyonun ilk harfi büyük harfle başlamalıdır) `return` yapacaklarınızı tam bir ifade olarak yazmanız daha iyi olacaktır. Kodu daha okunur hale getirir.
+<<<<<<< HEAD
func ToplaVeCarp(A, B int) (toplanan int, carpılan int) {
toplanan = A+B
carpılan = A*B
+=======
+ func SumAndProduct(A, B int) (add int, multiplied int) {
+ add = A+B
+ multiplied = A*B
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
return
}
@@ -265,7 +443,11 @@ Go birden fazla argüman alabilen fonksiyonları destekler. Bunlara variadic (be
`arg …int` kısmı Go ya bu fonksiyonun değişen sayıda argüman aldığını söyler. Bu argümanların türü `int` dir. `arg` fonksiyonun gövdesinde `int` türünde bir `slice` olur.
for _, n := range arg {
+<<<<<<< HEAD
fmt.Printf("Sayı: %d\n", n)
+=======
+ fmt.Printf("And the number is: %d\n", n)
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
}
### Değer ile devretmek ve pointerlar
@@ -277,15 +459,23 @@ Bunun kanıtı olarak bir örnek görelim.
package main
import "fmt"
+<<<<<<< HEAD
// a ya 1 eklemek için basit bir fonksiyon
func birEkle(a int) int {
a = a+1 // a'nın değeri değişti
return a // a'nın yeni değeri döndürülüyor
+=======
+ // simple function to add 1 to a
+ func add1(a int) int {
+ a = a+1 // we change value of a
+ return a // return new value of a
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
}
func main() {
x := 3
+<<<<<<< HEAD
fmt.Println("x = ", x) // sonuç "x = 3" olmalı
x1 := birEkle(x) // birEkle(x) fonksiyonu çağırıldı
@@ -301,25 +491,58 @@ Sebebi basit: `birEkle` i çağırdığımızda ona `x` in bir kopyasını gönd
Şimdi sorabilirsiniz `x` in kendisini nasıl fonksiyona verebilirim diye.
Burada pointer kullanmamız gerekiyor. Biliyoruz ki değişkenler bellekte tutulur ve bir adresleri vardır. Eğer değişkenin aslını değiştirmek istiyorsak onun bellek adresini kullanmalıyız. Böylelikle `birEkle` fonksiyonu `x` in adresini kullanarak onun değerini değiştirebilir. Parametre türünü `*int` olarak değiştiriyoruz ve değişkenin adresini `&x` ile fonksiyona veriyoruz. Fonksiyona değerin bir kopyasını değil de bir pointer verdiğimize dikkat edin.
+=======
+ fmt.Println("x = ", x) // should print "x = 3"
+
+ x1 := add1(x) // call add1(x)
+
+ fmt.Println("x+1 = ", x1) // should print "x+1 = 4"
+ fmt.Println("x = ", x) // should print "x = 3"
+ }
+
+Gördünüz mü? `add1` fonksiyonuna `x` i gönderdiğimiz halde asıl değeri değişmedi.
+
+Sebebi basit: `add1` i çağırdığımızda ona `x` in bir kopyasını gönderdik `x` in kendisini değil.
+
+Şimdi sorabilirsiniz `x` in kendisini nasıl fonksiyona verebilirim diye.
+
+Burada pointer kullanmamız gerekiyor. Biliyoruz ki değişkenler bellekte tutulur ve bir adresleri vardır. Eğer değişkenin aslını değiştirmek istiyorsak onun bellek adresini kullanmalıyız. Böylelikle `add1` fonksiyonu `x` in adresini kullanarak onun değerini değiştirebilir. Parametre türünü `*int` olarak değiştiriyoruz ve değişkenin adresini `&x` ile fonksiyona veriyoruz. Fonksiyona değerin bir kopyasını değil de bir pointer verdiğimize dikkat edin.
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
package main
import "fmt"
+<<<<<<< HEAD
// a ya 1 eklemek için basit bir fonksiyon
func birEkle(a *int) int {
*a = *a+1 // a'nın değeri değişti
return *a // a'nın yeni değeri döndürülüyor
+=======
+ // simple function to add 1 to a
+ func add1(a *int) int {
+ *a = *a+1 // we changed value of a
+ return *a // return new value of a
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
}
func main() {
x := 3
+<<<<<<< HEAD
fmt.Println("x = ", x) // sonuç "x = 3" olmalı
x1 := add1(&x) // birEkle(x) fonksiyonu çağırıldı ve x'in adresi verildi
fmt.Println("x+1 = ", x1) // sonuç "x+1 = 4" olmalı
fmt.Println("x = ", x) // sonuç "x = 4" olmalı
+=======
+ fmt.Println("x = ", x) // should print "x = 3"
+
+ x1 := add1(&x) // call add1(&x) pass memory address of x
+
+ fmt.Println("x+1 = ", x1) // should print "x+1 = 4"
+ fmt.Println("x = ", x) // should print "x = 4"
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
}
Şimdi `x` in asıl değerini değiştirebiliriz. Neden pointer kullanıyoruz? Avantajı nedir?
@@ -332,15 +555,26 @@ Burada pointer kullanmamız gerekiyor. Biliyoruz ki değişkenler bellekte tutul
Go da iyi tasarlanmış `defer` (ertelemek) adlı bir terim vardır. Bir fonksiyonda birden fazla `defer` ifadesi bulunabilir, program çalıştığında sondan başa sırayla çalışacaklardır. Programın dosya açtığı durumlarda bu dosyaların hata vermeden önce kapatılması gerekir. Örneklere bakalım.
+<<<<<<< HEAD
func OkuYaz() bool {
file.Open("dosya")
// Kod
if hataX {
+=======
+ func ReadWrite() bool {
+ file.Open("file")
+ // Do some work
+ if failureX {
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
file.Close()
return false
}
+<<<<<<< HEAD
if hataY {
+=======
+ if failureY {
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
file.Close()
return false
}
@@ -351,6 +585,7 @@ Go da iyi tasarlanmış `defer` (ertelemek) adlı bir terim vardır. Bir fonksiy
Bazı kodların tekrar ettiğini görüyoruz. `defer` bu problemi çok iyi çözer. Daha temiz kod yazmanıza yardım etmekle kalmaz kodunuzu daha okunur yapar.
+<<<<<<< HEAD
func OkuYaz() bool {
file.Open("dosya")
defer file.Close()
@@ -358,6 +593,15 @@ Bazı kodların tekrar ettiğini görüyoruz. `defer` bu problemi çok iyi çöz
return false
}
if hataY {
+=======
+ func ReadWrite() bool {
+ file.Open("file")
+ defer file.Close()
+ if failureX {
+ return false
+ }
+ if failureY {
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
return false
}
return true
@@ -373,29 +617,46 @@ Eğer birden fazla `defer` varsa ters sırayla çalışırlar. Sıradaki örnek
Go'da fonksiyonlar aynı zamanda değişken olabilirler. `type` onları kullanarak tanımlayabiliriz. Aynı imzaya sahip fonksiyonlar ayno tür olarak görülebilir.
+<<<<<<< HEAD
type türAdı func(parametre1 tür1, parametre2 tür2 [, ...]) (çıkış1 tür1 [, ...])
+=======
+ type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...])
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
Bunun avantajı nedir? Cevap, fonksiyonları değer olarak verebilmemizi sağlamasıdır.
package main
import "fmt"
+<<<<<<< HEAD
type testInt func(int) bool // değişken olarak fonksiyon tanımlandı
func tekMi(sayı int) bool {
if sayı%2 == 0 {
+=======
+ type testInt func(int) bool // define a function type of variable
+
+ func isOdd(integer int) bool {
+ if integer%2 == 0 {
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
return false
}
return true
}
+<<<<<<< HEAD
func çiftMi(sayı int) bool {
if sayı%2 == 0 {
+=======
+ func isEven(integer int) bool {
+ if integer%2 == 0 {
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
return true
}
return false
}
+<<<<<<< HEAD
// 'f' fonksiyonunu bir fonksiyona parametre olarak ata
func filtre(slice []int, f testInt) []int {
@@ -406,11 +667,24 @@ Bunun avantajı nedir? Cevap, fonksiyonları değer olarak verebilmemizi sağlam
}
}
return sonuc
+=======
+ // pass the function `f` as an argument to another function
+
+ func filter(slice []int, f testInt) []int {
+ var result []int
+ for _, value := range slice {
+ if f(value) {
+ result = append(result, value)
+ }
+ }
+ return result
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
}
func main(){
slice := []int {1, 2, 3, 4, 5, 7}
fmt.Println("slice = ", slice)
+<<<<<<< HEAD
tek := filtre(slice, tekMi) // fonksiyonu değer olarak kullan
fmt.Println("Slice'daki tek sayılar: ", tek)
çift := filtre(slice, çiftMi)
@@ -418,6 +692,15 @@ Bunun avantajı nedir? Cevap, fonksiyonları değer olarak verebilmemizi sağlam
}
Interface kullanılan durumlarda çok yararlıdır. Gördüğünüz gibi `testInt` fonksiyon türünde bir değişkendir ve `filtre`'nin döndürdüğü argümanlar ve değerler `testInt` ile aynıdır. Böylelikle programlarımızda karmaşık mantık yürütebilir ve esneklik kazanabiliriz.
+=======
+ odd := filter(slice, isOdd) // use function as values
+ fmt.Println("Odd elements of slice are: ", odd)
+ even := filter(slice, isEven)
+ fmt.Println("Even elements of slice are: ", even)
+ }
+
+Interface kullanılan durumlarda çok yararlıdır. Gördüğünüz gibi `testInt` fonksiyon türünde bir değişkendir ve `filter` ın döndürdüğü argümanlar ve değerler `testInt` ile aynıdır. Böylelikle programlarımızda karmaşık mantık yürütebilir ve esneklik kazanabiliriz.
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
### Panic ve Recover
@@ -429,23 +712,39 @@ Go da Java gibi `try-catch` yapısı yoktur. Go exception fırlatmak yerine `pan
Aşağıdaki örnekte `panic` nasıl kullanılır gösterilmiştir.
+<<<<<<< HEAD
var kullanıcı = os.Getenv("KULLANICI")
func init() {
if kullanıcı == "" {
panic("$KULLANICI için bir değer bulunamadı")
+=======
+ var user = os.Getenv("USER")
+
+ func init() {
+ if user == "" {
+ panic("no value for $USER")
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
}
}
Bu örnek de `panic` kontrolü yapılmasını gösterir.
+<<<<<<< HEAD
func panicYapar(f func()) (b bool) {
+=======
+ func throwsPanic(f func()) (b bool) {
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
defer func() {
if x := recover(); x != nil {
b = true
}
}()
+<<<<<<< HEAD
f() // eğer f() panic yaparsa recover yapılır
+=======
+ f() // if f causes panic, it will recover
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
return
}
@@ -459,7 +758,11 @@ Programlar işleme `main` paketinden başlar. Eğer `main` başka paketler dahil

+<<<<<<< HEAD
Şekil 2.6 Go da programların parafe edilmesinin akışı
+=======
+Figure 2.6 Go da programların parafe edilmesinin akışı
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
### import
@@ -510,8 +813,15 @@ Paketler dahil edilirken kullanılan özel operatörler vardır ve bunlar geneld
Bu operatör dahil edilen paketin sadece `init` fonksiyonun çağrılıp çalıştırılması için kullanılır. Paketteki diğer fonksiyonları kullanacağınıza emin değilseniz kullanabilirsiniz.
+<<<<<<< HEAD
## Linkler
- [Rehber](preface.md)
- Önceki bölüm: [Go temelleri](02.2.md)
+=======
+## Links
+
+- [Directory](preface.md)
+- Önceki bölüm: [Go foundation](02.2.md)
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa
- Sonraki bölüm: [struct](02.4.md)
diff --git a/zh/02.0.md b/zh/02.0.md~HEAD
similarity index 98%
rename from zh/02.0.md
rename to zh/02.0.md~HEAD
index 600c7c2e..5d4f9fd1 100644
--- a/zh/02.0.md
+++ b/zh/02.0.md~HEAD
@@ -1,19 +1,19 @@
-# 2 Go语言基础
-
-Go是一门类似C的编译型语言,但是它的编译速度非常快。这门语言的关键字总共也就二十五个,比英文字母还少一个,这对于我们的学习来说就简单了很多。先让我们看一眼这些关键字都长什么样:
-
- break default func interface select
- case defer go map struct
- chan else goto package switch
- const fallthrough if range type
- continue for import return var
-
-在接下来的这一章中,我将带领你去学习这门语言的基础。通过每一小节的介绍,你将发现,Go的世界是那么地简洁,设计是如此地美妙,编写Go将会是一件愉快的事情。等回过头来,你就会发现这二十五个关键字是多么地亲切。
-
-## 目录
-
-
-## links
- * [目录]()
- * 上一章: [第一章总结](<01.5.md>)
- * 下一节: [你好,Go](<02.1.md>)
+# 2 Go语言基础
+
+Go是一门类似C的编译型语言,但是它的编译速度非常快。这门语言的关键字总共也就二十五个,比英文字母还少一个,这对于我们的学习来说就简单了很多。先让我们看一眼这些关键字都长什么样:
+
+ break default func interface select
+ case defer go map struct
+ chan else goto package switch
+ const fallthrough if range type
+ continue for import return var
+
+在接下来的这一章中,我将带领你去学习这门语言的基础。通过每一小节的介绍,你将发现,Go的世界是那么地简洁,设计是如此地美妙,编写Go将会是一件愉快的事情。等回过头来,你就会发现这二十五个关键字是多么地亲切。
+
+## 目录
+
+
+## links
+ * [目录]()
+ * 上一章: [第一章总结](<01.5.md>)
+ * 下一节: [你好,Go](<02.1.md>)
diff --git a/zh/02.0.md~eead24cf064976b648de5826eab51880c803b0fa b/zh/02.0.md~eead24cf064976b648de5826eab51880c803b0fa
new file mode 100644
index 00000000..5d4f9fd1
--- /dev/null
+++ b/zh/02.0.md~eead24cf064976b648de5826eab51880c803b0fa
@@ -0,0 +1,19 @@
+# 2 Go语言基础
+
+Go是一门类似C的编译型语言,但是它的编译速度非常快。这门语言的关键字总共也就二十五个,比英文字母还少一个,这对于我们的学习来说就简单了很多。先让我们看一眼这些关键字都长什么样:
+
+ break default func interface select
+ case defer go map struct
+ chan else goto package switch
+ const fallthrough if range type
+ continue for import return var
+
+在接下来的这一章中,我将带领你去学习这门语言的基础。通过每一小节的介绍,你将发现,Go的世界是那么地简洁,设计是如此地美妙,编写Go将会是一件愉快的事情。等回过头来,你就会发现这二十五个关键字是多么地亲切。
+
+## 目录
+
+
+## links
+ * [目录]()
+ * 上一章: [第一章总结](<01.5.md>)
+ * 下一节: [你好,Go](<02.1.md>)
diff --git a/zh/02.1.md b/zh/02.1.md~HEAD
similarity index 98%
rename from zh/02.1.md
rename to zh/02.1.md~HEAD
index fa44a36b..5bc0e69f 100644
--- a/zh/02.1.md
+++ b/zh/02.1.md~HEAD
@@ -1,52 +1,52 @@
-# 2.1 你好,Go
-
-在开始编写应用之前,我们先从最基本的程序开始。就像你造房子之前不知道什么是地基一样,编写程序也不知道如何开始。因此,在本节中,我们要学习用最基本的语法让Go程序运行起来。
-
-## 程序
-
-这就像一个传统,在学习大部分语言之前,你先学会如何编写一个可以输出`hello world`的程序。
-
-准备好了吗?Let's Go!
-
- package main
-
- import "fmt"
-
- func main() {
- fmt.Printf("Hello, world or 你好,世界 or καλημ ́ρα κóσμ or こんにちはせかい\n")
- }
-
-输出如下:
-
- Hello, world or 你好,世界 or καλημ ́ρα κóσμ or こんにちはせかい
-
-## 详解
-首先我们要了解一个概念,Go程序是通过`package`来组织的
-
-`package `(在我们的例子中是`package main`)这一行告诉我们当前文件属于哪个包,而包名`main`则告诉我们它是一个可独立运行的包,它在编译后会产生可执行文件。除了`main`包之外,其它的包最后都会生成`*.a`文件(也就是包文件)并放置在`$GOPATH/pkg/$GOOS_$GOARCH`中(以Mac为例就是`$GOPATH/pkg/darwin_amd64`)。
-
->每一个可独立运行的Go程序,必定包含一个`package main`,在这个`main`包中必定包含一个入口函数`main`,而这个函数既没有参数,也没有返回值。
-
-为了打印`Hello, world...`,我们调用了一个函数`Printf`,这个函数来自于`fmt`包,所以我们在第三行中导入了系统级别的`fmt`包:`import "fmt"`。
-
-包的概念和Python中的package类似,它们都有一些特别的好处:模块化(能够把你的程序分成多个模块)和可重用性(每个模块都能被其它应用程序反复使用)。我们在这里只是先了解一下包的概念,后面我们将会编写自己的包。
-
-在第五行中,我们通过关键字`func`定义了一个`main`函数,函数体被放在`{}`(大括号)中,就像我们平时写C、C++或Java时一样。
-
-大家可以看到`main`函数是没有任何的参数的,我们接下来就学习如何编写带参数的、返回0个或多个值的函数。
-
-第六行,我们调用了`fmt`包里面定义的函数`Printf`。大家可以看到,这个函数是通过`.`的方式调用的,这一点和Python十分相似。
-
->前面提到过,包名和包所在的文件夹名可以是不同的,此处的``即为通过`package `声明的包名,而非文件夹名。
-
-最后大家可以看到我们输出的内容里面包含了很多非ASCII码字符。实际上,Go是天生支持UTF-8的,任何字符都可以直接输出,你甚至可以用UTF-8中的任何字符作为标识符。
-
-
-## 结论
-
-Go使用`package`(和Python的模块类似)来组织代码。`main.main()`函数(这个函数位于主包)是每一个独立的可运行程序的入口点。Go使用UTF-8字符串和标识符(因为UTF-8的发明者也就是Go的发明者之一),所以它天生支持多语言。
-
-## links
- * [目录]()
- * 上一节: [Go语言基础](<02.0.md>)
- * 下一节: [Go基础](<02.2.md>)
+# 2.1 你好,Go
+
+在开始编写应用之前,我们先从最基本的程序开始。就像你造房子之前不知道什么是地基一样,编写程序也不知道如何开始。因此,在本节中,我们要学习用最基本的语法让Go程序运行起来。
+
+## 程序
+
+这就像一个传统,在学习大部分语言之前,你先学会如何编写一个可以输出`hello world`的程序。
+
+准备好了吗?Let's Go!
+
+ package main
+
+ import "fmt"
+
+ func main() {
+ fmt.Printf("Hello, world or 你好,世界 or καλημ ́ρα κóσμ or こんにちはせかい\n")
+ }
+
+输出如下:
+
+ Hello, world or 你好,世界 or καλημ ́ρα κóσμ or こんにちはせかい
+
+## 详解
+首先我们要了解一个概念,Go程序是通过`package`来组织的
+
+`package `(在我们的例子中是`package main`)这一行告诉我们当前文件属于哪个包,而包名`main`则告诉我们它是一个可独立运行的包,它在编译后会产生可执行文件。除了`main`包之外,其它的包最后都会生成`*.a`文件(也就是包文件)并放置在`$GOPATH/pkg/$GOOS_$GOARCH`中(以Mac为例就是`$GOPATH/pkg/darwin_amd64`)。
+
+>每一个可独立运行的Go程序,必定包含一个`package main`,在这个`main`包中必定包含一个入口函数`main`,而这个函数既没有参数,也没有返回值。
+
+为了打印`Hello, world...`,我们调用了一个函数`Printf`,这个函数来自于`fmt`包,所以我们在第三行中导入了系统级别的`fmt`包:`import "fmt"`。
+
+包的概念和Python中的package类似,它们都有一些特别的好处:模块化(能够把你的程序分成多个模块)和可重用性(每个模块都能被其它应用程序反复使用)。我们在这里只是先了解一下包的概念,后面我们将会编写自己的包。
+
+在第五行中,我们通过关键字`func`定义了一个`main`函数,函数体被放在`{}`(大括号)中,就像我们平时写C、C++或Java时一样。
+
+大家可以看到`main`函数是没有任何的参数的,我们接下来就学习如何编写带参数的、返回0个或多个值的函数。
+
+第六行,我们调用了`fmt`包里面定义的函数`Printf`。大家可以看到,这个函数是通过`.`的方式调用的,这一点和Python十分相似。
+
+>前面提到过,包名和包所在的文件夹名可以是不同的,此处的``即为通过`package `声明的包名,而非文件夹名。
+
+最后大家可以看到我们输出的内容里面包含了很多非ASCII码字符。实际上,Go是天生支持UTF-8的,任何字符都可以直接输出,你甚至可以用UTF-8中的任何字符作为标识符。
+
+
+## 结论
+
+Go使用`package`(和Python的模块类似)来组织代码。`main.main()`函数(这个函数位于主包)是每一个独立的可运行程序的入口点。Go使用UTF-8字符串和标识符(因为UTF-8的发明者也就是Go的发明者之一),所以它天生支持多语言。
+
+## links
+ * [目录]()
+ * 上一节: [Go语言基础](<02.0.md>)
+ * 下一节: [Go基础](<02.2.md>)
diff --git a/zh/02.1.md~eead24cf064976b648de5826eab51880c803b0fa b/zh/02.1.md~eead24cf064976b648de5826eab51880c803b0fa
new file mode 100644
index 00000000..5bc0e69f
--- /dev/null
+++ b/zh/02.1.md~eead24cf064976b648de5826eab51880c803b0fa
@@ -0,0 +1,52 @@
+# 2.1 你好,Go
+
+在开始编写应用之前,我们先从最基本的程序开始。就像你造房子之前不知道什么是地基一样,编写程序也不知道如何开始。因此,在本节中,我们要学习用最基本的语法让Go程序运行起来。
+
+## 程序
+
+这就像一个传统,在学习大部分语言之前,你先学会如何编写一个可以输出`hello world`的程序。
+
+准备好了吗?Let's Go!
+
+ package main
+
+ import "fmt"
+
+ func main() {
+ fmt.Printf("Hello, world or 你好,世界 or καλημ ́ρα κóσμ or こんにちはせかい\n")
+ }
+
+输出如下:
+
+ Hello, world or 你好,世界 or καλημ ́ρα κóσμ or こんにちはせかい
+
+## 详解
+首先我们要了解一个概念,Go程序是通过`package`来组织的
+
+`package `(在我们的例子中是`package main`)这一行告诉我们当前文件属于哪个包,而包名`main`则告诉我们它是一个可独立运行的包,它在编译后会产生可执行文件。除了`main`包之外,其它的包最后都会生成`*.a`文件(也就是包文件)并放置在`$GOPATH/pkg/$GOOS_$GOARCH`中(以Mac为例就是`$GOPATH/pkg/darwin_amd64`)。
+
+>每一个可独立运行的Go程序,必定包含一个`package main`,在这个`main`包中必定包含一个入口函数`main`,而这个函数既没有参数,也没有返回值。
+
+为了打印`Hello, world...`,我们调用了一个函数`Printf`,这个函数来自于`fmt`包,所以我们在第三行中导入了系统级别的`fmt`包:`import "fmt"`。
+
+包的概念和Python中的package类似,它们都有一些特别的好处:模块化(能够把你的程序分成多个模块)和可重用性(每个模块都能被其它应用程序反复使用)。我们在这里只是先了解一下包的概念,后面我们将会编写自己的包。
+
+在第五行中,我们通过关键字`func`定义了一个`main`函数,函数体被放在`{}`(大括号)中,就像我们平时写C、C++或Java时一样。
+
+大家可以看到`main`函数是没有任何的参数的,我们接下来就学习如何编写带参数的、返回0个或多个值的函数。
+
+第六行,我们调用了`fmt`包里面定义的函数`Printf`。大家可以看到,这个函数是通过`.`的方式调用的,这一点和Python十分相似。
+
+>前面提到过,包名和包所在的文件夹名可以是不同的,此处的``即为通过`package `声明的包名,而非文件夹名。
+
+最后大家可以看到我们输出的内容里面包含了很多非ASCII码字符。实际上,Go是天生支持UTF-8的,任何字符都可以直接输出,你甚至可以用UTF-8中的任何字符作为标识符。
+
+
+## 结论
+
+Go使用`package`(和Python的模块类似)来组织代码。`main.main()`函数(这个函数位于主包)是每一个独立的可运行程序的入口点。Go使用UTF-8字符串和标识符(因为UTF-8的发明者也就是Go的发明者之一),所以它天生支持多语言。
+
+## links
+ * [目录]()
+ * 上一节: [Go语言基础](<02.0.md>)
+ * 下一节: [Go基础](<02.2.md>)
diff --git a/zh/02.2.md b/zh/02.2.md
index 24c32ea1..f9853cab 100644
--- a/zh/02.2.md
+++ b/zh/02.2.md
@@ -404,7 +404,11 @@ slice有一些简便的操作
`map`也就是Python中字典的概念,它的格式为`map[keyType]valueType`
+<<<<<<< HEAD:zh/02.2.md
我们看下面的代码,`map`的读取和设置也类似`slice`一样,通过`key`来操作,只是`slice`的`index`只能是`int`类型,而`map`多了很多类型,可以是`int`,可以是`string`及所有完全定义了`==`与`!=`操作的类型。
+=======
+我们看下面的代码,`map`的读取和设置也类似`slice`一样,通过`key`来操作,只是`slice`的`index`只能是`int`类型,而`map`多了很多类型,可以是`int`,可以是`string`及所有完全定义了`==`与`!=`操作的类型。
+>>>>>>> eead24cf064976b648de5826eab51880c803b0fa:zh/02.2.md
// 声明一个key是字符串,值为int的字典,这种方式的声明需要在使用之前使用make初始化
var numbers map[string]int
diff --git a/zh/02.3.md b/zh/02.3.md~HEAD
similarity index 97%
rename from zh/02.3.md
rename to zh/02.3.md~HEAD
index cae23309..551ccf94 100644
--- a/zh/02.3.md
+++ b/zh/02.3.md~HEAD
@@ -1,520 +1,520 @@
-# 2.3 流程和函数
-这小节我们要介绍Go里面的流程控制以及函数操作。
-## 流程控制
-流程控制在编程语言中是最伟大的发明了,因为有了它,你可以通过很简单的流程描述来表达很复杂的逻辑。Go中流程控制分三大类:条件判断,循环控制和无条件跳转。
-### if
-`if`也许是各种编程语言中最常见的了,它的语法概括起来就是:如果满足条件就做某事,否则做另一件事。
-
-Go里面`if`条件判断语句中不需要括号,如下代码所示
-
- if x > 10 {
- fmt.Println("x is greater than 10")
- } else {
- fmt.Println("x is less than 10")
- }
-
-Go的`if`还有一个强大的地方就是条件判断语句里面允许声明一个变量,这个变量的作用域只能在该条件逻辑块内,其他地方就不起作用了,如下所示
-
- // 计算获取值x,然后根据x返回的大小,判断是否大于10。
- if x := computedValue(); x > 10 {
- fmt.Println("x is greater than 10")
- } else {
- fmt.Println("x is less than 10")
- }
-
- //这个地方如果这样调用就编译出错了,因为x是条件里面的变量
- fmt.Println(x)
-
-多个条件的时候如下所示:
-
- if integer == 3 {
- fmt.Println("The integer is equal to 3")
- } else if integer < 3 {
- fmt.Println("The integer is less than 3")
- } else {
- fmt.Println("The integer is greater than 3")
- }
-
-### goto
-
-Go有`goto`语句——请明智地使用它。用`goto`跳转到必须在当前函数内定义的标签。例如假设这样一个循环:
-
- func myFunc() {
- i := 0
- Here: //这行的第一个词,以冒号结束作为标签
- println(i)
- i++
- goto Here //跳转到Here去
- }
-
->标签名是大小写敏感的。
-
-### for
-Go里面最强大的一个控制逻辑就是`for`,它即可以用来循环读取数据,又可以当作`while`来控制逻辑,还能迭代操作。它的语法如下:
-
- for expression1; expression2; expression3 {
- //...
- }
-
-`expression1`、`expression2`和`expression3`都是表达式,其中`expression1`和`expression3`是变量声明或者函数调用返回值之类的,`expression2`是用来条件判断,`expression1`在循环开始之前调用,`expression3`在每轮循环结束之时调用。
-
-一个例子比上面讲那么多更有用,那么我们看看下面的例子吧:
-
- package main
- import "fmt"
-
- func main(){
- sum := 0;
- for index:=0; index < 10 ; index++ {
- sum += index
- }
- fmt.Println("sum is equal to ", sum)
- }
- // 输出:sum is equal to 45
-
-有些时候需要进行多个赋值操作,由于Go里面没有`,`操作符,那么可以使用平行赋值`i, j = i+1, j-1`
-
-
-有些时候如果我们忽略`expression1`和`expression3`:
-
- sum := 1
- for ; sum < 1000; {
- sum += sum
- }
-
-其中`;`也可以省略,那么就变成如下的代码了,是不是似曾相识?对,这就是`while`的功能。
-
- sum := 1
- for sum < 1000 {
- sum += sum
- }
-
-在循环里面有两个关键操作`break`和`continue` ,`break`操作是跳出当前循环,`continue`是跳过本次循环。当嵌套过深的时候,`break`可以配合标签使用,即跳转至标签所指定的位置,详细参考如下例子:
-
- for index := 10; index>0; index-- {
- if index == 5{
- break // 或者continue
- }
- fmt.Println(index)
- }
- // break打印出来10、9、8、7、6
- // continue打印出来10、9、8、7、6、4、3、2、1
-
-`break`和`continue`还可以跟着标号,用来跳到多重循环中的外层循环
-
-`for`配合`range`可以用于读取`slice`和`map`的数据:
-
- for k,v:=range map {
- fmt.Println("map's key:",k)
- fmt.Println("map's val:",v)
- }
-
-由于 Go 支持 “多值返回”, 而对于“声明而未被调用”的变量, 编译器会报错, 在这种情况下, 可以使用`_`来丢弃不需要的返回值
-例如
-
- for _, v := range map{
- fmt.Println("map's val:", v)
- }
-
-
-### switch
-有些时候你需要写很多的`if-else`来实现一些逻辑处理,这个时候代码看上去就很丑很冗长,而且也不易于以后的维护,这个时候`switch`就能很好的解决这个问题。它的语法如下
-
- switch sExpr {
- case expr1:
- some instructions
- case expr2:
- some other instructions
- case expr3:
- some other instructions
- default:
- other code
- }
-
-`sExpr`和`expr1`、`expr2`、`expr3`的类型必须一致。Go的`switch`非常灵活,表达式不必是常量或整数,执行的过程从上至下,直到找到匹配项;而如果`switch`没有表达式,它会匹配`true`。
-
- i := 10
- switch i {
- case 1:
- fmt.Println("i is equal to 1")
- case 2, 3, 4:
- fmt.Println("i is equal to 2, 3 or 4")
- case 10:
- fmt.Println("i is equal to 10")
- default:
- fmt.Println("All I know is that i is an integer")
- }
-
-在第5行中,我们把很多值聚合在了一个`case`里面,同时,Go里面`switch`默认相当于每个`case`最后带有`break`,匹配成功后不会自动向下执行其他case,而是跳出整个`switch`, 但是可以使用`fallthrough`强制执行后面的case代码。
-
- integer := 6
- switch integer {
- case 4:
- fmt.Println("The integer was <= 4")
- fallthrough
- case 5:
- fmt.Println("The integer was <= 5")
- fallthrough
- case 6:
- fmt.Println("The integer was <= 6")
- fallthrough
- case 7:
- fmt.Println("The integer was <= 7")
- fallthrough
- case 8:
- fmt.Println("The integer was <= 8")
- fallthrough
- default:
- fmt.Println("default case")
- }
-
-上面的程序将输出
-
- The integer was <= 6
- The integer was <= 7
- The integer was <= 8
- default case
-
-
-## 函数
-函数是Go里面的核心设计,它通过关键字`func`来声明,它的格式如下:
-
- func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
- //这里是处理逻辑代码
- //返回多个值
- return value1, value2
- }
-
-上面的代码我们看出
-
-- 关键字`func`用来声明一个函数`funcName`
-- 函数可以有一个或者多个参数,每个参数后面带有类型,通过`,`分隔
-- 函数可以返回多个值
-- 上面返回值声明了两个变量`output1`和`output2`,如果你不想声明也可以,直接就两个类型
-- 如果只有一个返回值且不声明返回值变量,那么你可以省略 包括返回值 的括号
-- 如果没有返回值,那么就直接省略最后的返回信息
-- 如果有返回值, 那么必须在函数的外层添加return语句
-
-下面我们来看一个实际应用函数的例子(用来计算Max值)
-
- package main
- import "fmt"
-
- // 返回a、b中最大值.
- func max(a, b int) int {
- if a > b {
- return a
- }
- return b
- }
-
- func main() {
- x := 3
- y := 4
- z := 5
-
- max_xy := max(x, y) //调用函数max(x, y)
- max_xz := max(x, z) //调用函数max(x, z)
-
- fmt.Printf("max(%d, %d) = %d\n", x, y, max_xy)
- fmt.Printf("max(%d, %d) = %d\n", x, z, max_xz)
- fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // 也可在这直接调用它
- }
-
-上面这个里面我们可以看到`max`函数有两个参数,它们的类型都是`int`,那么第一个变量的类型可以省略(即 a,b int,而非 a int, b int),默认为离它最近的类型,同理多于2个同类型的变量或者返回值。同时我们注意到它的返回值就是一个类型,这个就是省略写法。
-
-### 多个返回值
-Go语言比C更先进的特性,其中一点就是函数能够返回多个值。
-
-我们直接上代码看例子
-
- package main
- import "fmt"
-
- //返回 A+B 和 A*B
- func SumAndProduct(A, B int) (int, int) {
- return A+B, A*B
- }
-
- func main() {
- x := 3
- y := 4
-
- xPLUSy, xTIMESy := SumAndProduct(x, y)
-
- fmt.Printf("%d + %d = %d\n", x, y, xPLUSy)
- fmt.Printf("%d * %d = %d\n", x, y, xTIMESy)
- }
-
-上面的例子我们可以看到直接返回了两个参数,当然我们也可以命名返回参数的变量,这个例子里面只是用了两个类型,我们也可以改成如下这样的定义,然后返回的时候不用带上变量名,因为直接在函数里面初始化了。但如果你的函数是导出的(首字母大写),官方建议:最好命名返回值,因为不命名返回值,虽然使得代码更加简洁了,但是会造成生成的文档可读性差。
-
- func SumAndProduct(A, B int) (add int, Multiplied int) {
- add = A+B
- Multiplied = A*B
- return
- }
-
-### 变参
-Go函数支持变参。接受变参的函数是有着不定数量的参数的。为了做到这点,首先需要定义函数使其接受变参:
-
- func myfunc(arg ...int) {}
-`arg ...int`告诉Go这个函数接受不定数量的参数。注意,这些参数的类型全部是`int`。在函数体中,变量`arg`是一个`int`的`slice`:
-
- for _, n := range arg {
- fmt.Printf("And the number is: %d\n", n)
- }
-
-### 传值与传指针
-当我们传一个参数值到被调用函数里面时,实际上是传了这个值的一份copy,当在被调用函数中修改参数值的时候,调用函数中相应实参不会发生任何变化,因为数值变化只作用在copy上。
-
-为了验证我们上面的说法,我们来看一个例子
-
- package main
- import "fmt"
-
- //简单的一个函数,实现了参数+1的操作
- func add1(a int) int {
- a = a+1 // 我们改变了a的值
- return a //返回一个新值
- }
-
- func main() {
- x := 3
-
- fmt.Println("x = ", x) // 应该输出 "x = 3"
-
- x1 := add1(x) //调用add1(x)
-
- fmt.Println("x+1 = ", x1) // 应该输出"x+1 = 4"
- fmt.Println("x = ", x) // 应该输出"x = 3"
- }
-
-看到了吗?虽然我们调用了`add1`函数,并且在`add1`中执行`a = a+1`操作,但是上面例子中`x`变量的值没有发生变化
-
-理由很简单:因为当我们调用`add1`的时候,`add1`接收的参数其实是`x`的copy,而不是`x`本身。
-
-那你也许会问了,如果真的需要传这个`x`本身,该怎么办呢?
-
-这就牵扯到了所谓的指针。我们知道,变量在内存中是存放于一定地址上的,修改变量实际是修改变量地址处的内存。只有`add1`函数知道`x`变量所在的地址,才能修改`x`变量的值。所以我们需要将`x`所在地址`&x`传入函数,并将函数的参数的类型由`int`改为`*int`,即改为指针类型,才能在函数中修改`x`变量的值。此时参数仍然是按copy传递的,只是copy的是一个指针。请看下面的例子
-
- package main
- import "fmt"
-
- //简单的一个函数,实现了参数+1的操作
- func add1(a *int) int { // 请注意,
- *a = *a+1 // 修改了a的值
- return *a // 返回新值
- }
-
- func main() {
- x := 3
-
- fmt.Println("x = ", x) // 应该输出 "x = 3"
-
- x1 := add1(&x) // 调用 add1(&x) 传x的地址
-
- fmt.Println("x+1 = ", x1) // 应该输出 "x+1 = 4"
- fmt.Println("x = ", x) // 应该输出 "x = 4"
- }
-
-这样,我们就达到了修改`x`的目的。那么到底传指针有什么好处呢?
-
-- 传指针使得多个函数能操作同一个对象。
-- 传指针比较轻量级 (8bytes),只是传内存地址,我们可以用指针传递体积大的结构体。如果用参数值传递的话, 在每次copy上面就会花费相对较多的系统开销(内存和时间)。所以当你要传递大的结构体的时候,用指针是一个明智的选择。
-- Go语言中`channel`,`slice`,`map`这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针。(注:若函数需改变`slice`的长度,则仍需要取地址传递指针)
-
-### defer
-Go语言中有种不错的设计,即延迟(defer)语句,你可以在函数中添加多个defer语句。当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。特别是当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然很容易造成资源泄露等问题。如下代码所示,我们一般写打开一个资源是这样操作的:
-
- func ReadWrite() bool {
- file.Open("file")
- // 做一些工作
- if failureX {
- file.Close()
- return false
- }
-
- if failureY {
- file.Close()
- return false
- }
-
- file.Close()
- return true
- }
-
-我们看到上面有很多重复的代码,Go的`defer`有效解决了这个问题。使用它后,不但代码量减少了很多,而且程序变得更优雅。在`defer`后指定的函数会在函数退出前调用。
-
- func ReadWrite() bool {
- file.Open("file")
- defer file.Close()
- if failureX {
- return false
- }
- if failureY {
- return false
- }
- return true
- }
-
-如果有很多调用`defer`,那么`defer`是采用后进先出模式,所以如下代码会输出`4 3 2 1 0`
-
- for i := 0; i < 5; i++ {
- defer fmt.Printf("%d ", i)
- }
-
-### 函数作为值、类型
-
-在Go中函数也是一种变量,我们可以通过`type`来定义它,它的类型就是所有拥有相同的参数,相同的返回值的一种类型
-
- type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...])
-
-函数作为类型到底有什么好处呢?那就是可以把这个类型的函数当做值来传递,请看下面的例子
-
- package main
- import "fmt"
-
- type testInt func(int) bool // 声明了一个函数类型
-
- func isOdd(integer int) bool {
- if integer%2 == 0 {
- return false
- }
- return true
- }
-
- func isEven(integer int) bool {
- if integer%2 == 0 {
- return true
- }
- return false
- }
-
- // 声明的函数类型在这个地方当做了一个参数
-
- func filter(slice []int, f testInt) []int {
- var result []int
- for _, value := range slice {
- if f(value) {
- result = append(result, value)
- }
- }
- return result
- }
-
- func main(){
- slice := []int {1, 2, 3, 4, 5, 7}
- fmt.Println("slice = ", slice)
- odd := filter(slice, isOdd) // 函数当做值来传递了
- fmt.Println("Odd elements of slice are: ", odd)
- even := filter(slice, isEven) // 函数当做值来传递了
- fmt.Println("Even elements of slice are: ", even)
- }
-
-函数当做值和类型在我们写一些通用接口的时候非常有用,通过上面例子我们看到`testInt`这个类型是一个函数类型,然后两个`filter`函数的参数和返回值与`testInt`类型是一样的,但是我们可以实现很多种的逻辑,这样使得我们的程序变得非常的灵活。
-
-### Panic和Recover
-
-Go没有像Java那样的异常机制,它不能抛出异常,而是使用了`panic`和`recover`机制。一定要记住,你应当把它作为最后的手段来使用,也就是说,你的代码中应当没有,或者很少有`panic`的东西。这是个强大的工具,请明智地使用它。那么,我们应该如何使用它呢?
-
-Panic
->是一个内建函数,可以中断原有的控制流程,进入一个令人恐慌的流程中。当函数`F`调用`panic`,函数F的执行被中断,但是`F`中的延迟函数会正常执行,然后F返回到调用它的地方。在调用的地方,`F`的行为就像调用了`panic`。这一过程继续向上,直到发生`panic`的`goroutine`中所有调用的函数返回,此时程序退出。恐慌可以直接调用`panic`产生。也可以由运行时错误产生,例如访问越界的数组。
-
-Recover
->是一个内建的函数,可以让进入令人恐慌的流程中的`goroutine`恢复过来。`recover`仅在延迟函数中有效。在正常的执行过程中,调用`recover`会返回`nil`,并且没有其它任何效果。如果当前的`goroutine`陷入恐慌,调用`recover`可以捕获到`panic`的输入值,并且恢复正常的执行。
-
-下面这个函数演示了如何在过程中使用`panic`
-
- var user = os.Getenv("USER")
-
- func init() {
- if user == "" {
- panic("no value for $USER")
- }
- }
-
-下面这个函数检查作为其参数的函数在执行时是否会产生`panic`:
-
- func throwsPanic(f func()) (b bool) {
- defer func() {
- if x := recover(); x != nil {
- b = true
- }
- }()
- f() //执行函数f,如果f中出现了panic,那么就可以恢复回来
- return
- }
-
-### `main`函数和`init`函数
-
-Go里面有两个保留的函数:`init`函数(能够应用于所有的`package`)和`main`函数(只能应用于`package main`)。这两个函数在定义时不能有任何的参数和返回值。虽然一个`package`里面可以写任意多个`init`函数,但这无论是对于可读性还是以后的可维护性来说,我们都强烈建议用户在一个`package`中每个文件只写一个`init`函数。
-
-Go程序会自动调用`init()`和`main()`,所以你不需要在任何地方调用这两个函数。每个`package`中的`init`函数都是可选的,但`package main`就必须包含一个`main`函数。
-
-程序的初始化和执行都起始于`main`包。如果`main`包还导入了其它的包,那么就会在编译时将它们依次导入。有时一个包会被多个包同时导入,那么它只会被导入一次(例如很多包可能都会用到`fmt`包,但它只会被导入一次,因为没有必要导入多次)。当一个包被导入时,如果该包还导入了其它的包,那么会先将其它包导入进来,然后再对这些包中的包级常量和变量进行初始化,接着执行`init`函数(如果有的话),依次类推。等所有被导入的包都加载完毕了,就会开始对`main`包中的包级常量和变量进行初始化,然后执行`main`包中的`init`函数(如果存在的话),最后执行`main`函数。下图详细地解释了整个执行过程:
-
-
-
-图2.6 main函数引入包初始化流程图
-
-### import
-我们在写Go代码的时候经常用到import这个命令用来导入包文件,而我们经常看到的方式参考如下:
-
- import(
- "fmt"
- )
-
-然后我们代码里面可以通过如下的方式调用
-
- fmt.Println("hello world")
-
-上面这个fmt是Go语言的标准库,其实是去`GOROOT`环境变量指定目录下去加载该模块,当然Go的import还支持如下两种方式来加载自己写的模块:
-
-1. 相对路径
-
- import “./model” //当前文件同一目录的model目录,但是不建议这种方式来import
-
-2. 绝对路径
-
- import “shorturl/model” //加载gopath/src/shorturl/model模块
-
-
-上面展示了一些import常用的几种方式,但是还有一些特殊的import,让很多新手很费解,下面我们来一一讲解一下到底是怎么一回事
-
-
-1. 点操作
-
- 我们有时候会看到如下的方式导入包
-
- import(
- . "fmt"
- )
-
- 这个点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名,也就是前面你调用的fmt.Println("hello world")可以省略的写成Println("hello world")
-
-2. 别名操作
-
- 别名操作顾名思义我们可以把包命名成另一个我们用起来容易记忆的名字
-
- import(
- f "fmt"
- )
-
- 别名操作的话调用包函数时前缀变成了我们的前缀,即f.Println("hello world")
-
-3. _操作
-
- 这个操作经常是让很多人费解的一个操作符,请看下面这个import
-
- import (
- "database/sql"
- _ "github.com/ziutek/mymysql/godrv"
- )
-
- _操作其实是引入该包,而不直接使用包里面的函数,而是调用了该包里面的init函数。
-
-
-## links
- * [目录]()
- * 上一章: [Go基础](<02.2.md>)
- * 下一节: [struct类型](<02.4.md>)
+# 2.3 流程和函数
+这小节我们要介绍Go里面的流程控制以及函数操作。
+## 流程控制
+流程控制在编程语言中是最伟大的发明了,因为有了它,你可以通过很简单的流程描述来表达很复杂的逻辑。Go中流程控制分三大类:条件判断,循环控制和无条件跳转。
+### if
+`if`也许是各种编程语言中最常见的了,它的语法概括起来就是:如果满足条件就做某事,否则做另一件事。
+
+Go里面`if`条件判断语句中不需要括号,如下代码所示
+
+ if x > 10 {
+ fmt.Println("x is greater than 10")
+ } else {
+ fmt.Println("x is less than 10")
+ }
+
+Go的`if`还有一个强大的地方就是条件判断语句里面允许声明一个变量,这个变量的作用域只能在该条件逻辑块内,其他地方就不起作用了,如下所示
+
+ // 计算获取值x,然后根据x返回的大小,判断是否大于10。
+ if x := computedValue(); x > 10 {
+ fmt.Println("x is greater than 10")
+ } else {
+ fmt.Println("x is less than 10")
+ }
+
+ //这个地方如果这样调用就编译出错了,因为x是条件里面的变量
+ fmt.Println(x)
+
+多个条件的时候如下所示:
+
+ if integer == 3 {
+ fmt.Println("The integer is equal to 3")
+ } else if integer < 3 {
+ fmt.Println("The integer is less than 3")
+ } else {
+ fmt.Println("The integer is greater than 3")
+ }
+
+### goto
+
+Go有`goto`语句——请明智地使用它。用`goto`跳转到必须在当前函数内定义的标签。例如假设这样一个循环:
+
+ func myFunc() {
+ i := 0
+ Here: //这行的第一个词,以冒号结束作为标签
+ println(i)
+ i++
+ goto Here //跳转到Here去
+ }
+
+>标签名是大小写敏感的。
+
+### for
+Go里面最强大的一个控制逻辑就是`for`,它即可以用来循环读取数据,又可以当作`while`来控制逻辑,还能迭代操作。它的语法如下:
+
+ for expression1; expression2; expression3 {
+ //...
+ }
+
+`expression1`、`expression2`和`expression3`都是表达式,其中`expression1`和`expression3`是变量声明或者函数调用返回值之类的,`expression2`是用来条件判断,`expression1`在循环开始之前调用,`expression3`在每轮循环结束之时调用。
+
+一个例子比上面讲那么多更有用,那么我们看看下面的例子吧:
+
+ package main
+ import "fmt"
+
+ func main(){
+ sum := 0;
+ for index:=0; index < 10 ; index++ {
+ sum += index
+ }
+ fmt.Println("sum is equal to ", sum)
+ }
+ // 输出:sum is equal to 45
+
+有些时候需要进行多个赋值操作,由于Go里面没有`,`操作符,那么可以使用平行赋值`i, j = i+1, j-1`
+
+
+有些时候如果我们忽略`expression1`和`expression3`:
+
+ sum := 1
+ for ; sum < 1000; {
+ sum += sum
+ }
+
+其中`;`也可以省略,那么就变成如下的代码了,是不是似曾相识?对,这就是`while`的功能。
+
+ sum := 1
+ for sum < 1000 {
+ sum += sum
+ }
+
+在循环里面有两个关键操作`break`和`continue` ,`break`操作是跳出当前循环,`continue`是跳过本次循环。当嵌套过深的时候,`break`可以配合标签使用,即跳转至标签所指定的位置,详细参考如下例子:
+
+ for index := 10; index>0; index-- {
+ if index == 5{
+ break // 或者continue
+ }
+ fmt.Println(index)
+ }
+ // break打印出来10、9、8、7、6
+ // continue打印出来10、9、8、7、6、4、3、2、1
+
+`break`和`continue`还可以跟着标号,用来跳到多重循环中的外层循环
+
+`for`配合`range`可以用于读取`slice`和`map`的数据:
+
+ for k,v:=range map {
+ fmt.Println("map's key:",k)
+ fmt.Println("map's val:",v)
+ }
+
+由于 Go 支持 “多值返回”, 而对于“声明而未被调用”的变量, 编译器会报错, 在这种情况下, 可以使用`_`来丢弃不需要的返回值
+例如
+
+ for _, v := range map{
+ fmt.Println("map's val:", v)
+ }
+
+
+### switch
+有些时候你需要写很多的`if-else`来实现一些逻辑处理,这个时候代码看上去就很丑很冗长,而且也不易于以后的维护,这个时候`switch`就能很好的解决这个问题。它的语法如下
+
+ switch sExpr {
+ case expr1:
+ some instructions
+ case expr2:
+ some other instructions
+ case expr3:
+ some other instructions
+ default:
+ other code
+ }
+
+`sExpr`和`expr1`、`expr2`、`expr3`的类型必须一致。Go的`switch`非常灵活,表达式不必是常量或整数,执行的过程从上至下,直到找到匹配项;而如果`switch`没有表达式,它会匹配`true`。
+
+ i := 10
+ switch i {
+ case 1:
+ fmt.Println("i is equal to 1")
+ case 2, 3, 4:
+ fmt.Println("i is equal to 2, 3 or 4")
+ case 10:
+ fmt.Println("i is equal to 10")
+ default:
+ fmt.Println("All I know is that i is an integer")
+ }
+
+在第5行中,我们把很多值聚合在了一个`case`里面,同时,Go里面`switch`默认相当于每个`case`最后带有`break`,匹配成功后不会自动向下执行其他case,而是跳出整个`switch`, 但是可以使用`fallthrough`强制执行后面的case代码。
+
+ integer := 6
+ switch integer {
+ case 4:
+ fmt.Println("The integer was <= 4")
+ fallthrough
+ case 5:
+ fmt.Println("The integer was <= 5")
+ fallthrough
+ case 6:
+ fmt.Println("The integer was <= 6")
+ fallthrough
+ case 7:
+ fmt.Println("The integer was <= 7")
+ fallthrough
+ case 8:
+ fmt.Println("The integer was <= 8")
+ fallthrough
+ default:
+ fmt.Println("default case")
+ }
+
+上面的程序将输出
+
+ The integer was <= 6
+ The integer was <= 7
+ The integer was <= 8
+ default case
+
+
+## 函数
+函数是Go里面的核心设计,它通过关键字`func`来声明,它的格式如下:
+
+ func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
+ //这里是处理逻辑代码
+ //返回多个值
+ return value1, value2
+ }
+
+上面的代码我们看出
+
+- 关键字`func`用来声明一个函数`funcName`
+- 函数可以有一个或者多个参数,每个参数后面带有类型,通过`,`分隔
+- 函数可以返回多个值
+- 上面返回值声明了两个变量`output1`和`output2`,如果你不想声明也可以,直接就两个类型
+- 如果只有一个返回值且不声明返回值变量,那么你可以省略 包括返回值 的括号
+- 如果没有返回值,那么就直接省略最后的返回信息
+- 如果有返回值, 那么必须在函数的外层添加return语句
+
+下面我们来看一个实际应用函数的例子(用来计算Max值)
+
+ package main
+ import "fmt"
+
+ // 返回a、b中最大值.
+ func max(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+ }
+
+ func main() {
+ x := 3
+ y := 4
+ z := 5
+
+ max_xy := max(x, y) //调用函数max(x, y)
+ max_xz := max(x, z) //调用函数max(x, z)
+
+ fmt.Printf("max(%d, %d) = %d\n", x, y, max_xy)
+ fmt.Printf("max(%d, %d) = %d\n", x, z, max_xz)
+ fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // 也可在这直接调用它
+ }
+
+上面这个里面我们可以看到`max`函数有两个参数,它们的类型都是`int`,那么第一个变量的类型可以省略(即 a,b int,而非 a int, b int),默认为离它最近的类型,同理多于2个同类型的变量或者返回值。同时我们注意到它的返回值就是一个类型,这个就是省略写法。
+
+### 多个返回值
+Go语言比C更先进的特性,其中一点就是函数能够返回多个值。
+
+我们直接上代码看例子
+
+ package main
+ import "fmt"
+
+ //返回 A+B 和 A*B
+ func SumAndProduct(A, B int) (int, int) {
+ return A+B, A*B
+ }
+
+ func main() {
+ x := 3
+ y := 4
+
+ xPLUSy, xTIMESy := SumAndProduct(x, y)
+
+ fmt.Printf("%d + %d = %d\n", x, y, xPLUSy)
+ fmt.Printf("%d * %d = %d\n", x, y, xTIMESy)
+ }
+
+上面的例子我们可以看到直接返回了两个参数,当然我们也可以命名返回参数的变量,这个例子里面只是用了两个类型,我们也可以改成如下这样的定义,然后返回的时候不用带上变量名,因为直接在函数里面初始化了。但如果你的函数是导出的(首字母大写),官方建议:最好命名返回值,因为不命名返回值,虽然使得代码更加简洁了,但是会造成生成的文档可读性差。
+
+ func SumAndProduct(A, B int) (add int, Multiplied int) {
+ add = A+B
+ Multiplied = A*B
+ return
+ }
+
+### 变参
+Go函数支持变参。接受变参的函数是有着不定数量的参数的。为了做到这点,首先需要定义函数使其接受变参:
+
+ func myfunc(arg ...int) {}
+`arg ...int`告诉Go这个函数接受不定数量的参数。注意,这些参数的类型全部是`int`。在函数体中,变量`arg`是一个`int`的`slice`:
+
+ for _, n := range arg {
+ fmt.Printf("And the number is: %d\n", n)
+ }
+
+### 传值与传指针
+当我们传一个参数值到被调用函数里面时,实际上是传了这个值的一份copy,当在被调用函数中修改参数值的时候,调用函数中相应实参不会发生任何变化,因为数值变化只作用在copy上。
+
+为了验证我们上面的说法,我们来看一个例子
+
+ package main
+ import "fmt"
+
+ //简单的一个函数,实现了参数+1的操作
+ func add1(a int) int {
+ a = a+1 // 我们改变了a的值
+ return a //返回一个新值
+ }
+
+ func main() {
+ x := 3
+
+ fmt.Println("x = ", x) // 应该输出 "x = 3"
+
+ x1 := add1(x) //调用add1(x)
+
+ fmt.Println("x+1 = ", x1) // 应该输出"x+1 = 4"
+ fmt.Println("x = ", x) // 应该输出"x = 3"
+ }
+
+看到了吗?虽然我们调用了`add1`函数,并且在`add1`中执行`a = a+1`操作,但是上面例子中`x`变量的值没有发生变化
+
+理由很简单:因为当我们调用`add1`的时候,`add1`接收的参数其实是`x`的copy,而不是`x`本身。
+
+那你也许会问了,如果真的需要传这个`x`本身,该怎么办呢?
+
+这就牵扯到了所谓的指针。我们知道,变量在内存中是存放于一定地址上的,修改变量实际是修改变量地址处的内存。只有`add1`函数知道`x`变量所在的地址,才能修改`x`变量的值。所以我们需要将`x`所在地址`&x`传入函数,并将函数的参数的类型由`int`改为`*int`,即改为指针类型,才能在函数中修改`x`变量的值。此时参数仍然是按copy传递的,只是copy的是一个指针。请看下面的例子
+
+ package main
+ import "fmt"
+
+ //简单的一个函数,实现了参数+1的操作
+ func add1(a *int) int { // 请注意,
+ *a = *a+1 // 修改了a的值
+ return *a // 返回新值
+ }
+
+ func main() {
+ x := 3
+
+ fmt.Println("x = ", x) // 应该输出 "x = 3"
+
+ x1 := add1(&x) // 调用 add1(&x) 传x的地址
+
+ fmt.Println("x+1 = ", x1) // 应该输出 "x+1 = 4"
+ fmt.Println("x = ", x) // 应该输出 "x = 4"
+ }
+
+这样,我们就达到了修改`x`的目的。那么到底传指针有什么好处呢?
+
+- 传指针使得多个函数能操作同一个对象。
+- 传指针比较轻量级 (8bytes),只是传内存地址,我们可以用指针传递体积大的结构体。如果用参数值传递的话, 在每次copy上面就会花费相对较多的系统开销(内存和时间)。所以当你要传递大的结构体的时候,用指针是一个明智的选择。
+- Go语言中`channel`,`slice`,`map`这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针。(注:若函数需改变`slice`的长度,则仍需要取地址传递指针)
+
+### defer
+Go语言中有种不错的设计,即延迟(defer)语句,你可以在函数中添加多个defer语句。当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。特别是当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然很容易造成资源泄露等问题。如下代码所示,我们一般写打开一个资源是这样操作的:
+
+ func ReadWrite() bool {
+ file.Open("file")
+ // 做一些工作
+ if failureX {
+ file.Close()
+ return false
+ }
+
+ if failureY {
+ file.Close()
+ return false
+ }
+
+ file.Close()
+ return true
+ }
+
+我们看到上面有很多重复的代码,Go的`defer`有效解决了这个问题。使用它后,不但代码量减少了很多,而且程序变得更优雅。在`defer`后指定的函数会在函数退出前调用。
+
+ func ReadWrite() bool {
+ file.Open("file")
+ defer file.Close()
+ if failureX {
+ return false
+ }
+ if failureY {
+ return false
+ }
+ return true
+ }
+
+如果有很多调用`defer`,那么`defer`是采用后进先出模式,所以如下代码会输出`4 3 2 1 0`
+
+ for i := 0; i < 5; i++ {
+ defer fmt.Printf("%d ", i)
+ }
+
+### 函数作为值、类型
+
+在Go中函数也是一种变量,我们可以通过`type`来定义它,它的类型就是所有拥有相同的参数,相同的返回值的一种类型
+
+ type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...])
+
+函数作为类型到底有什么好处呢?那就是可以把这个类型的函数当做值来传递,请看下面的例子
+
+ package main
+ import "fmt"
+
+ type testInt func(int) bool // 声明了一个函数类型
+
+ func isOdd(integer int) bool {
+ if integer%2 == 0 {
+ return false
+ }
+ return true
+ }
+
+ func isEven(integer int) bool {
+ if integer%2 == 0 {
+ return true
+ }
+ return false
+ }
+
+ // 声明的函数类型在这个地方当做了一个参数
+
+ func filter(slice []int, f testInt) []int {
+ var result []int
+ for _, value := range slice {
+ if f(value) {
+ result = append(result, value)
+ }
+ }
+ return result
+ }
+
+ func main(){
+ slice := []int {1, 2, 3, 4, 5, 7}
+ fmt.Println("slice = ", slice)
+ odd := filter(slice, isOdd) // 函数当做值来传递了
+ fmt.Println("Odd elements of slice are: ", odd)
+ even := filter(slice, isEven) // 函数当做值来传递了
+ fmt.Println("Even elements of slice are: ", even)
+ }
+
+函数当做值和类型在我们写一些通用接口的时候非常有用,通过上面例子我们看到`testInt`这个类型是一个函数类型,然后两个`filter`函数的参数和返回值与`testInt`类型是一样的,但是我们可以实现很多种的逻辑,这样使得我们的程序变得非常的灵活。
+
+### Panic和Recover
+
+Go没有像Java那样的异常机制,它不能抛出异常,而是使用了`panic`和`recover`机制。一定要记住,你应当把它作为最后的手段来使用,也就是说,你的代码中应当没有,或者很少有`panic`的东西。这是个强大的工具,请明智地使用它。那么,我们应该如何使用它呢?
+
+Panic
+>是一个内建函数,可以中断原有的控制流程,进入一个令人恐慌的流程中。当函数`F`调用`panic`,函数F的执行被中断,但是`F`中的延迟函数会正常执行,然后F返回到调用它的地方。在调用的地方,`F`的行为就像调用了`panic`。这一过程继续向上,直到发生`panic`的`goroutine`中所有调用的函数返回,此时程序退出。恐慌可以直接调用`panic`产生。也可以由运行时错误产生,例如访问越界的数组。
+
+Recover
+>是一个内建的函数,可以让进入令人恐慌的流程中的`goroutine`恢复过来。`recover`仅在延迟函数中有效。在正常的执行过程中,调用`recover`会返回`nil`,并且没有其它任何效果。如果当前的`goroutine`陷入恐慌,调用`recover`可以捕获到`panic`的输入值,并且恢复正常的执行。
+
+下面这个函数演示了如何在过程中使用`panic`
+
+ var user = os.Getenv("USER")
+
+ func init() {
+ if user == "" {
+ panic("no value for $USER")
+ }
+ }
+
+下面这个函数检查作为其参数的函数在执行时是否会产生`panic`:
+
+ func throwsPanic(f func()) (b bool) {
+ defer func() {
+ if x := recover(); x != nil {
+ b = true
+ }
+ }()
+ f() //执行函数f,如果f中出现了panic,那么就可以恢复回来
+ return
+ }
+
+### `main`函数和`init`函数
+
+Go里面有两个保留的函数:`init`函数(能够应用于所有的`package`)和`main`函数(只能应用于`package main`)。这两个函数在定义时不能有任何的参数和返回值。虽然一个`package`里面可以写任意多个`init`函数,但这无论是对于可读性还是以后的可维护性来说,我们都强烈建议用户在一个`package`中每个文件只写一个`init`函数。
+
+Go程序会自动调用`init()`和`main()`,所以你不需要在任何地方调用这两个函数。每个`package`中的`init`函数都是可选的,但`package main`就必须包含一个`main`函数。
+
+程序的初始化和执行都起始于`main`包。如果`main`包还导入了其它的包,那么就会在编译时将它们依次导入。有时一个包会被多个包同时导入,那么它只会被导入一次(例如很多包可能都会用到`fmt`包,但它只会被导入一次,因为没有必要导入多次)。当一个包被导入时,如果该包还导入了其它的包,那么会先将其它包导入进来,然后再对这些包中的包级常量和变量进行初始化,接着执行`init`函数(如果有的话),依次类推。等所有被导入的包都加载完毕了,就会开始对`main`包中的包级常量和变量进行初始化,然后执行`main`包中的`init`函数(如果存在的话),最后执行`main`函数。下图详细地解释了整个执行过程:
+
+
+
+图2.6 main函数引入包初始化流程图
+
+### import
+我们在写Go代码的时候经常用到import这个命令用来导入包文件,而我们经常看到的方式参考如下:
+
+ import(
+ "fmt"
+ )
+
+然后我们代码里面可以通过如下的方式调用
+
+ fmt.Println("hello world")
+
+上面这个fmt是Go语言的标准库,其实是去`GOROOT`环境变量指定目录下去加载该模块,当然Go的import还支持如下两种方式来加载自己写的模块:
+
+1. 相对路径
+
+ import “./model” //当前文件同一目录的model目录,但是不建议这种方式来import
+
+2. 绝对路径
+
+ import “shorturl/model” //加载gopath/src/shorturl/model模块
+
+
+上面展示了一些import常用的几种方式,但是还有一些特殊的import,让很多新手很费解,下面我们来一一讲解一下到底是怎么一回事
+
+
+1. 点操作
+
+ 我们有时候会看到如下的方式导入包
+
+ import(
+ . "fmt"
+ )
+
+ 这个点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名,也就是前面你调用的fmt.Println("hello world")可以省略的写成Println("hello world")
+
+2. 别名操作
+
+ 别名操作顾名思义我们可以把包命名成另一个我们用起来容易记忆的名字
+
+ import(
+ f "fmt"
+ )
+
+ 别名操作的话调用包函数时前缀变成了我们的前缀,即f.Println("hello world")
+
+3. _操作
+
+ 这个操作经常是让很多人费解的一个操作符,请看下面这个import
+
+ import (
+ "database/sql"
+ _ "github.com/ziutek/mymysql/godrv"
+ )
+
+ _操作其实是引入该包,而不直接使用包里面的函数,而是调用了该包里面的init函数。
+
+
+## links
+ * [目录]()
+ * 上一章: [Go基础](<02.2.md>)
+ * 下一节: [struct类型](<02.4.md>)
diff --git a/zh/02.3.md~eead24cf064976b648de5826eab51880c803b0fa b/zh/02.3.md~eead24cf064976b648de5826eab51880c803b0fa
new file mode 100644
index 00000000..551ccf94
--- /dev/null
+++ b/zh/02.3.md~eead24cf064976b648de5826eab51880c803b0fa
@@ -0,0 +1,520 @@
+# 2.3 流程和函数
+这小节我们要介绍Go里面的流程控制以及函数操作。
+## 流程控制
+流程控制在编程语言中是最伟大的发明了,因为有了它,你可以通过很简单的流程描述来表达很复杂的逻辑。Go中流程控制分三大类:条件判断,循环控制和无条件跳转。
+### if
+`if`也许是各种编程语言中最常见的了,它的语法概括起来就是:如果满足条件就做某事,否则做另一件事。
+
+Go里面`if`条件判断语句中不需要括号,如下代码所示
+
+ if x > 10 {
+ fmt.Println("x is greater than 10")
+ } else {
+ fmt.Println("x is less than 10")
+ }
+
+Go的`if`还有一个强大的地方就是条件判断语句里面允许声明一个变量,这个变量的作用域只能在该条件逻辑块内,其他地方就不起作用了,如下所示
+
+ // 计算获取值x,然后根据x返回的大小,判断是否大于10。
+ if x := computedValue(); x > 10 {
+ fmt.Println("x is greater than 10")
+ } else {
+ fmt.Println("x is less than 10")
+ }
+
+ //这个地方如果这样调用就编译出错了,因为x是条件里面的变量
+ fmt.Println(x)
+
+多个条件的时候如下所示:
+
+ if integer == 3 {
+ fmt.Println("The integer is equal to 3")
+ } else if integer < 3 {
+ fmt.Println("The integer is less than 3")
+ } else {
+ fmt.Println("The integer is greater than 3")
+ }
+
+### goto
+
+Go有`goto`语句——请明智地使用它。用`goto`跳转到必须在当前函数内定义的标签。例如假设这样一个循环:
+
+ func myFunc() {
+ i := 0
+ Here: //这行的第一个词,以冒号结束作为标签
+ println(i)
+ i++
+ goto Here //跳转到Here去
+ }
+
+>标签名是大小写敏感的。
+
+### for
+Go里面最强大的一个控制逻辑就是`for`,它即可以用来循环读取数据,又可以当作`while`来控制逻辑,还能迭代操作。它的语法如下:
+
+ for expression1; expression2; expression3 {
+ //...
+ }
+
+`expression1`、`expression2`和`expression3`都是表达式,其中`expression1`和`expression3`是变量声明或者函数调用返回值之类的,`expression2`是用来条件判断,`expression1`在循环开始之前调用,`expression3`在每轮循环结束之时调用。
+
+一个例子比上面讲那么多更有用,那么我们看看下面的例子吧:
+
+ package main
+ import "fmt"
+
+ func main(){
+ sum := 0;
+ for index:=0; index < 10 ; index++ {
+ sum += index
+ }
+ fmt.Println("sum is equal to ", sum)
+ }
+ // 输出:sum is equal to 45
+
+有些时候需要进行多个赋值操作,由于Go里面没有`,`操作符,那么可以使用平行赋值`i, j = i+1, j-1`
+
+
+有些时候如果我们忽略`expression1`和`expression3`:
+
+ sum := 1
+ for ; sum < 1000; {
+ sum += sum
+ }
+
+其中`;`也可以省略,那么就变成如下的代码了,是不是似曾相识?对,这就是`while`的功能。
+
+ sum := 1
+ for sum < 1000 {
+ sum += sum
+ }
+
+在循环里面有两个关键操作`break`和`continue` ,`break`操作是跳出当前循环,`continue`是跳过本次循环。当嵌套过深的时候,`break`可以配合标签使用,即跳转至标签所指定的位置,详细参考如下例子:
+
+ for index := 10; index>0; index-- {
+ if index == 5{
+ break // 或者continue
+ }
+ fmt.Println(index)
+ }
+ // break打印出来10、9、8、7、6
+ // continue打印出来10、9、8、7、6、4、3、2、1
+
+`break`和`continue`还可以跟着标号,用来跳到多重循环中的外层循环
+
+`for`配合`range`可以用于读取`slice`和`map`的数据:
+
+ for k,v:=range map {
+ fmt.Println("map's key:",k)
+ fmt.Println("map's val:",v)
+ }
+
+由于 Go 支持 “多值返回”, 而对于“声明而未被调用”的变量, 编译器会报错, 在这种情况下, 可以使用`_`来丢弃不需要的返回值
+例如
+
+ for _, v := range map{
+ fmt.Println("map's val:", v)
+ }
+
+
+### switch
+有些时候你需要写很多的`if-else`来实现一些逻辑处理,这个时候代码看上去就很丑很冗长,而且也不易于以后的维护,这个时候`switch`就能很好的解决这个问题。它的语法如下
+
+ switch sExpr {
+ case expr1:
+ some instructions
+ case expr2:
+ some other instructions
+ case expr3:
+ some other instructions
+ default:
+ other code
+ }
+
+`sExpr`和`expr1`、`expr2`、`expr3`的类型必须一致。Go的`switch`非常灵活,表达式不必是常量或整数,执行的过程从上至下,直到找到匹配项;而如果`switch`没有表达式,它会匹配`true`。
+
+ i := 10
+ switch i {
+ case 1:
+ fmt.Println("i is equal to 1")
+ case 2, 3, 4:
+ fmt.Println("i is equal to 2, 3 or 4")
+ case 10:
+ fmt.Println("i is equal to 10")
+ default:
+ fmt.Println("All I know is that i is an integer")
+ }
+
+在第5行中,我们把很多值聚合在了一个`case`里面,同时,Go里面`switch`默认相当于每个`case`最后带有`break`,匹配成功后不会自动向下执行其他case,而是跳出整个`switch`, 但是可以使用`fallthrough`强制执行后面的case代码。
+
+ integer := 6
+ switch integer {
+ case 4:
+ fmt.Println("The integer was <= 4")
+ fallthrough
+ case 5:
+ fmt.Println("The integer was <= 5")
+ fallthrough
+ case 6:
+ fmt.Println("The integer was <= 6")
+ fallthrough
+ case 7:
+ fmt.Println("The integer was <= 7")
+ fallthrough
+ case 8:
+ fmt.Println("The integer was <= 8")
+ fallthrough
+ default:
+ fmt.Println("default case")
+ }
+
+上面的程序将输出
+
+ The integer was <= 6
+ The integer was <= 7
+ The integer was <= 8
+ default case
+
+
+## 函数
+函数是Go里面的核心设计,它通过关键字`func`来声明,它的格式如下:
+
+ func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
+ //这里是处理逻辑代码
+ //返回多个值
+ return value1, value2
+ }
+
+上面的代码我们看出
+
+- 关键字`func`用来声明一个函数`funcName`
+- 函数可以有一个或者多个参数,每个参数后面带有类型,通过`,`分隔
+- 函数可以返回多个值
+- 上面返回值声明了两个变量`output1`和`output2`,如果你不想声明也可以,直接就两个类型
+- 如果只有一个返回值且不声明返回值变量,那么你可以省略 包括返回值 的括号
+- 如果没有返回值,那么就直接省略最后的返回信息
+- 如果有返回值, 那么必须在函数的外层添加return语句
+
+下面我们来看一个实际应用函数的例子(用来计算Max值)
+
+ package main
+ import "fmt"
+
+ // 返回a、b中最大值.
+ func max(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+ }
+
+ func main() {
+ x := 3
+ y := 4
+ z := 5
+
+ max_xy := max(x, y) //调用函数max(x, y)
+ max_xz := max(x, z) //调用函数max(x, z)
+
+ fmt.Printf("max(%d, %d) = %d\n", x, y, max_xy)
+ fmt.Printf("max(%d, %d) = %d\n", x, z, max_xz)
+ fmt.Printf("max(%d, %d) = %d\n", y, z, max(y,z)) // 也可在这直接调用它
+ }
+
+上面这个里面我们可以看到`max`函数有两个参数,它们的类型都是`int`,那么第一个变量的类型可以省略(即 a,b int,而非 a int, b int),默认为离它最近的类型,同理多于2个同类型的变量或者返回值。同时我们注意到它的返回值就是一个类型,这个就是省略写法。
+
+### 多个返回值
+Go语言比C更先进的特性,其中一点就是函数能够返回多个值。
+
+我们直接上代码看例子
+
+ package main
+ import "fmt"
+
+ //返回 A+B 和 A*B
+ func SumAndProduct(A, B int) (int, int) {
+ return A+B, A*B
+ }
+
+ func main() {
+ x := 3
+ y := 4
+
+ xPLUSy, xTIMESy := SumAndProduct(x, y)
+
+ fmt.Printf("%d + %d = %d\n", x, y, xPLUSy)
+ fmt.Printf("%d * %d = %d\n", x, y, xTIMESy)
+ }
+
+上面的例子我们可以看到直接返回了两个参数,当然我们也可以命名返回参数的变量,这个例子里面只是用了两个类型,我们也可以改成如下这样的定义,然后返回的时候不用带上变量名,因为直接在函数里面初始化了。但如果你的函数是导出的(首字母大写),官方建议:最好命名返回值,因为不命名返回值,虽然使得代码更加简洁了,但是会造成生成的文档可读性差。
+
+ func SumAndProduct(A, B int) (add int, Multiplied int) {
+ add = A+B
+ Multiplied = A*B
+ return
+ }
+
+### 变参
+Go函数支持变参。接受变参的函数是有着不定数量的参数的。为了做到这点,首先需要定义函数使其接受变参:
+
+ func myfunc(arg ...int) {}
+`arg ...int`告诉Go这个函数接受不定数量的参数。注意,这些参数的类型全部是`int`。在函数体中,变量`arg`是一个`int`的`slice`:
+
+ for _, n := range arg {
+ fmt.Printf("And the number is: %d\n", n)
+ }
+
+### 传值与传指针
+当我们传一个参数值到被调用函数里面时,实际上是传了这个值的一份copy,当在被调用函数中修改参数值的时候,调用函数中相应实参不会发生任何变化,因为数值变化只作用在copy上。
+
+为了验证我们上面的说法,我们来看一个例子
+
+ package main
+ import "fmt"
+
+ //简单的一个函数,实现了参数+1的操作
+ func add1(a int) int {
+ a = a+1 // 我们改变了a的值
+ return a //返回一个新值
+ }
+
+ func main() {
+ x := 3
+
+ fmt.Println("x = ", x) // 应该输出 "x = 3"
+
+ x1 := add1(x) //调用add1(x)
+
+ fmt.Println("x+1 = ", x1) // 应该输出"x+1 = 4"
+ fmt.Println("x = ", x) // 应该输出"x = 3"
+ }
+
+看到了吗?虽然我们调用了`add1`函数,并且在`add1`中执行`a = a+1`操作,但是上面例子中`x`变量的值没有发生变化
+
+理由很简单:因为当我们调用`add1`的时候,`add1`接收的参数其实是`x`的copy,而不是`x`本身。
+
+那你也许会问了,如果真的需要传这个`x`本身,该怎么办呢?
+
+这就牵扯到了所谓的指针。我们知道,变量在内存中是存放于一定地址上的,修改变量实际是修改变量地址处的内存。只有`add1`函数知道`x`变量所在的地址,才能修改`x`变量的值。所以我们需要将`x`所在地址`&x`传入函数,并将函数的参数的类型由`int`改为`*int`,即改为指针类型,才能在函数中修改`x`变量的值。此时参数仍然是按copy传递的,只是copy的是一个指针。请看下面的例子
+
+ package main
+ import "fmt"
+
+ //简单的一个函数,实现了参数+1的操作
+ func add1(a *int) int { // 请注意,
+ *a = *a+1 // 修改了a的值
+ return *a // 返回新值
+ }
+
+ func main() {
+ x := 3
+
+ fmt.Println("x = ", x) // 应该输出 "x = 3"
+
+ x1 := add1(&x) // 调用 add1(&x) 传x的地址
+
+ fmt.Println("x+1 = ", x1) // 应该输出 "x+1 = 4"
+ fmt.Println("x = ", x) // 应该输出 "x = 4"
+ }
+
+这样,我们就达到了修改`x`的目的。那么到底传指针有什么好处呢?
+
+- 传指针使得多个函数能操作同一个对象。
+- 传指针比较轻量级 (8bytes),只是传内存地址,我们可以用指针传递体积大的结构体。如果用参数值传递的话, 在每次copy上面就会花费相对较多的系统开销(内存和时间)。所以当你要传递大的结构体的时候,用指针是一个明智的选择。
+- Go语言中`channel`,`slice`,`map`这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针。(注:若函数需改变`slice`的长度,则仍需要取地址传递指针)
+
+### defer
+Go语言中有种不错的设计,即延迟(defer)语句,你可以在函数中添加多个defer语句。当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。特别是当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然很容易造成资源泄露等问题。如下代码所示,我们一般写打开一个资源是这样操作的:
+
+ func ReadWrite() bool {
+ file.Open("file")
+ // 做一些工作
+ if failureX {
+ file.Close()
+ return false
+ }
+
+ if failureY {
+ file.Close()
+ return false
+ }
+
+ file.Close()
+ return true
+ }
+
+我们看到上面有很多重复的代码,Go的`defer`有效解决了这个问题。使用它后,不但代码量减少了很多,而且程序变得更优雅。在`defer`后指定的函数会在函数退出前调用。
+
+ func ReadWrite() bool {
+ file.Open("file")
+ defer file.Close()
+ if failureX {
+ return false
+ }
+ if failureY {
+ return false
+ }
+ return true
+ }
+
+如果有很多调用`defer`,那么`defer`是采用后进先出模式,所以如下代码会输出`4 3 2 1 0`
+
+ for i := 0; i < 5; i++ {
+ defer fmt.Printf("%d ", i)
+ }
+
+### 函数作为值、类型
+
+在Go中函数也是一种变量,我们可以通过`type`来定义它,它的类型就是所有拥有相同的参数,相同的返回值的一种类型
+
+ type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...])
+
+函数作为类型到底有什么好处呢?那就是可以把这个类型的函数当做值来传递,请看下面的例子
+
+ package main
+ import "fmt"
+
+ type testInt func(int) bool // 声明了一个函数类型
+
+ func isOdd(integer int) bool {
+ if integer%2 == 0 {
+ return false
+ }
+ return true
+ }
+
+ func isEven(integer int) bool {
+ if integer%2 == 0 {
+ return true
+ }
+ return false
+ }
+
+ // 声明的函数类型在这个地方当做了一个参数
+
+ func filter(slice []int, f testInt) []int {
+ var result []int
+ for _, value := range slice {
+ if f(value) {
+ result = append(result, value)
+ }
+ }
+ return result
+ }
+
+ func main(){
+ slice := []int {1, 2, 3, 4, 5, 7}
+ fmt.Println("slice = ", slice)
+ odd := filter(slice, isOdd) // 函数当做值来传递了
+ fmt.Println("Odd elements of slice are: ", odd)
+ even := filter(slice, isEven) // 函数当做值来传递了
+ fmt.Println("Even elements of slice are: ", even)
+ }
+
+函数当做值和类型在我们写一些通用接口的时候非常有用,通过上面例子我们看到`testInt`这个类型是一个函数类型,然后两个`filter`函数的参数和返回值与`testInt`类型是一样的,但是我们可以实现很多种的逻辑,这样使得我们的程序变得非常的灵活。
+
+### Panic和Recover
+
+Go没有像Java那样的异常机制,它不能抛出异常,而是使用了`panic`和`recover`机制。一定要记住,你应当把它作为最后的手段来使用,也就是说,你的代码中应当没有,或者很少有`panic`的东西。这是个强大的工具,请明智地使用它。那么,我们应该如何使用它呢?
+
+Panic
+>是一个内建函数,可以中断原有的控制流程,进入一个令人恐慌的流程中。当函数`F`调用`panic`,函数F的执行被中断,但是`F`中的延迟函数会正常执行,然后F返回到调用它的地方。在调用的地方,`F`的行为就像调用了`panic`。这一过程继续向上,直到发生`panic`的`goroutine`中所有调用的函数返回,此时程序退出。恐慌可以直接调用`panic`产生。也可以由运行时错误产生,例如访问越界的数组。
+
+Recover
+>是一个内建的函数,可以让进入令人恐慌的流程中的`goroutine`恢复过来。`recover`仅在延迟函数中有效。在正常的执行过程中,调用`recover`会返回`nil`,并且没有其它任何效果。如果当前的`goroutine`陷入恐慌,调用`recover`可以捕获到`panic`的输入值,并且恢复正常的执行。
+
+下面这个函数演示了如何在过程中使用`panic`
+
+ var user = os.Getenv("USER")
+
+ func init() {
+ if user == "" {
+ panic("no value for $USER")
+ }
+ }
+
+下面这个函数检查作为其参数的函数在执行时是否会产生`panic`:
+
+ func throwsPanic(f func()) (b bool) {
+ defer func() {
+ if x := recover(); x != nil {
+ b = true
+ }
+ }()
+ f() //执行函数f,如果f中出现了panic,那么就可以恢复回来
+ return
+ }
+
+### `main`函数和`init`函数
+
+Go里面有两个保留的函数:`init`函数(能够应用于所有的`package`)和`main`函数(只能应用于`package main`)。这两个函数在定义时不能有任何的参数和返回值。虽然一个`package`里面可以写任意多个`init`函数,但这无论是对于可读性还是以后的可维护性来说,我们都强烈建议用户在一个`package`中每个文件只写一个`init`函数。
+
+Go程序会自动调用`init()`和`main()`,所以你不需要在任何地方调用这两个函数。每个`package`中的`init`函数都是可选的,但`package main`就必须包含一个`main`函数。
+
+程序的初始化和执行都起始于`main`包。如果`main`包还导入了其它的包,那么就会在编译时将它们依次导入。有时一个包会被多个包同时导入,那么它只会被导入一次(例如很多包可能都会用到`fmt`包,但它只会被导入一次,因为没有必要导入多次)。当一个包被导入时,如果该包还导入了其它的包,那么会先将其它包导入进来,然后再对这些包中的包级常量和变量进行初始化,接着执行`init`函数(如果有的话),依次类推。等所有被导入的包都加载完毕了,就会开始对`main`包中的包级常量和变量进行初始化,然后执行`main`包中的`init`函数(如果存在的话),最后执行`main`函数。下图详细地解释了整个执行过程:
+
+
+
+图2.6 main函数引入包初始化流程图
+
+### import
+我们在写Go代码的时候经常用到import这个命令用来导入包文件,而我们经常看到的方式参考如下:
+
+ import(
+ "fmt"
+ )
+
+然后我们代码里面可以通过如下的方式调用
+
+ fmt.Println("hello world")
+
+上面这个fmt是Go语言的标准库,其实是去`GOROOT`环境变量指定目录下去加载该模块,当然Go的import还支持如下两种方式来加载自己写的模块:
+
+1. 相对路径
+
+ import “./model” //当前文件同一目录的model目录,但是不建议这种方式来import
+
+2. 绝对路径
+
+ import “shorturl/model” //加载gopath/src/shorturl/model模块
+
+
+上面展示了一些import常用的几种方式,但是还有一些特殊的import,让很多新手很费解,下面我们来一一讲解一下到底是怎么一回事
+
+
+1. 点操作
+
+ 我们有时候会看到如下的方式导入包
+
+ import(
+ . "fmt"
+ )
+
+ 这个点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名,也就是前面你调用的fmt.Println("hello world")可以省略的写成Println("hello world")
+
+2. 别名操作
+
+ 别名操作顾名思义我们可以把包命名成另一个我们用起来容易记忆的名字
+
+ import(
+ f "fmt"
+ )
+
+ 别名操作的话调用包函数时前缀变成了我们的前缀,即f.Println("hello world")
+
+3. _操作
+
+ 这个操作经常是让很多人费解的一个操作符,请看下面这个import
+
+ import (
+ "database/sql"
+ _ "github.com/ziutek/mymysql/godrv"
+ )
+
+ _操作其实是引入该包,而不直接使用包里面的函数,而是调用了该包里面的init函数。
+
+
+## links
+ * [目录]()
+ * 上一章: [Go基础](<02.2.md>)
+ * 下一节: [struct类型](<02.4.md>)
diff --git a/zh/02.4.md b/zh/02.4.md~HEAD
similarity index 97%
rename from zh/02.4.md
rename to zh/02.4.md~HEAD
index 1e273b10..5cdbad44 100644
--- a/zh/02.4.md
+++ b/zh/02.4.md~HEAD
@@ -1,213 +1,213 @@
-# 2.4 struct类型
-## struct
-Go语言中,也和C或者其他语言一样,我们可以声明新的类型,作为其它类型的属性或字段的容器。例如,我们可以创建一个自定义类型`person`代表一个人的实体。这个实体拥有属性:姓名和年龄。这样的类型我们称之`struct`。如下代码所示:
-
- type person struct {
- name string
- age int
- }
-看到了吗?声明一个struct如此简单,上面的类型包含有两个字段
-- 一个string类型的字段name,用来保存用户名称这个属性
-- 一个int类型的字段age,用来保存用户年龄这个属性
-
-如何使用struct呢?请看下面的代码
-
- type person struct {
- name string
- age int
- }
-
- var P person // P现在就是person类型的变量了
-
- P.name = "Astaxie" // 赋值"Astaxie"给P的name属性.
- P.age = 25 // 赋值"25"给变量P的age属性
- fmt.Printf("The person's name is %s", P.name) // 访问P的name属性.
-除了上面这种P的声明使用之外,还有另外几种声明使用方式:
-
-- 1.按照顺序提供初始化值
-
- P := person{"Tom", 25}
-
-- 2.通过`field:value`的方式初始化,这样可以任意顺序
-
- P := person{age:24, name:"Tom"}
-
-- 3.当然也可以通过`new`函数分配一个指针,此处P的类型为*person
-
- P := new(person)
-
-下面我们看一个完整的使用struct的例子
-
- package main
- import "fmt"
-
- // 声明一个新的类型
- type person struct {
- name string
- age int
- }
-
- // 比较两个人的年龄,返回年龄大的那个人,并且返回年龄差
- // struct也是传值的
- func Older(p1, p2 person) (person, int) {
- if p1.age>p2.age { // 比较p1和p2这两个人的年龄
- return p1, p1.age-p2.age
- }
- return p2, p2.age-p1.age
- }
-
- func main() {
- var tom person
-
- // 赋值初始化
- tom.name, tom.age = "Tom", 18
-
- // 两个字段都写清楚的初始化
- bob := person{age:25, name:"Bob"}
-
- // 按照struct定义顺序初始化值
- paul := person{"Paul", 43}
-
- tb_Older, tb_diff := Older(tom, bob)
- tp_Older, tp_diff := Older(tom, paul)
- bp_Older, bp_diff := Older(bob, paul)
-
- fmt.Printf("Of %s and %s, %s is older by %d years\n",
- tom.name, bob.name, tb_Older.name, tb_diff)
-
- fmt.Printf("Of %s and %s, %s is older by %d years\n",
- tom.name, paul.name, tp_Older.name, tp_diff)
-
- fmt.Printf("Of %s and %s, %s is older by %d years\n",
- bob.name, paul.name, bp_Older.name, bp_diff)
- }
-
-### struct的匿名字段
-我们上面介绍了如何定义一个struct,定义的时候是字段名与其类型一一对应,实际上Go支持只提供类型,而不写字段名的方式,也就是匿名字段,也称为嵌入字段。
-
-当匿名字段是一个struct的时候,那么这个struct所拥有的全部字段都被隐式地引入了当前定义的这个struct。
-
-让我们来看一个例子,让上面说的这些更具体化
-
- package main
- import "fmt"
-
- type Human struct {
- name string
- age int
- weight int
- }
-
- type Student struct {
- Human // 匿名字段,那么默认Student就包含了Human的所有字段
- speciality string
- }
-
- func main() {
- // 我们初始化一个学生
- mark := Student{Human{"Mark", 25, 120}, "Computer Science"}
-
- // 我们访问相应的字段
- fmt.Println("His name is ", mark.name)
- fmt.Println("His age is ", mark.age)
- fmt.Println("His weight is ", mark.weight)
- fmt.Println("His speciality is ", mark.speciality)
- // 修改对应的备注信息
- mark.speciality = "AI"
- fmt.Println("Mark changed his speciality")
- fmt.Println("His speciality is ", mark.speciality)
- // 修改他的年龄信息
- fmt.Println("Mark become old")
- mark.age = 46
- fmt.Println("His age is", mark.age)
- // 修改他的体重信息
- fmt.Println("Mark is not an athlet anymore")
- mark.weight += 60
- fmt.Println("His weight is", mark.weight)
- }
-
-图例如下:
-
-
-
-图2.7 struct组合,Student组合了Human struct和string基本类型
-
-我们看到Student访问属性age和name的时候,就像访问自己所有用的字段一样,对,匿名字段就是这样,能够实现字段的继承。是不是很酷啊?还有比这个更酷的呢,那就是student还能访问Human这个字段作为字段名。请看下面的代码,是不是更酷了。
-
- mark.Human = Human{"Marcus", 55, 220}
- mark.Human.age -= 1
-
-通过匿名访问和修改字段相当的有用,但是不仅仅是struct字段哦,所有的内置类型和自定义类型都是可以作为匿名字段的。请看下面的例子
-
- package main
- import "fmt"
-
- type Skills []string
-
- type Human struct {
- name string
- age int
- weight int
- }
-
- type Student struct {
- Human // 匿名字段,struct
- Skills // 匿名字段,自定义的类型string slice
- int // 内置类型作为匿名字段
- speciality string
- }
-
- func main() {
- // 初始化学生Jane
- jane := Student{Human:Human{"Jane", 35, 100}, speciality:"Biology"}
- // 现在我们来访问相应的字段
- fmt.Println("Her name is ", jane.name)
- fmt.Println("Her age is ", jane.age)
- fmt.Println("Her weight is ", jane.weight)
- fmt.Println("Her speciality is ", jane.speciality)
- // 我们来修改他的skill技能字段
- jane.Skills = []string{"anatomy"}
- fmt.Println("Her skills are ", jane.Skills)
- fmt.Println("She acquired two new ones ")
- jane.Skills = append(jane.Skills, "physics", "golang")
- fmt.Println("Her skills now are ", jane.Skills)
- // 修改匿名内置类型字段
- jane.int = 3
- fmt.Println("Her preferred number is", jane.int)
- }
-
-从上面例子我们看出来struct不仅仅能够将struct作为匿名字段、自定义类型、内置类型都可以作为匿名字段,而且可以在相应的字段上面进行函数操作(如例子中的append)。
-
-这里有一个问题:如果human里面有一个字段叫做phone,而student也有一个字段叫做phone,那么该怎么办呢?
-
-Go里面很简单的解决了这个问题,最外层的优先访问,也就是当你通过`student.phone`访问的时候,是访问student里面的字段,而不是human里面的字段。
-
-这样就允许我们去重载通过匿名字段继承的一些字段,当然如果我们想访问重载后对应匿名类型里面的字段,可以通过匿名字段名来访问。请看下面的例子
-
- package main
- import "fmt"
-
- type Human struct {
- name string
- age int
- phone string // Human类型拥有的字段
- }
-
- type Employee struct {
- Human // 匿名字段Human
- speciality string
- phone string // 雇员的phone字段
- }
-
- func main() {
- Bob := Employee{Human{"Bob", 34, "777-444-XXXX"}, "Designer", "333-222"}
- fmt.Println("Bob's work phone is:", Bob.phone)
- // 如果我们要访问Human的phone字段
- fmt.Println("Bob's personal phone is:", Bob.Human.phone)
- }
-
-
-## links
- * [目录]()
- * 上一章: [流程和函数](<02.3.md>)
- * 下一节: [面向对象](<02.5.md>)
+# 2.4 struct类型
+## struct
+Go语言中,也和C或者其他语言一样,我们可以声明新的类型,作为其它类型的属性或字段的容器。例如,我们可以创建一个自定义类型`person`代表一个人的实体。这个实体拥有属性:姓名和年龄。这样的类型我们称之`struct`。如下代码所示:
+
+ type person struct {
+ name string
+ age int
+ }
+看到了吗?声明一个struct如此简单,上面的类型包含有两个字段
+- 一个string类型的字段name,用来保存用户名称这个属性
+- 一个int类型的字段age,用来保存用户年龄这个属性
+
+如何使用struct呢?请看下面的代码
+
+ type person struct {
+ name string
+ age int
+ }
+
+ var P person // P现在就是person类型的变量了
+
+ P.name = "Astaxie" // 赋值"Astaxie"给P的name属性.
+ P.age = 25 // 赋值"25"给变量P的age属性
+ fmt.Printf("The person's name is %s", P.name) // 访问P的name属性.
+除了上面这种P的声明使用之外,还有另外几种声明使用方式:
+
+- 1.按照顺序提供初始化值
+
+ P := person{"Tom", 25}
+
+- 2.通过`field:value`的方式初始化,这样可以任意顺序
+
+ P := person{age:24, name:"Tom"}
+
+- 3.当然也可以通过`new`函数分配一个指针,此处P的类型为*person
+
+ P := new(person)
+
+下面我们看一个完整的使用struct的例子
+
+ package main
+ import "fmt"
+
+ // 声明一个新的类型
+ type person struct {
+ name string
+ age int
+ }
+
+ // 比较两个人的年龄,返回年龄大的那个人,并且返回年龄差
+ // struct也是传值的
+ func Older(p1, p2 person) (person, int) {
+ if p1.age>p2.age { // 比较p1和p2这两个人的年龄
+ return p1, p1.age-p2.age
+ }
+ return p2, p2.age-p1.age
+ }
+
+ func main() {
+ var tom person
+
+ // 赋值初始化
+ tom.name, tom.age = "Tom", 18
+
+ // 两个字段都写清楚的初始化
+ bob := person{age:25, name:"Bob"}
+
+ // 按照struct定义顺序初始化值
+ paul := person{"Paul", 43}
+
+ tb_Older, tb_diff := Older(tom, bob)
+ tp_Older, tp_diff := Older(tom, paul)
+ bp_Older, bp_diff := Older(bob, paul)
+
+ fmt.Printf("Of %s and %s, %s is older by %d years\n",
+ tom.name, bob.name, tb_Older.name, tb_diff)
+
+ fmt.Printf("Of %s and %s, %s is older by %d years\n",
+ tom.name, paul.name, tp_Older.name, tp_diff)
+
+ fmt.Printf("Of %s and %s, %s is older by %d years\n",
+ bob.name, paul.name, bp_Older.name, bp_diff)
+ }
+
+### struct的匿名字段
+我们上面介绍了如何定义一个struct,定义的时候是字段名与其类型一一对应,实际上Go支持只提供类型,而不写字段名的方式,也就是匿名字段,也称为嵌入字段。
+
+当匿名字段是一个struct的时候,那么这个struct所拥有的全部字段都被隐式地引入了当前定义的这个struct。
+
+让我们来看一个例子,让上面说的这些更具体化
+
+ package main
+ import "fmt"
+
+ type Human struct {
+ name string
+ age int
+ weight int
+ }
+
+ type Student struct {
+ Human // 匿名字段,那么默认Student就包含了Human的所有字段
+ speciality string
+ }
+
+ func main() {
+ // 我们初始化一个学生
+ mark := Student{Human{"Mark", 25, 120}, "Computer Science"}
+
+ // 我们访问相应的字段
+ fmt.Println("His name is ", mark.name)
+ fmt.Println("His age is ", mark.age)
+ fmt.Println("His weight is ", mark.weight)
+ fmt.Println("His speciality is ", mark.speciality)
+ // 修改对应的备注信息
+ mark.speciality = "AI"
+ fmt.Println("Mark changed his speciality")
+ fmt.Println("His speciality is ", mark.speciality)
+ // 修改他的年龄信息
+ fmt.Println("Mark become old")
+ mark.age = 46
+ fmt.Println("His age is", mark.age)
+ // 修改他的体重信息
+ fmt.Println("Mark is not an athlet anymore")
+ mark.weight += 60
+ fmt.Println("His weight is", mark.weight)
+ }
+
+图例如下:
+
+
+
+图2.7 struct组合,Student组合了Human struct和string基本类型
+
+我们看到Student访问属性age和name的时候,就像访问自己所有用的字段一样,对,匿名字段就是这样,能够实现字段的继承。是不是很酷啊?还有比这个更酷的呢,那就是student还能访问Human这个字段作为字段名。请看下面的代码,是不是更酷了。
+
+ mark.Human = Human{"Marcus", 55, 220}
+ mark.Human.age -= 1
+
+通过匿名访问和修改字段相当的有用,但是不仅仅是struct字段哦,所有的内置类型和自定义类型都是可以作为匿名字段的。请看下面的例子
+
+ package main
+ import "fmt"
+
+ type Skills []string
+
+ type Human struct {
+ name string
+ age int
+ weight int
+ }
+
+ type Student struct {
+ Human // 匿名字段,struct
+ Skills // 匿名字段,自定义的类型string slice
+ int // 内置类型作为匿名字段
+ speciality string
+ }
+
+ func main() {
+ // 初始化学生Jane
+ jane := Student{Human:Human{"Jane", 35, 100}, speciality:"Biology"}
+ // 现在我们来访问相应的字段
+ fmt.Println("Her name is ", jane.name)
+ fmt.Println("Her age is ", jane.age)
+ fmt.Println("Her weight is ", jane.weight)
+ fmt.Println("Her speciality is ", jane.speciality)
+ // 我们来修改他的skill技能字段
+ jane.Skills = []string{"anatomy"}
+ fmt.Println("Her skills are ", jane.Skills)
+ fmt.Println("She acquired two new ones ")
+ jane.Skills = append(jane.Skills, "physics", "golang")
+ fmt.Println("Her skills now are ", jane.Skills)
+ // 修改匿名内置类型字段
+ jane.int = 3
+ fmt.Println("Her preferred number is", jane.int)
+ }
+
+从上面例子我们看出来struct不仅仅能够将struct作为匿名字段、自定义类型、内置类型都可以作为匿名字段,而且可以在相应的字段上面进行函数操作(如例子中的append)。
+
+这里有一个问题:如果human里面有一个字段叫做phone,而student也有一个字段叫做phone,那么该怎么办呢?
+
+Go里面很简单的解决了这个问题,最外层的优先访问,也就是当你通过`student.phone`访问的时候,是访问student里面的字段,而不是human里面的字段。
+
+这样就允许我们去重载通过匿名字段继承的一些字段,当然如果我们想访问重载后对应匿名类型里面的字段,可以通过匿名字段名来访问。请看下面的例子
+
+ package main
+ import "fmt"
+
+ type Human struct {
+ name string
+ age int
+ phone string // Human类型拥有的字段
+ }
+
+ type Employee struct {
+ Human // 匿名字段Human
+ speciality string
+ phone string // 雇员的phone字段
+ }
+
+ func main() {
+ Bob := Employee{Human{"Bob", 34, "777-444-XXXX"}, "Designer", "333-222"}
+ fmt.Println("Bob's work phone is:", Bob.phone)
+ // 如果我们要访问Human的phone字段
+ fmt.Println("Bob's personal phone is:", Bob.Human.phone)
+ }
+
+
+## links
+ * [目录]()
+ * 上一章: [流程和函数](<02.3.md>)
+ * 下一节: [面向对象](<02.5.md>)
diff --git a/zh/02.4.md~eead24cf064976b648de5826eab51880c803b0fa b/zh/02.4.md~eead24cf064976b648de5826eab51880c803b0fa
new file mode 100644
index 00000000..5cdbad44
--- /dev/null
+++ b/zh/02.4.md~eead24cf064976b648de5826eab51880c803b0fa
@@ -0,0 +1,213 @@
+# 2.4 struct类型
+## struct
+Go语言中,也和C或者其他语言一样,我们可以声明新的类型,作为其它类型的属性或字段的容器。例如,我们可以创建一个自定义类型`person`代表一个人的实体。这个实体拥有属性:姓名和年龄。这样的类型我们称之`struct`。如下代码所示:
+
+ type person struct {
+ name string
+ age int
+ }
+看到了吗?声明一个struct如此简单,上面的类型包含有两个字段
+- 一个string类型的字段name,用来保存用户名称这个属性
+- 一个int类型的字段age,用来保存用户年龄这个属性
+
+如何使用struct呢?请看下面的代码
+
+ type person struct {
+ name string
+ age int
+ }
+
+ var P person // P现在就是person类型的变量了
+
+ P.name = "Astaxie" // 赋值"Astaxie"给P的name属性.
+ P.age = 25 // 赋值"25"给变量P的age属性
+ fmt.Printf("The person's name is %s", P.name) // 访问P的name属性.
+除了上面这种P的声明使用之外,还有另外几种声明使用方式:
+
+- 1.按照顺序提供初始化值
+
+ P := person{"Tom", 25}
+
+- 2.通过`field:value`的方式初始化,这样可以任意顺序
+
+ P := person{age:24, name:"Tom"}
+
+- 3.当然也可以通过`new`函数分配一个指针,此处P的类型为*person
+
+ P := new(person)
+
+下面我们看一个完整的使用struct的例子
+
+ package main
+ import "fmt"
+
+ // 声明一个新的类型
+ type person struct {
+ name string
+ age int
+ }
+
+ // 比较两个人的年龄,返回年龄大的那个人,并且返回年龄差
+ // struct也是传值的
+ func Older(p1, p2 person) (person, int) {
+ if p1.age>p2.age { // 比较p1和p2这两个人的年龄
+ return p1, p1.age-p2.age
+ }
+ return p2, p2.age-p1.age
+ }
+
+ func main() {
+ var tom person
+
+ // 赋值初始化
+ tom.name, tom.age = "Tom", 18
+
+ // 两个字段都写清楚的初始化
+ bob := person{age:25, name:"Bob"}
+
+ // 按照struct定义顺序初始化值
+ paul := person{"Paul", 43}
+
+ tb_Older, tb_diff := Older(tom, bob)
+ tp_Older, tp_diff := Older(tom, paul)
+ bp_Older, bp_diff := Older(bob, paul)
+
+ fmt.Printf("Of %s and %s, %s is older by %d years\n",
+ tom.name, bob.name, tb_Older.name, tb_diff)
+
+ fmt.Printf("Of %s and %s, %s is older by %d years\n",
+ tom.name, paul.name, tp_Older.name, tp_diff)
+
+ fmt.Printf("Of %s and %s, %s is older by %d years\n",
+ bob.name, paul.name, bp_Older.name, bp_diff)
+ }
+
+### struct的匿名字段
+我们上面介绍了如何定义一个struct,定义的时候是字段名与其类型一一对应,实际上Go支持只提供类型,而不写字段名的方式,也就是匿名字段,也称为嵌入字段。
+
+当匿名字段是一个struct的时候,那么这个struct所拥有的全部字段都被隐式地引入了当前定义的这个struct。
+
+让我们来看一个例子,让上面说的这些更具体化
+
+ package main
+ import "fmt"
+
+ type Human struct {
+ name string
+ age int
+ weight int
+ }
+
+ type Student struct {
+ Human // 匿名字段,那么默认Student就包含了Human的所有字段
+ speciality string
+ }
+
+ func main() {
+ // 我们初始化一个学生
+ mark := Student{Human{"Mark", 25, 120}, "Computer Science"}
+
+ // 我们访问相应的字段
+ fmt.Println("His name is ", mark.name)
+ fmt.Println("His age is ", mark.age)
+ fmt.Println("His weight is ", mark.weight)
+ fmt.Println("His speciality is ", mark.speciality)
+ // 修改对应的备注信息
+ mark.speciality = "AI"
+ fmt.Println("Mark changed his speciality")
+ fmt.Println("His speciality is ", mark.speciality)
+ // 修改他的年龄信息
+ fmt.Println("Mark become old")
+ mark.age = 46
+ fmt.Println("His age is", mark.age)
+ // 修改他的体重信息
+ fmt.Println("Mark is not an athlet anymore")
+ mark.weight += 60
+ fmt.Println("His weight is", mark.weight)
+ }
+
+图例如下:
+
+
+
+图2.7 struct组合,Student组合了Human struct和string基本类型
+
+我们看到Student访问属性age和name的时候,就像访问自己所有用的字段一样,对,匿名字段就是这样,能够实现字段的继承。是不是很酷啊?还有比这个更酷的呢,那就是student还能访问Human这个字段作为字段名。请看下面的代码,是不是更酷了。
+
+ mark.Human = Human{"Marcus", 55, 220}
+ mark.Human.age -= 1
+
+通过匿名访问和修改字段相当的有用,但是不仅仅是struct字段哦,所有的内置类型和自定义类型都是可以作为匿名字段的。请看下面的例子
+
+ package main
+ import "fmt"
+
+ type Skills []string
+
+ type Human struct {
+ name string
+ age int
+ weight int
+ }
+
+ type Student struct {
+ Human // 匿名字段,struct
+ Skills // 匿名字段,自定义的类型string slice
+ int // 内置类型作为匿名字段
+ speciality string
+ }
+
+ func main() {
+ // 初始化学生Jane
+ jane := Student{Human:Human{"Jane", 35, 100}, speciality:"Biology"}
+ // 现在我们来访问相应的字段
+ fmt.Println("Her name is ", jane.name)
+ fmt.Println("Her age is ", jane.age)
+ fmt.Println("Her weight is ", jane.weight)
+ fmt.Println("Her speciality is ", jane.speciality)
+ // 我们来修改他的skill技能字段
+ jane.Skills = []string{"anatomy"}
+ fmt.Println("Her skills are ", jane.Skills)
+ fmt.Println("She acquired two new ones ")
+ jane.Skills = append(jane.Skills, "physics", "golang")
+ fmt.Println("Her skills now are ", jane.Skills)
+ // 修改匿名内置类型字段
+ jane.int = 3
+ fmt.Println("Her preferred number is", jane.int)
+ }
+
+从上面例子我们看出来struct不仅仅能够将struct作为匿名字段、自定义类型、内置类型都可以作为匿名字段,而且可以在相应的字段上面进行函数操作(如例子中的append)。
+
+这里有一个问题:如果human里面有一个字段叫做phone,而student也有一个字段叫做phone,那么该怎么办呢?
+
+Go里面很简单的解决了这个问题,最外层的优先访问,也就是当你通过`student.phone`访问的时候,是访问student里面的字段,而不是human里面的字段。
+
+这样就允许我们去重载通过匿名字段继承的一些字段,当然如果我们想访问重载后对应匿名类型里面的字段,可以通过匿名字段名来访问。请看下面的例子
+
+ package main
+ import "fmt"
+
+ type Human struct {
+ name string
+ age int
+ phone string // Human类型拥有的字段
+ }
+
+ type Employee struct {
+ Human // 匿名字段Human
+ speciality string
+ phone string // 雇员的phone字段
+ }
+
+ func main() {
+ Bob := Employee{Human{"Bob", 34, "777-444-XXXX"}, "Designer", "333-222"}
+ fmt.Println("Bob's work phone is:", Bob.phone)
+ // 如果我们要访问Human的phone字段
+ fmt.Println("Bob's personal phone is:", Bob.Human.phone)
+ }
+
+
+## links
+ * [目录]()
+ * 上一章: [流程和函数](<02.3.md>)
+ * 下一节: [面向对象](<02.5.md>)
diff --git a/zh/02.5.md b/zh/02.5.md~HEAD
similarity index 97%
rename from zh/02.5.md
rename to zh/02.5.md~HEAD
index 71c39fac..63d22239 100644
--- a/zh/02.5.md
+++ b/zh/02.5.md~HEAD
@@ -1,325 +1,325 @@
-# 2.5 面向对象
-前面两章我们介绍了函数和struct,那你是否想过函数当作struct的字段一样来处理呢?今天我们就讲解一下函数的另一种形态,带有接收者的函数,我们称为`method`
-
-## method
-现在假设有这么一个场景,你定义了一个struct叫做长方形,你现在想要计算他的面积,那么按照我们一般的思路应该会用下面的方式来实现
-
- package main
- import "fmt"
-
- type Rectangle struct {
- width, height float64
- }
-
- func area(r Rectangle) float64 {
- return r.width*r.height
- }
-
- func main() {
- r1 := Rectangle{12, 2}
- r2 := Rectangle{9, 4}
- fmt.Println("Area of r1 is: ", area(r1))
- fmt.Println("Area of r2 is: ", area(r2))
- }
-
-这段代码可以计算出来长方形的面积,但是area()不是作为Rectangle的方法实现的(类似面向对象里面的方法),而是将Rectangle的对象(如r1,r2)作为参数传入函数计算面积的。
-
-这样实现当然没有问题咯,但是当需要增加圆形、正方形、五边形甚至其它多边形的时候,你想计算他们的面积的时候怎么办啊?那就只能增加新的函数咯,但是函数名你就必须要跟着换了,变成`area_rectangle, area_circle, area_triangle...`
-
-像下图所表示的那样, 椭圆代表函数, 而这些函数并不从属于struct(或者以面向对象的术语来说,并不属于class),他们是单独存在于struct外围,而非在概念上属于某个struct的。
-
-
-
-图2.8 方法和struct的关系图
-
-很显然,这样的实现并不优雅,并且从概念上来说"面积"是"形状"的一个属性,它是属于这个特定的形状的,就像长方形的长和宽一样。
-
-基于上面的原因所以就有了`method`的概念,`method`是附属在一个给定的类型上的,他的语法和函数的声明语法几乎一样,只是在`func`后面增加了一个receiver(也就是method所依从的主体)。
-
-用上面提到的形状的例子来说,method `area()` 是依赖于某个形状(比如说Rectangle)来发生作用的。Rectangle.area()的发出者是Rectangle, area()是属于Rectangle的方法,而非一个外围函数。
-
-更具体地说,Rectangle存在字段length 和 width, 同时存在方法area(), 这些字段和方法都属于Rectangle。
-
-用Rob Pike的话来说就是:
-
->"A method is a function with an implicit first argument, called a receiver."
-
-method的语法如下:
-
- func (r ReceiverType) funcName(parameters) (results)
-
-下面我们用最开始的例子用method来实现:
-
- package main
- import (
- "fmt"
- "math"
- )
-
- type Rectangle struct {
- width, height float64
- }
-
- type Circle struct {
- radius float64
- }
-
- func (r Rectangle) area() float64 {
- return r.width*r.height
- }
-
- func (c Circle) area() float64 {
- return c.radius * c.radius * math.Pi
- }
-
-
- func main() {
- r1 := Rectangle{12, 2}
- r2 := Rectangle{9, 4}
- c1 := Circle{10}
- c2 := Circle{25}
-
- fmt.Println("Area of r1 is: ", r1.area())
- fmt.Println("Area of r2 is: ", r2.area())
- fmt.Println("Area of c1 is: ", c1.area())
- fmt.Println("Area of c2 is: ", c2.area())
- }
-
-
-
-在使用method的时候重要注意几点
-
-- 虽然method的名字一模一样,但是如果接收者不一样,那么method就不一样
-- method里面可以访问接收者的字段
-- 调用method通过`.`访问,就像struct里面访问字段一样
-
-图示如下:
-
-
-
-图2.9 不同struct的method不同
-
-在上例,method area() 分别属于Rectangle和Circle, 于是他们的 Receiver 就变成了Rectangle 和 Circle, 或者说,这个area()方法 是由 Rectangle/Circle 发出的。
-
->值得说明的一点是,图示中method用虚线标出,意思是此处方法的Receiver是以值传递,而非引用传递,是的,Receiver还可以是指针, 两者的差别在于, 指针作为Receiver会对实例对象的内容发生操作,而普通类型作为Receiver仅仅是以副本作为操作对象,并不对原实例对象发生操作。后文对此会有详细论述。
-
-那是不是method只能作用在struct上面呢?当然不是咯,他可以定义在任何你自定义的类型、内置类型、struct等各种类型上面。这里你是不是有点迷糊了,什么叫自定义类型,自定义类型不就是struct嘛,不是这样的哦,struct只是自定义类型里面一种比较特殊的类型而已,还有其他自定义类型申明,可以通过如下这样的申明来实现。
-
- type typeName typeLiteral
-
-请看下面这个申明自定义类型的代码
-
- type ages int
-
- type money float32
-
- type months map[string]int
-
- m := months {
- "January":31,
- "February":28,
- ...
- "December":31,
- }
-
-看到了吗?简单的很吧,这样你就可以在自己的代码里面定义有意义的类型了,实际上只是一个定义了一个别名,有点类似于c中的typedef,例如上面ages替代了int
-
-好了,让我们回到`method`
-
-你可以在任何的自定义类型中定义任意多的`method`,接下来让我们看一个复杂一点的例子
-
- package main
- import "fmt"
-
- const(
- WHITE = iota
- BLACK
- BLUE
- RED
- YELLOW
- )
-
- type Color byte
-
- type Box struct {
- width, height, depth float64
- color Color
- }
-
- type BoxList []Box //a slice of boxes
-
- func (b Box) Volume() float64 {
- return b.width * b.height * b.depth
- }
-
- func (b *Box) SetColor(c Color) {
- b.color = c
- }
-
- func (bl BoxList) BiggestColor() Color {
- v := 0.00
- k := Color(WHITE)
- for _, b := range bl {
- if bv := b.Volume(); bv > v {
- v = bv
- k = b.color
- }
- }
- return k
- }
-
- func (bl BoxList) PaintItBlack() {
- for i, _ := range bl {
- bl[i].SetColor(BLACK)
- }
- }
-
- func (c Color) String() string {
- strings := []string {"WHITE", "BLACK", "BLUE", "RED", "YELLOW"}
- return strings[c]
- }
-
- func main() {
- boxes := BoxList {
- Box{4, 4, 4, RED},
- Box{10, 10, 1, YELLOW},
- Box{1, 1, 20, BLACK},
- Box{10, 10, 1, BLUE},
- Box{10, 30, 1, WHITE},
- Box{20, 20, 20, YELLOW},
- }
-
- fmt.Printf("We have %d boxes in our set\n", len(boxes))
- fmt.Println("The volume of the first one is", boxes[0].Volume(), "cm³")
- fmt.Println("The color of the last one is",boxes[len(boxes)-1].color.String())
- fmt.Println("The biggest one is", boxes.BiggestColor().String())
-
- fmt.Println("Let's paint them all black")
- boxes.PaintItBlack()
- fmt.Println("The color of the second one is", boxes[1].color.String())
-
- fmt.Println("Obviously, now, the biggest one is", boxes.BiggestColor().String())
- }
-
-上面的代码通过const定义了一些常量,然后定义了一些自定义类型
-
-- Color作为byte的别名
-- 定义了一个struct:Box,含有三个长宽高字段和一个颜色属性
-- 定义了一个slice:BoxList,含有Box
-
-然后以上面的自定义类型为接收者定义了一些method
-
-- Volume()定义了接收者为Box,返回Box的容量
-- SetColor(c Color),把Box的颜色改为c
-- BiggestColor()定在在BoxList上面,返回list里面容量最大的颜色
-- PaintItBlack()把BoxList里面所有Box的颜色全部变成黑色
-- String()定义在Color上面,返回Color的具体颜色(字符串格式)
-
-上面的代码通过文字描述出来之后是不是很简单?我们一般解决问题都是通过问题的描述,去写相应的代码实现。
-
-### 指针作为receiver
-现在让我们回过头来看看SetColor这个method,它的receiver是一个指向Box的指针,是的,你可以使用*Box。想想为啥要使用指针而不是Box本身呢?
-
-我们定义SetColor的真正目的是想改变这个Box的颜色,如果不传Box的指针,那么SetColor接受的其实是Box的一个copy,也就是说method内对于颜色值的修改,其实只作用于Box的copy,而不是真正的Box。所以我们需要传入指针。
-
-这里可以把receiver当作method的第一个参数来看,然后结合前面函数讲解的传值和传引用就不难理解
-
-这里你也许会问了那SetColor函数里面应该这样定义`*b.Color=c`,而不是`b.Color=c`,因为我们需要读取到指针相应的值。
-
-你是对的,其实Go里面这两种方式都是正确的,当你用指针去访问相应的字段时(虽然指针没有任何的字段),Go知道你要通过指针去获取这个值,看到了吧,Go的设计是不是越来越吸引你了。
-
-也许细心的读者会问这样的问题,PaintItBlack里面调用SetColor的时候是不是应该写成`(&bl[i]).SetColor(BLACK)`,因为SetColor的receiver是*Box,而不是Box。
-
-你又说对的,这两种方式都可以,因为Go知道receiver是指针,他自动帮你转了。
-
-也就是说:
->如果一个method的receiver是*T,你可以在一个T类型的实例变量V上面调用这个method,而不需要&V去调用这个method
-
-类似的
->如果一个method的receiver是T,你可以在一个*T类型的变量P上面调用这个method,而不需要 *P去调用这个method
-
-所以,你不用担心你是调用的指针的method还是不是指针的method,Go知道你要做的一切,这对于有多年C/C++编程经验的同学来说,真是解决了一个很大的痛苦。
-
-### method继承
-前面一章我们学习了字段的继承,那么你也会发现Go的一个神奇之处,method也是可以继承的。如果匿名字段实现了一个method,那么包含这个匿名字段的struct也能调用该method。让我们来看下面这个例子
-
- package main
- import "fmt"
-
- type Human struct {
- name string
- age int
- phone string
- }
-
- type Student struct {
- Human //匿名字段
- school string
- }
-
- type Employee struct {
- Human //匿名字段
- company string
- }
-
- //在human上面定义了一个method
- func (h *Human) SayHi() {
- fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
- }
-
- func main() {
- mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
- sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
-
- mark.SayHi()
- sam.SayHi()
- }
-
-### method重写
-上面的例子中,如果Employee想要实现自己的SayHi,怎么办?简单,和匿名字段冲突一样的道理,我们可以在Employee上面定义一个method,重写了匿名字段的方法。请看下面的例子
-
- package main
- import "fmt"
-
- type Human struct {
- name string
- age int
- phone string
- }
-
- type Student struct {
- Human //匿名字段
- school string
- }
-
- type Employee struct {
- Human //匿名字段
- company string
- }
-
- //Human定义method
- func (h *Human) SayHi() {
- fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
- }
-
- //Employee的method重写Human的method
- func (e *Employee) SayHi() {
- fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
- e.company, e.phone) //Yes you can split into 2 lines here.
- }
-
- func main() {
- mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
- sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
-
- mark.SayHi()
- sam.SayHi()
- }
-
-上面的代码设计的是如此的美妙,让人不自觉的为Go的设计惊叹!
-
-通过这些内容,我们可以设计出基本的面向对象的程序了,但是Go里面的面向对象是如此的简单,没有任何的私有、公有关键字,通过大小写来实现(大写开头的为公有,小写开头的为私有),方法也同样适用这个原则。
-## links
- * [目录]()
- * 上一章: [struct类型](<02.4.md>)
- * 下一节: [interface](<02.6.md>)
+# 2.5 面向对象
+前面两章我们介绍了函数和struct,那你是否想过函数当作struct的字段一样来处理呢?今天我们就讲解一下函数的另一种形态,带有接收者的函数,我们称为`method`
+
+## method
+现在假设有这么一个场景,你定义了一个struct叫做长方形,你现在想要计算他的面积,那么按照我们一般的思路应该会用下面的方式来实现
+
+ package main
+ import "fmt"
+
+ type Rectangle struct {
+ width, height float64
+ }
+
+ func area(r Rectangle) float64 {
+ return r.width*r.height
+ }
+
+ func main() {
+ r1 := Rectangle{12, 2}
+ r2 := Rectangle{9, 4}
+ fmt.Println("Area of r1 is: ", area(r1))
+ fmt.Println("Area of r2 is: ", area(r2))
+ }
+
+这段代码可以计算出来长方形的面积,但是area()不是作为Rectangle的方法实现的(类似面向对象里面的方法),而是将Rectangle的对象(如r1,r2)作为参数传入函数计算面积的。
+
+这样实现当然没有问题咯,但是当需要增加圆形、正方形、五边形甚至其它多边形的时候,你想计算他们的面积的时候怎么办啊?那就只能增加新的函数咯,但是函数名你就必须要跟着换了,变成`area_rectangle, area_circle, area_triangle...`
+
+像下图所表示的那样, 椭圆代表函数, 而这些函数并不从属于struct(或者以面向对象的术语来说,并不属于class),他们是单独存在于struct外围,而非在概念上属于某个struct的。
+
+
+
+图2.8 方法和struct的关系图
+
+很显然,这样的实现并不优雅,并且从概念上来说"面积"是"形状"的一个属性,它是属于这个特定的形状的,就像长方形的长和宽一样。
+
+基于上面的原因所以就有了`method`的概念,`method`是附属在一个给定的类型上的,他的语法和函数的声明语法几乎一样,只是在`func`后面增加了一个receiver(也就是method所依从的主体)。
+
+用上面提到的形状的例子来说,method `area()` 是依赖于某个形状(比如说Rectangle)来发生作用的。Rectangle.area()的发出者是Rectangle, area()是属于Rectangle的方法,而非一个外围函数。
+
+更具体地说,Rectangle存在字段length 和 width, 同时存在方法area(), 这些字段和方法都属于Rectangle。
+
+用Rob Pike的话来说就是:
+
+>"A method is a function with an implicit first argument, called a receiver."
+
+method的语法如下:
+
+ func (r ReceiverType) funcName(parameters) (results)
+
+下面我们用最开始的例子用method来实现:
+
+ package main
+ import (
+ "fmt"
+ "math"
+ )
+
+ type Rectangle struct {
+ width, height float64
+ }
+
+ type Circle struct {
+ radius float64
+ }
+
+ func (r Rectangle) area() float64 {
+ return r.width*r.height
+ }
+
+ func (c Circle) area() float64 {
+ return c.radius * c.radius * math.Pi
+ }
+
+
+ func main() {
+ r1 := Rectangle{12, 2}
+ r2 := Rectangle{9, 4}
+ c1 := Circle{10}
+ c2 := Circle{25}
+
+ fmt.Println("Area of r1 is: ", r1.area())
+ fmt.Println("Area of r2 is: ", r2.area())
+ fmt.Println("Area of c1 is: ", c1.area())
+ fmt.Println("Area of c2 is: ", c2.area())
+ }
+
+
+
+在使用method的时候重要注意几点
+
+- 虽然method的名字一模一样,但是如果接收者不一样,那么method就不一样
+- method里面可以访问接收者的字段
+- 调用method通过`.`访问,就像struct里面访问字段一样
+
+图示如下:
+
+
+
+图2.9 不同struct的method不同
+
+在上例,method area() 分别属于Rectangle和Circle, 于是他们的 Receiver 就变成了Rectangle 和 Circle, 或者说,这个area()方法 是由 Rectangle/Circle 发出的。
+
+>值得说明的一点是,图示中method用虚线标出,意思是此处方法的Receiver是以值传递,而非引用传递,是的,Receiver还可以是指针, 两者的差别在于, 指针作为Receiver会对实例对象的内容发生操作,而普通类型作为Receiver仅仅是以副本作为操作对象,并不对原实例对象发生操作。后文对此会有详细论述。
+
+那是不是method只能作用在struct上面呢?当然不是咯,他可以定义在任何你自定义的类型、内置类型、struct等各种类型上面。这里你是不是有点迷糊了,什么叫自定义类型,自定义类型不就是struct嘛,不是这样的哦,struct只是自定义类型里面一种比较特殊的类型而已,还有其他自定义类型申明,可以通过如下这样的申明来实现。
+
+ type typeName typeLiteral
+
+请看下面这个申明自定义类型的代码
+
+ type ages int
+
+ type money float32
+
+ type months map[string]int
+
+ m := months {
+ "January":31,
+ "February":28,
+ ...
+ "December":31,
+ }
+
+看到了吗?简单的很吧,这样你就可以在自己的代码里面定义有意义的类型了,实际上只是一个定义了一个别名,有点类似于c中的typedef,例如上面ages替代了int
+
+好了,让我们回到`method`
+
+你可以在任何的自定义类型中定义任意多的`method`,接下来让我们看一个复杂一点的例子
+
+ package main
+ import "fmt"
+
+ const(
+ WHITE = iota
+ BLACK
+ BLUE
+ RED
+ YELLOW
+ )
+
+ type Color byte
+
+ type Box struct {
+ width, height, depth float64
+ color Color
+ }
+
+ type BoxList []Box //a slice of boxes
+
+ func (b Box) Volume() float64 {
+ return b.width * b.height * b.depth
+ }
+
+ func (b *Box) SetColor(c Color) {
+ b.color = c
+ }
+
+ func (bl BoxList) BiggestColor() Color {
+ v := 0.00
+ k := Color(WHITE)
+ for _, b := range bl {
+ if bv := b.Volume(); bv > v {
+ v = bv
+ k = b.color
+ }
+ }
+ return k
+ }
+
+ func (bl BoxList) PaintItBlack() {
+ for i, _ := range bl {
+ bl[i].SetColor(BLACK)
+ }
+ }
+
+ func (c Color) String() string {
+ strings := []string {"WHITE", "BLACK", "BLUE", "RED", "YELLOW"}
+ return strings[c]
+ }
+
+ func main() {
+ boxes := BoxList {
+ Box{4, 4, 4, RED},
+ Box{10, 10, 1, YELLOW},
+ Box{1, 1, 20, BLACK},
+ Box{10, 10, 1, BLUE},
+ Box{10, 30, 1, WHITE},
+ Box{20, 20, 20, YELLOW},
+ }
+
+ fmt.Printf("We have %d boxes in our set\n", len(boxes))
+ fmt.Println("The volume of the first one is", boxes[0].Volume(), "cm³")
+ fmt.Println("The color of the last one is",boxes[len(boxes)-1].color.String())
+ fmt.Println("The biggest one is", boxes.BiggestColor().String())
+
+ fmt.Println("Let's paint them all black")
+ boxes.PaintItBlack()
+ fmt.Println("The color of the second one is", boxes[1].color.String())
+
+ fmt.Println("Obviously, now, the biggest one is", boxes.BiggestColor().String())
+ }
+
+上面的代码通过const定义了一些常量,然后定义了一些自定义类型
+
+- Color作为byte的别名
+- 定义了一个struct:Box,含有三个长宽高字段和一个颜色属性
+- 定义了一个slice:BoxList,含有Box
+
+然后以上面的自定义类型为接收者定义了一些method
+
+- Volume()定义了接收者为Box,返回Box的容量
+- SetColor(c Color),把Box的颜色改为c
+- BiggestColor()定在在BoxList上面,返回list里面容量最大的颜色
+- PaintItBlack()把BoxList里面所有Box的颜色全部变成黑色
+- String()定义在Color上面,返回Color的具体颜色(字符串格式)
+
+上面的代码通过文字描述出来之后是不是很简单?我们一般解决问题都是通过问题的描述,去写相应的代码实现。
+
+### 指针作为receiver
+现在让我们回过头来看看SetColor这个method,它的receiver是一个指向Box的指针,是的,你可以使用*Box。想想为啥要使用指针而不是Box本身呢?
+
+我们定义SetColor的真正目的是想改变这个Box的颜色,如果不传Box的指针,那么SetColor接受的其实是Box的一个copy,也就是说method内对于颜色值的修改,其实只作用于Box的copy,而不是真正的Box。所以我们需要传入指针。
+
+这里可以把receiver当作method的第一个参数来看,然后结合前面函数讲解的传值和传引用就不难理解
+
+这里你也许会问了那SetColor函数里面应该这样定义`*b.Color=c`,而不是`b.Color=c`,因为我们需要读取到指针相应的值。
+
+你是对的,其实Go里面这两种方式都是正确的,当你用指针去访问相应的字段时(虽然指针没有任何的字段),Go知道你要通过指针去获取这个值,看到了吧,Go的设计是不是越来越吸引你了。
+
+也许细心的读者会问这样的问题,PaintItBlack里面调用SetColor的时候是不是应该写成`(&bl[i]).SetColor(BLACK)`,因为SetColor的receiver是*Box,而不是Box。
+
+你又说对的,这两种方式都可以,因为Go知道receiver是指针,他自动帮你转了。
+
+也就是说:
+>如果一个method的receiver是*T,你可以在一个T类型的实例变量V上面调用这个method,而不需要&V去调用这个method
+
+类似的
+>如果一个method的receiver是T,你可以在一个*T类型的变量P上面调用这个method,而不需要 *P去调用这个method
+
+所以,你不用担心你是调用的指针的method还是不是指针的method,Go知道你要做的一切,这对于有多年C/C++编程经验的同学来说,真是解决了一个很大的痛苦。
+
+### method继承
+前面一章我们学习了字段的继承,那么你也会发现Go的一个神奇之处,method也是可以继承的。如果匿名字段实现了一个method,那么包含这个匿名字段的struct也能调用该method。让我们来看下面这个例子
+
+ package main
+ import "fmt"
+
+ type Human struct {
+ name string
+ age int
+ phone string
+ }
+
+ type Student struct {
+ Human //匿名字段
+ school string
+ }
+
+ type Employee struct {
+ Human //匿名字段
+ company string
+ }
+
+ //在human上面定义了一个method
+ func (h *Human) SayHi() {
+ fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
+ }
+
+ func main() {
+ mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
+ sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
+
+ mark.SayHi()
+ sam.SayHi()
+ }
+
+### method重写
+上面的例子中,如果Employee想要实现自己的SayHi,怎么办?简单,和匿名字段冲突一样的道理,我们可以在Employee上面定义一个method,重写了匿名字段的方法。请看下面的例子
+
+ package main
+ import "fmt"
+
+ type Human struct {
+ name string
+ age int
+ phone string
+ }
+
+ type Student struct {
+ Human //匿名字段
+ school string
+ }
+
+ type Employee struct {
+ Human //匿名字段
+ company string
+ }
+
+ //Human定义method
+ func (h *Human) SayHi() {
+ fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
+ }
+
+ //Employee的method重写Human的method
+ func (e *Employee) SayHi() {
+ fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
+ e.company, e.phone) //Yes you can split into 2 lines here.
+ }
+
+ func main() {
+ mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
+ sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
+
+ mark.SayHi()
+ sam.SayHi()
+ }
+
+上面的代码设计的是如此的美妙,让人不自觉的为Go的设计惊叹!
+
+通过这些内容,我们可以设计出基本的面向对象的程序了,但是Go里面的面向对象是如此的简单,没有任何的私有、公有关键字,通过大小写来实现(大写开头的为公有,小写开头的为私有),方法也同样适用这个原则。
+## links
+ * [目录]()
+ * 上一章: [struct类型](<02.4.md>)
+ * 下一节: [interface](<02.6.md>)
diff --git a/zh/02.5.md~eead24cf064976b648de5826eab51880c803b0fa b/zh/02.5.md~eead24cf064976b648de5826eab51880c803b0fa
new file mode 100644
index 00000000..63d22239
--- /dev/null
+++ b/zh/02.5.md~eead24cf064976b648de5826eab51880c803b0fa
@@ -0,0 +1,325 @@
+# 2.5 面向对象
+前面两章我们介绍了函数和struct,那你是否想过函数当作struct的字段一样来处理呢?今天我们就讲解一下函数的另一种形态,带有接收者的函数,我们称为`method`
+
+## method
+现在假设有这么一个场景,你定义了一个struct叫做长方形,你现在想要计算他的面积,那么按照我们一般的思路应该会用下面的方式来实现
+
+ package main
+ import "fmt"
+
+ type Rectangle struct {
+ width, height float64
+ }
+
+ func area(r Rectangle) float64 {
+ return r.width*r.height
+ }
+
+ func main() {
+ r1 := Rectangle{12, 2}
+ r2 := Rectangle{9, 4}
+ fmt.Println("Area of r1 is: ", area(r1))
+ fmt.Println("Area of r2 is: ", area(r2))
+ }
+
+这段代码可以计算出来长方形的面积,但是area()不是作为Rectangle的方法实现的(类似面向对象里面的方法),而是将Rectangle的对象(如r1,r2)作为参数传入函数计算面积的。
+
+这样实现当然没有问题咯,但是当需要增加圆形、正方形、五边形甚至其它多边形的时候,你想计算他们的面积的时候怎么办啊?那就只能增加新的函数咯,但是函数名你就必须要跟着换了,变成`area_rectangle, area_circle, area_triangle...`
+
+像下图所表示的那样, 椭圆代表函数, 而这些函数并不从属于struct(或者以面向对象的术语来说,并不属于class),他们是单独存在于struct外围,而非在概念上属于某个struct的。
+
+
+
+图2.8 方法和struct的关系图
+
+很显然,这样的实现并不优雅,并且从概念上来说"面积"是"形状"的一个属性,它是属于这个特定的形状的,就像长方形的长和宽一样。
+
+基于上面的原因所以就有了`method`的概念,`method`是附属在一个给定的类型上的,他的语法和函数的声明语法几乎一样,只是在`func`后面增加了一个receiver(也就是method所依从的主体)。
+
+用上面提到的形状的例子来说,method `area()` 是依赖于某个形状(比如说Rectangle)来发生作用的。Rectangle.area()的发出者是Rectangle, area()是属于Rectangle的方法,而非一个外围函数。
+
+更具体地说,Rectangle存在字段length 和 width, 同时存在方法area(), 这些字段和方法都属于Rectangle。
+
+用Rob Pike的话来说就是:
+
+>"A method is a function with an implicit first argument, called a receiver."
+
+method的语法如下:
+
+ func (r ReceiverType) funcName(parameters) (results)
+
+下面我们用最开始的例子用method来实现:
+
+ package main
+ import (
+ "fmt"
+ "math"
+ )
+
+ type Rectangle struct {
+ width, height float64
+ }
+
+ type Circle struct {
+ radius float64
+ }
+
+ func (r Rectangle) area() float64 {
+ return r.width*r.height
+ }
+
+ func (c Circle) area() float64 {
+ return c.radius * c.radius * math.Pi
+ }
+
+
+ func main() {
+ r1 := Rectangle{12, 2}
+ r2 := Rectangle{9, 4}
+ c1 := Circle{10}
+ c2 := Circle{25}
+
+ fmt.Println("Area of r1 is: ", r1.area())
+ fmt.Println("Area of r2 is: ", r2.area())
+ fmt.Println("Area of c1 is: ", c1.area())
+ fmt.Println("Area of c2 is: ", c2.area())
+ }
+
+
+
+在使用method的时候重要注意几点
+
+- 虽然method的名字一模一样,但是如果接收者不一样,那么method就不一样
+- method里面可以访问接收者的字段
+- 调用method通过`.`访问,就像struct里面访问字段一样
+
+图示如下:
+
+
+
+图2.9 不同struct的method不同
+
+在上例,method area() 分别属于Rectangle和Circle, 于是他们的 Receiver 就变成了Rectangle 和 Circle, 或者说,这个area()方法 是由 Rectangle/Circle 发出的。
+
+>值得说明的一点是,图示中method用虚线标出,意思是此处方法的Receiver是以值传递,而非引用传递,是的,Receiver还可以是指针, 两者的差别在于, 指针作为Receiver会对实例对象的内容发生操作,而普通类型作为Receiver仅仅是以副本作为操作对象,并不对原实例对象发生操作。后文对此会有详细论述。
+
+那是不是method只能作用在struct上面呢?当然不是咯,他可以定义在任何你自定义的类型、内置类型、struct等各种类型上面。这里你是不是有点迷糊了,什么叫自定义类型,自定义类型不就是struct嘛,不是这样的哦,struct只是自定义类型里面一种比较特殊的类型而已,还有其他自定义类型申明,可以通过如下这样的申明来实现。
+
+ type typeName typeLiteral
+
+请看下面这个申明自定义类型的代码
+
+ type ages int
+
+ type money float32
+
+ type months map[string]int
+
+ m := months {
+ "January":31,
+ "February":28,
+ ...
+ "December":31,
+ }
+
+看到了吗?简单的很吧,这样你就可以在自己的代码里面定义有意义的类型了,实际上只是一个定义了一个别名,有点类似于c中的typedef,例如上面ages替代了int
+
+好了,让我们回到`method`
+
+你可以在任何的自定义类型中定义任意多的`method`,接下来让我们看一个复杂一点的例子
+
+ package main
+ import "fmt"
+
+ const(
+ WHITE = iota
+ BLACK
+ BLUE
+ RED
+ YELLOW
+ )
+
+ type Color byte
+
+ type Box struct {
+ width, height, depth float64
+ color Color
+ }
+
+ type BoxList []Box //a slice of boxes
+
+ func (b Box) Volume() float64 {
+ return b.width * b.height * b.depth
+ }
+
+ func (b *Box) SetColor(c Color) {
+ b.color = c
+ }
+
+ func (bl BoxList) BiggestColor() Color {
+ v := 0.00
+ k := Color(WHITE)
+ for _, b := range bl {
+ if bv := b.Volume(); bv > v {
+ v = bv
+ k = b.color
+ }
+ }
+ return k
+ }
+
+ func (bl BoxList) PaintItBlack() {
+ for i, _ := range bl {
+ bl[i].SetColor(BLACK)
+ }
+ }
+
+ func (c Color) String() string {
+ strings := []string {"WHITE", "BLACK", "BLUE", "RED", "YELLOW"}
+ return strings[c]
+ }
+
+ func main() {
+ boxes := BoxList {
+ Box{4, 4, 4, RED},
+ Box{10, 10, 1, YELLOW},
+ Box{1, 1, 20, BLACK},
+ Box{10, 10, 1, BLUE},
+ Box{10, 30, 1, WHITE},
+ Box{20, 20, 20, YELLOW},
+ }
+
+ fmt.Printf("We have %d boxes in our set\n", len(boxes))
+ fmt.Println("The volume of the first one is", boxes[0].Volume(), "cm³")
+ fmt.Println("The color of the last one is",boxes[len(boxes)-1].color.String())
+ fmt.Println("The biggest one is", boxes.BiggestColor().String())
+
+ fmt.Println("Let's paint them all black")
+ boxes.PaintItBlack()
+ fmt.Println("The color of the second one is", boxes[1].color.String())
+
+ fmt.Println("Obviously, now, the biggest one is", boxes.BiggestColor().String())
+ }
+
+上面的代码通过const定义了一些常量,然后定义了一些自定义类型
+
+- Color作为byte的别名
+- 定义了一个struct:Box,含有三个长宽高字段和一个颜色属性
+- 定义了一个slice:BoxList,含有Box
+
+然后以上面的自定义类型为接收者定义了一些method
+
+- Volume()定义了接收者为Box,返回Box的容量
+- SetColor(c Color),把Box的颜色改为c
+- BiggestColor()定在在BoxList上面,返回list里面容量最大的颜色
+- PaintItBlack()把BoxList里面所有Box的颜色全部变成黑色
+- String()定义在Color上面,返回Color的具体颜色(字符串格式)
+
+上面的代码通过文字描述出来之后是不是很简单?我们一般解决问题都是通过问题的描述,去写相应的代码实现。
+
+### 指针作为receiver
+现在让我们回过头来看看SetColor这个method,它的receiver是一个指向Box的指针,是的,你可以使用*Box。想想为啥要使用指针而不是Box本身呢?
+
+我们定义SetColor的真正目的是想改变这个Box的颜色,如果不传Box的指针,那么SetColor接受的其实是Box的一个copy,也就是说method内对于颜色值的修改,其实只作用于Box的copy,而不是真正的Box。所以我们需要传入指针。
+
+这里可以把receiver当作method的第一个参数来看,然后结合前面函数讲解的传值和传引用就不难理解
+
+这里你也许会问了那SetColor函数里面应该这样定义`*b.Color=c`,而不是`b.Color=c`,因为我们需要读取到指针相应的值。
+
+你是对的,其实Go里面这两种方式都是正确的,当你用指针去访问相应的字段时(虽然指针没有任何的字段),Go知道你要通过指针去获取这个值,看到了吧,Go的设计是不是越来越吸引你了。
+
+也许细心的读者会问这样的问题,PaintItBlack里面调用SetColor的时候是不是应该写成`(&bl[i]).SetColor(BLACK)`,因为SetColor的receiver是*Box,而不是Box。
+
+你又说对的,这两种方式都可以,因为Go知道receiver是指针,他自动帮你转了。
+
+也就是说:
+>如果一个method的receiver是*T,你可以在一个T类型的实例变量V上面调用这个method,而不需要&V去调用这个method
+
+类似的
+>如果一个method的receiver是T,你可以在一个*T类型的变量P上面调用这个method,而不需要 *P去调用这个method
+
+所以,你不用担心你是调用的指针的method还是不是指针的method,Go知道你要做的一切,这对于有多年C/C++编程经验的同学来说,真是解决了一个很大的痛苦。
+
+### method继承
+前面一章我们学习了字段的继承,那么你也会发现Go的一个神奇之处,method也是可以继承的。如果匿名字段实现了一个method,那么包含这个匿名字段的struct也能调用该method。让我们来看下面这个例子
+
+ package main
+ import "fmt"
+
+ type Human struct {
+ name string
+ age int
+ phone string
+ }
+
+ type Student struct {
+ Human //匿名字段
+ school string
+ }
+
+ type Employee struct {
+ Human //匿名字段
+ company string
+ }
+
+ //在human上面定义了一个method
+ func (h *Human) SayHi() {
+ fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
+ }
+
+ func main() {
+ mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
+ sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
+
+ mark.SayHi()
+ sam.SayHi()
+ }
+
+### method重写
+上面的例子中,如果Employee想要实现自己的SayHi,怎么办?简单,和匿名字段冲突一样的道理,我们可以在Employee上面定义一个method,重写了匿名字段的方法。请看下面的例子
+
+ package main
+ import "fmt"
+
+ type Human struct {
+ name string
+ age int
+ phone string
+ }
+
+ type Student struct {
+ Human //匿名字段
+ school string
+ }
+
+ type Employee struct {
+ Human //匿名字段
+ company string
+ }
+
+ //Human定义method
+ func (h *Human) SayHi() {
+ fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
+ }
+
+ //Employee的method重写Human的method
+ func (e *Employee) SayHi() {
+ fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
+ e.company, e.phone) //Yes you can split into 2 lines here.
+ }
+
+ func main() {
+ mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
+ sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
+
+ mark.SayHi()
+ sam.SayHi()
+ }
+
+上面的代码设计的是如此的美妙,让人不自觉的为Go的设计惊叹!
+
+通过这些内容,我们可以设计出基本的面向对象的程序了,但是Go里面的面向对象是如此的简单,没有任何的私有、公有关键字,通过大小写来实现(大写开头的为公有,小写开头的为私有),方法也同样适用这个原则。
+## links
+ * [目录]()
+ * 上一章: [struct类型](<02.4.md>)
+ * 下一节: [interface](<02.6.md>)
diff --git a/zh/02.8.md b/zh/02.8.md~HEAD
similarity index 97%
rename from zh/02.8.md
rename to zh/02.8.md~HEAD
index 6ec9fa1d..a0ec6bb9 100644
--- a/zh/02.8.md
+++ b/zh/02.8.md~HEAD
@@ -1,31 +1,31 @@
-# 2.8 总结
-
-这一章我们主要介绍了Go语言的一些语法,通过语法我们可以发现Go是多么的简单,只有二十五个关键字。让我们再来回顾一下这些关键字都是用来干什么的。
-
- break default func interface select
- case defer go map struct
- chan else goto package switch
- const fallthrough if range type
- continue for import return var
-
-- var和const参考2.2Go语言基础里面的变量和常量申明
-- package和import已经有过短暂的接触
-- func 用于定义函数和方法
-- return 用于从函数返回
-- defer 用于类似析构函数
-- go 用于并发
-- select 用于选择不同类型的通讯
-- interface 用于定义接口,参考2.6小节
-- struct 用于定义抽象数据类型,参考2.5小节
-- break、case、continue、for、fallthrough、else、if、switch、goto、default这些参考2.3流程介绍里面
-- chan用于channel通讯
-- type用于声明自定义类型
-- map用于声明map类型数据
-- range用于读取slice、map、channel数据
-
-上面这二十五个关键字记住了,那么Go你也已经差不多学会了。
-
-## links
- * [目录]()
- * 上一节: [并发](<02.7.md>)
- * 下一章: [Web基础](<03.0.md>)
+# 2.8 总结
+
+这一章我们主要介绍了Go语言的一些语法,通过语法我们可以发现Go是多么的简单,只有二十五个关键字。让我们再来回顾一下这些关键字都是用来干什么的。
+
+ break default func interface select
+ case defer go map struct
+ chan else goto package switch
+ const fallthrough if range type
+ continue for import return var
+
+- var和const参考2.2Go语言基础里面的变量和常量申明
+- package和import已经有过短暂的接触
+- func 用于定义函数和方法
+- return 用于从函数返回
+- defer 用于类似析构函数
+- go 用于并发
+- select 用于选择不同类型的通讯
+- interface 用于定义接口,参考2.6小节
+- struct 用于定义抽象数据类型,参考2.5小节
+- break、case、continue、for、fallthrough、else、if、switch、goto、default这些参考2.3流程介绍里面
+- chan用于channel通讯
+- type用于声明自定义类型
+- map用于声明map类型数据
+- range用于读取slice、map、channel数据
+
+上面这二十五个关键字记住了,那么Go你也已经差不多学会了。
+
+## links
+ * [目录]()
+ * 上一节: [并发](<02.7.md>)
+ * 下一章: [Web基础](<03.0.md>)
diff --git a/zh/02.8.md~eead24cf064976b648de5826eab51880c803b0fa b/zh/02.8.md~eead24cf064976b648de5826eab51880c803b0fa
new file mode 100644
index 00000000..a0ec6bb9
--- /dev/null
+++ b/zh/02.8.md~eead24cf064976b648de5826eab51880c803b0fa
@@ -0,0 +1,31 @@
+# 2.8 总结
+
+这一章我们主要介绍了Go语言的一些语法,通过语法我们可以发现Go是多么的简单,只有二十五个关键字。让我们再来回顾一下这些关键字都是用来干什么的。
+
+ break default func interface select
+ case defer go map struct
+ chan else goto package switch
+ const fallthrough if range type
+ continue for import return var
+
+- var和const参考2.2Go语言基础里面的变量和常量申明
+- package和import已经有过短暂的接触
+- func 用于定义函数和方法
+- return 用于从函数返回
+- defer 用于类似析构函数
+- go 用于并发
+- select 用于选择不同类型的通讯
+- interface 用于定义接口,参考2.6小节
+- struct 用于定义抽象数据类型,参考2.5小节
+- break、case、continue、for、fallthrough、else、if、switch、goto、default这些参考2.3流程介绍里面
+- chan用于channel通讯
+- type用于声明自定义类型
+- map用于声明map类型数据
+- range用于读取slice、map、channel数据
+
+上面这二十五个关键字记住了,那么Go你也已经差不多学会了。
+
+## links
+ * [目录]()
+ * 上一节: [并发](<02.7.md>)
+ * 下一章: [Web基础](<03.0.md>)
diff --git a/zh/03.0.md b/zh/03.0.md~HEAD
similarity index 98%
rename from zh/03.0.md
rename to zh/03.0.md~HEAD
index 9d429bf9..1442717e 100644
--- a/zh/03.0.md
+++ b/zh/03.0.md~HEAD
@@ -1,11 +1,11 @@
-# 3 Web基础
-
-学习基于Web的编程可能正是你读本书的原因。事实上,如何通过Go来编写Web应用也是我编写这本书的初衷。前面已经介绍过,Go目前已经拥有了成熟的HTTP处理包,这使得编写能做任何事情的动态Web程序易如反掌。在接下来的各章中将要介绍的内容,都是属于Web编程的范畴。本章则集中讨论一些与Web相关的概念和Go如何运行Web程序的话题。
-
-## 目录
-
-
-## links
- * [目录]()
- * 上一章: [第二章总结](<02.8.md>)
- * 下一节: [Web工作方式](<03.1.md>)
+# 3 Web基础
+
+学习基于Web的编程可能正是你读本书的原因。事实上,如何通过Go来编写Web应用也是我编写这本书的初衷。前面已经介绍过,Go目前已经拥有了成熟的HTTP处理包,这使得编写能做任何事情的动态Web程序易如反掌。在接下来的各章中将要介绍的内容,都是属于Web编程的范畴。本章则集中讨论一些与Web相关的概念和Go如何运行Web程序的话题。
+
+## 目录
+
+
+## links
+ * [目录]()
+ * 上一章: [第二章总结](<02.8.md>)
+ * 下一节: [Web工作方式](<03.1.md>)
diff --git a/zh/03.0.md~eead24cf064976b648de5826eab51880c803b0fa b/zh/03.0.md~eead24cf064976b648de5826eab51880c803b0fa
new file mode 100644
index 00000000..1442717e
--- /dev/null
+++ b/zh/03.0.md~eead24cf064976b648de5826eab51880c803b0fa
@@ -0,0 +1,11 @@
+# 3 Web基础
+
+学习基于Web的编程可能正是你读本书的原因。事实上,如何通过Go来编写Web应用也是我编写这本书的初衷。前面已经介绍过,Go目前已经拥有了成熟的HTTP处理包,这使得编写能做任何事情的动态Web程序易如反掌。在接下来的各章中将要介绍的内容,都是属于Web编程的范畴。本章则集中讨论一些与Web相关的概念和Go如何运行Web程序的话题。
+
+## 目录
+
+
+## links
+ * [目录]()
+ * 上一章: [第二章总结](<02.8.md>)
+ * 下一节: [Web工作方式](<03.1.md>)
diff --git a/zh/03.2.md b/zh/03.2.md~HEAD
similarity index 97%
rename from zh/03.2.md
rename to zh/03.2.md~HEAD
index da77eec0..f926aeef 100644
--- a/zh/03.2.md
+++ b/zh/03.2.md~HEAD
@@ -1,66 +1,66 @@
-# 3.2 Go搭建一个Web服务器
-
-前面小节已经介绍了Web是基于http协议的一个服务,Go语言里面提供了一个完善的net/http包,通过http包可以很方便的就搭建起来一个可以运行的Web服务。同时使用这个包能很简单地对Web的路由,静态文件,模版,cookie等数据进行设置和操作。
-
-## http包建立Web服务器
-
- package main
-
- import (
- "fmt"
- "net/http"
- "strings"
- "log"
- )
-
- func sayhelloName(w http.ResponseWriter, r *http.Request) {
- r.ParseForm() //解析参数,默认是不会解析的
- fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
- fmt.Println("path", r.URL.Path)
- fmt.Println("scheme", r.URL.Scheme)
- fmt.Println(r.Form["url_long"])
- for k, v := range r.Form {
- fmt.Println("key:", k)
- fmt.Println("val:", strings.Join(v, ""))
- }
- fmt.Fprintf(w, "Hello astaxie!") //这个写入到w的是输出到客户端的
- }
-
- func main() {
- http.HandleFunc("/", sayhelloName) //设置访问的路由
- err := http.ListenAndServe(":9090", nil) //设置监听的端口
- if err != nil {
- log.Fatal("ListenAndServe: ", err)
- }
- }
-
-上面这个代码,我们build之后,然后执行web.exe,这个时候其实已经在9090端口监听http链接请求了。
-
-在浏览器输入`http://localhost:9090`
-
-可以看到浏览器页面输出了`Hello astaxie!`
-
-可以换一个地址试试:`http://localhost:9090/?url_long=111&url_long=222`
-
-看看浏览器输出的是什么,服务器输出的是什么?
-
-在服务器端输出的信息如下:
-
-
-
-图3.8 用户访问Web之后服务器端打印的信息
-
-我们看到上面的代码,要编写一个Web服务器很简单,只要调用http包的两个函数就可以了。
-
->如果你以前是PHP程序员,那你也许就会问,我们的nginx、apache服务器不需要吗?Go就是不需要这些,因为他直接就监听tcp端口了,做了nginx做的事情,然后sayhelloName这个其实就是我们写的逻辑函数了,跟php里面的控制层(controller)函数类似。
-
->如果你以前是Python程序员,那么你一定听说过tornado,这个代码和他是不是很像,对,没错,Go就是拥有类似Python这样动态语言的特性,写Web应用很方便。
-
->如果你以前是Ruby程序员,会发现和ROR的/script/server启动有点类似。
-
-我们看到Go通过简单的几行代码就已经运行起来一个Web服务了,而且这个Web服务内部有支持高并发的特性,我将会在接下来的两个小节里面详细的讲解一下Go是如何实现Web高并发的。
-
-## links
- * [目录]()
- * 上一节: [Web工作方式](<03.1.md>)
- * 下一节: [Go如何使得web工作](<03.3.md>)
+# 3.2 Go搭建一个Web服务器
+
+前面小节已经介绍了Web是基于http协议的一个服务,Go语言里面提供了一个完善的net/http包,通过http包可以很方便的就搭建起来一个可以运行的Web服务。同时使用这个包能很简单地对Web的路由,静态文件,模版,cookie等数据进行设置和操作。
+
+## http包建立Web服务器
+
+ package main
+
+ import (
+ "fmt"
+ "net/http"
+ "strings"
+ "log"
+ )
+
+ func sayhelloName(w http.ResponseWriter, r *http.Request) {
+ r.ParseForm() //解析参数,默认是不会解析的
+ fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
+ fmt.Println("path", r.URL.Path)
+ fmt.Println("scheme", r.URL.Scheme)
+ fmt.Println(r.Form["url_long"])
+ for k, v := range r.Form {
+ fmt.Println("key:", k)
+ fmt.Println("val:", strings.Join(v, ""))
+ }
+ fmt.Fprintf(w, "Hello astaxie!") //这个写入到w的是输出到客户端的
+ }
+
+ func main() {
+ http.HandleFunc("/", sayhelloName) //设置访问的路由
+ err := http.ListenAndServe(":9090", nil) //设置监听的端口
+ if err != nil {
+ log.Fatal("ListenAndServe: ", err)
+ }
+ }
+
+上面这个代码,我们build之后,然后执行web.exe,这个时候其实已经在9090端口监听http链接请求了。
+
+在浏览器输入`http://localhost:9090`
+
+可以看到浏览器页面输出了`Hello astaxie!`
+
+可以换一个地址试试:`http://localhost:9090/?url_long=111&url_long=222`
+
+看看浏览器输出的是什么,服务器输出的是什么?
+
+在服务器端输出的信息如下:
+
+
+
+图3.8 用户访问Web之后服务器端打印的信息
+
+我们看到上面的代码,要编写一个Web服务器很简单,只要调用http包的两个函数就可以了。
+
+>如果你以前是PHP程序员,那你也许就会问,我们的nginx、apache服务器不需要吗?Go就是不需要这些,因为他直接就监听tcp端口了,做了nginx做的事情,然后sayhelloName这个其实就是我们写的逻辑函数了,跟php里面的控制层(controller)函数类似。
+
+>如果你以前是Python程序员,那么你一定听说过tornado,这个代码和他是不是很像,对,没错,Go就是拥有类似Python这样动态语言的特性,写Web应用很方便。
+
+>如果你以前是Ruby程序员,会发现和ROR的/script/server启动有点类似。
+
+我们看到Go通过简单的几行代码就已经运行起来一个Web服务了,而且这个Web服务内部有支持高并发的特性,我将会在接下来的两个小节里面详细的讲解一下Go是如何实现Web高并发的。
+
+## links
+ * [目录]()
+ * 上一节: [Web工作方式](<03.1.md>)
+ * 下一节: [Go如何使得web工作](<03.3.md>)
diff --git a/zh/03.2.md~eead24cf064976b648de5826eab51880c803b0fa b/zh/03.2.md~eead24cf064976b648de5826eab51880c803b0fa
new file mode 100644
index 00000000..f926aeef
--- /dev/null
+++ b/zh/03.2.md~eead24cf064976b648de5826eab51880c803b0fa
@@ -0,0 +1,66 @@
+# 3.2 Go搭建一个Web服务器
+
+前面小节已经介绍了Web是基于http协议的一个服务,Go语言里面提供了一个完善的net/http包,通过http包可以很方便的就搭建起来一个可以运行的Web服务。同时使用这个包能很简单地对Web的路由,静态文件,模版,cookie等数据进行设置和操作。
+
+## http包建立Web服务器
+
+ package main
+
+ import (
+ "fmt"
+ "net/http"
+ "strings"
+ "log"
+ )
+
+ func sayhelloName(w http.ResponseWriter, r *http.Request) {
+ r.ParseForm() //解析参数,默认是不会解析的
+ fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
+ fmt.Println("path", r.URL.Path)
+ fmt.Println("scheme", r.URL.Scheme)
+ fmt.Println(r.Form["url_long"])
+ for k, v := range r.Form {
+ fmt.Println("key:", k)
+ fmt.Println("val:", strings.Join(v, ""))
+ }
+ fmt.Fprintf(w, "Hello astaxie!") //这个写入到w的是输出到客户端的
+ }
+
+ func main() {
+ http.HandleFunc("/", sayhelloName) //设置访问的路由
+ err := http.ListenAndServe(":9090", nil) //设置监听的端口
+ if err != nil {
+ log.Fatal("ListenAndServe: ", err)
+ }
+ }
+
+上面这个代码,我们build之后,然后执行web.exe,这个时候其实已经在9090端口监听http链接请求了。
+
+在浏览器输入`http://localhost:9090`
+
+可以看到浏览器页面输出了`Hello astaxie!`
+
+可以换一个地址试试:`http://localhost:9090/?url_long=111&url_long=222`
+
+看看浏览器输出的是什么,服务器输出的是什么?
+
+在服务器端输出的信息如下:
+
+
+
+图3.8 用户访问Web之后服务器端打印的信息
+
+我们看到上面的代码,要编写一个Web服务器很简单,只要调用http包的两个函数就可以了。
+
+>如果你以前是PHP程序员,那你也许就会问,我们的nginx、apache服务器不需要吗?Go就是不需要这些,因为他直接就监听tcp端口了,做了nginx做的事情,然后sayhelloName这个其实就是我们写的逻辑函数了,跟php里面的控制层(controller)函数类似。
+
+>如果你以前是Python程序员,那么你一定听说过tornado,这个代码和他是不是很像,对,没错,Go就是拥有类似Python这样动态语言的特性,写Web应用很方便。
+
+>如果你以前是Ruby程序员,会发现和ROR的/script/server启动有点类似。
+
+我们看到Go通过简单的几行代码就已经运行起来一个Web服务了,而且这个Web服务内部有支持高并发的特性,我将会在接下来的两个小节里面详细的讲解一下Go是如何实现Web高并发的。
+
+## links
+ * [目录]()
+ * 上一节: [Web工作方式](<03.1.md>)
+ * 下一节: [Go如何使得web工作](<03.3.md>)
diff --git a/zh/04.0.md b/zh/04.0.md~HEAD
similarity index 98%
rename from zh/04.0.md
rename to zh/04.0.md~HEAD
index 46ba3fc9..7b84acd1 100644
--- a/zh/04.0.md
+++ b/zh/04.0.md~HEAD
@@ -1,25 +1,25 @@
-# 4 表单
-
-表单是我们平常编写Web应用常用的工具,通过表单我们可以方便的让客户端和服务器进行数据的交互。对于以前开发过Web的用户来说表单都非常熟悉,但是对于C/C++程序员来说,这可能是一个有些陌生的东西,那么什么是表单呢?
-
-表单是一个包含表单元素的区域。表单元素是允许用户在表单中(比如:文本域、下拉列表、单选框、复选框等等)输入信息的元素。表单使用表单标签(\
-
-Go里面对于form处理已经有很方便的方法了,在Request里面的有专门的form处理,可以很方便的整合到Web开发里面来,4.1小节里面将讲解Go如何处理表单的输入。由于不能信任任何用户的输入,所以我们需要对这些输入进行有效性验证,4.2小节将就如何进行一些普通的验证进行详细的演示。
-
-HTTP协议是一种无状态的协议,那么如何才能辨别是否是同一个用户呢?同时又如何保证一个表单不出现多次递交的情况呢?4.3和4.4小节里面将对cookie(cookie是存储在客户端的信息,能够每次通过header和服务器进行交互的数据)等进行详细讲解。
-
-表单还有一个很大的功能就是能够上传文件,那么Go是如何处理文件上传的呢?针对大文件上传我们如何有效的处理呢?4.5小节我们将一起学习Go处理文件上传的知识。
-
-## 目录
-
-
-## links
- * [目录]()
- * 上一章: [第三章总结](<03.5.md>)
- * 下一节: [处理表单的输入](<04.1.md>)
+# 4 表单
+
+表单是我们平常编写Web应用常用的工具,通过表单我们可以方便的让客户端和服务器进行数据的交互。对于以前开发过Web的用户来说表单都非常熟悉,但是对于C/C++程序员来说,这可能是一个有些陌生的东西,那么什么是表单呢?
+
+表单是一个包含表单元素的区域。表单元素是允许用户在表单中(比如:文本域、下拉列表、单选框、复选框等等)输入信息的元素。表单使用表单标签(\
+
+Go里面对于form处理已经有很方便的方法了,在Request里面的有专门的form处理,可以很方便的整合到Web开发里面来,4.1小节里面将讲解Go如何处理表单的输入。由于不能信任任何用户的输入,所以我们需要对这些输入进行有效性验证,4.2小节将就如何进行一些普通的验证进行详细的演示。
+
+HTTP协议是一种无状态的协议,那么如何才能辨别是否是同一个用户呢?同时又如何保证一个表单不出现多次递交的情况呢?4.3和4.4小节里面将对cookie(cookie是存储在客户端的信息,能够每次通过header和服务器进行交互的数据)等进行详细讲解。
+
+表单还有一个很大的功能就是能够上传文件,那么Go是如何处理文件上传的呢?针对大文件上传我们如何有效的处理呢?4.5小节我们将一起学习Go处理文件上传的知识。
+
+## 目录
+
+
+## links
+ * [目录]()
+ * 上一章: [第三章总结](<03.5.md>)
+ * 下一节: [处理表单的输入](<04.1.md>)
diff --git a/zh/04.0.md~eead24cf064976b648de5826eab51880c803b0fa b/zh/04.0.md~eead24cf064976b648de5826eab51880c803b0fa
new file mode 100644
index 00000000..7b84acd1
--- /dev/null
+++ b/zh/04.0.md~eead24cf064976b648de5826eab51880c803b0fa
@@ -0,0 +1,25 @@
+# 4 表单
+
+表单是我们平常编写Web应用常用的工具,通过表单我们可以方便的让客户端和服务器进行数据的交互。对于以前开发过Web的用户来说表单都非常熟悉,但是对于C/C++程序员来说,这可能是一个有些陌生的东西,那么什么是表单呢?
+
+表单是一个包含表单元素的区域。表单元素是允许用户在表单中(比如:文本域、下拉列表、单选框、复选框等等)输入信息的元素。表单使用表单标签(\
+
+Go里面对于form处理已经有很方便的方法了,在Request里面的有专门的form处理,可以很方便的整合到Web开发里面来,4.1小节里面将讲解Go如何处理表单的输入。由于不能信任任何用户的输入,所以我们需要对这些输入进行有效性验证,4.2小节将就如何进行一些普通的验证进行详细的演示。
+
+HTTP协议是一种无状态的协议,那么如何才能辨别是否是同一个用户呢?同时又如何保证一个表单不出现多次递交的情况呢?4.3和4.4小节里面将对cookie(cookie是存储在客户端的信息,能够每次通过header和服务器进行交互的数据)等进行详细讲解。
+
+表单还有一个很大的功能就是能够上传文件,那么Go是如何处理文件上传的呢?针对大文件上传我们如何有效的处理呢?4.5小节我们将一起学习Go处理文件上传的知识。
+
+## 目录
+
+
+## links
+ * [目录]()
+ * 上一章: [第三章总结](<03.5.md>)
+ * 下一节: [处理表单的输入](<04.1.md>)
diff --git a/zh/04.1.md b/zh/04.1.md~HEAD
similarity index 97%
rename from zh/04.1.md
rename to zh/04.1.md~HEAD
index 4f7217c6..1f364713 100644
--- a/zh/04.1.md
+++ b/zh/04.1.md~HEAD
@@ -1,109 +1,109 @@
-# 4.1 处理表单的输入
-
-先来看一个表单递交的例子,我们有如下的表单内容,命名成文件login.gtpl(放入当前新建项目的目录里面)
-
-
-
-
-
-
-
-
-
-
-上面递交表单到服务器的`/login`,当用户输入信息点击登陆之后,会跳转到服务器的路由`login`里面,我们首先要判断这个是什么方式传递过来,POST还是GET呢?
-
-http包里面有一个很简单的方式就可以获取,我们在前面web的例子的基础上来看看怎么处理login页面的form数据
-
-
- package main
-
- import (
- "fmt"
- "html/template"
- "log"
- "net/http"
- "strings"
- )
-
- func sayhelloName(w http.ResponseWriter, r *http.Request) {
- r.ParseForm() //解析url传递的参数,对于POST则解析响应包的主体(request body)
- //注意:如果没有调用ParseForm方法,下面无法获取表单的数据
- fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
- fmt.Println("path", r.URL.Path)
- fmt.Println("scheme", r.URL.Scheme)
- fmt.Println(r.Form["url_long"])
- for k, v := range r.Form {
- fmt.Println("key:", k)
- fmt.Println("val:", strings.Join(v, ""))
- }
- fmt.Fprintf(w, "Hello astaxie!") //这个写入到w的是输出到客户端的
- }
-
- func login(w http.ResponseWriter, r *http.Request) {
- fmt.Println("method:", r.Method) //获取请求的方法
- if r.Method == "GET" {
- t, _ := template.ParseFiles("login.gtpl")
- log.Println(t.Execute(w, nil))
- } else {
- //请求的是登陆数据,那么执行登陆的逻辑判断
- fmt.Println("username:", r.Form["username"])
- fmt.Println("password:", r.Form["password"])
- }
- }
-
- func main() {
- http.HandleFunc("/", sayhelloName) //设置访问的路由
- http.HandleFunc("/login", login) //设置访问的路由
- err := http.ListenAndServe(":9090", nil) //设置监听的端口
- if err != nil {
- log.Fatal("ListenAndServe: ", err)
- }
- }
-
-
-通过上面的代码我们可以看出获取请求方法是通过`r.Method`来完成的,这是个字符串类型的变量,返回GET, POST, PUT等method信息。
-
-login函数中我们根据`r.Method`来判断是显示登录界面还是处理登录逻辑。当GET方式请求时显示登录界面,其他方式请求时则处理登录逻辑,如查询数据库、验证登录信息等。
-
-当我们在浏览器里面打开`http://127.0.0.1:9090/login`的时候,出现如下界面
-
-
-
-如果你看到一个空页面,可能是你写的 login.gtpl 文件中有错误,请根据控制台中的日志进行修复。
-
-图4.1 用户登录界面
-
-我们输入用户名和密码之后发现在服务器端是不会打印出来任何输出的,为什么呢?默认情况下,Handler里面是不会自动解析form的,必须显式的调用`r.ParseForm()`后,你才能对这个表单数据进行操作。我们修改一下代码,在`fmt.Println("username:", r.Form["username"])`之前加一行`r.ParseForm()`,重新编译,再次测试输入递交,现在是不是在服务器端有输出你的输入的用户名和密码了。
-
-`r.Form`里面包含了所有请求的参数,比如URL中query-string、POST的数据、PUT的数据,所有当你在URL的query-string字段和POST冲突时,会保存成一个slice,里面存储了多个值,Go官方文档中说在接下来的版本里面将会把POST、GET这些数据分离开来。
-
-现在我们修改一下login.gtpl里面form的action值`http://127.0.0.1:9090/login`修改为`http://127.0.0.1:9090/login?username=astaxie`,再次测试,服务器的输出username是不是一个slice。服务器端的输出如下:
-
-
-
-图4.2 服务器端打印接受到的信息
-
-`request.Form`是一个url.Values类型,里面存储的是对应的类似`key=value`的信息,下面展示了可以对form数据进行的一些操作:
-
- v := url.Values{}
- v.Set("name", "Ava")
- v.Add("friend", "Jess")
- v.Add("friend", "Sarah")
- v.Add("friend", "Zoe")
- // v.Encode() == "name=Ava&friend=Jess&friend=Sarah&friend=Zoe"
- fmt.Println(v.Get("name"))
- fmt.Println(v.Get("friend"))
- fmt.Println(v["friend"])
-
->**Tips**:
-Request本身也提供了FormValue()函数来获取用户提交的参数。如r.Form["username"]也可写成r.FormValue("username")。调用r.FormValue时会自动调用r.ParseForm,所以不必提前调用。r.FormValue只会返回同名参数中的第一个,若参数不存在则返回空字符串。
-
-## links
- * [目录]()
- * 上一节: [表单](<04.0.md>)
- * 下一节: [验证表单的输入](<04.2.md>)
+# 4.1 处理表单的输入
+
+先来看一个表单递交的例子,我们有如下的表单内容,命名成文件login.gtpl(放入当前新建项目的目录里面)
+
+
+
+
+
+
+
+
+
+
+上面递交表单到服务器的`/login`,当用户输入信息点击登陆之后,会跳转到服务器的路由`login`里面,我们首先要判断这个是什么方式传递过来,POST还是GET呢?
+
+http包里面有一个很简单的方式就可以获取,我们在前面web的例子的基础上来看看怎么处理login页面的form数据
+
+
+ package main
+
+ import (
+ "fmt"
+ "html/template"
+ "log"
+ "net/http"
+ "strings"
+ )
+
+ func sayhelloName(w http.ResponseWriter, r *http.Request) {
+ r.ParseForm() //解析url传递的参数,对于POST则解析响应包的主体(request body)
+ //注意:如果没有调用ParseForm方法,下面无法获取表单的数据
+ fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
+ fmt.Println("path", r.URL.Path)
+ fmt.Println("scheme", r.URL.Scheme)
+ fmt.Println(r.Form["url_long"])
+ for k, v := range r.Form {
+ fmt.Println("key:", k)
+ fmt.Println("val:", strings.Join(v, ""))
+ }
+ fmt.Fprintf(w, "Hello astaxie!") //这个写入到w的是输出到客户端的
+ }
+
+ func login(w http.ResponseWriter, r *http.Request) {
+ fmt.Println("method:", r.Method) //获取请求的方法
+ if r.Method == "GET" {
+ t, _ := template.ParseFiles("login.gtpl")
+ log.Println(t.Execute(w, nil))
+ } else {
+ //请求的是登陆数据,那么执行登陆的逻辑判断
+ fmt.Println("username:", r.Form["username"])
+ fmt.Println("password:", r.Form["password"])
+ }
+ }
+
+ func main() {
+ http.HandleFunc("/", sayhelloName) //设置访问的路由
+ http.HandleFunc("/login", login) //设置访问的路由
+ err := http.ListenAndServe(":9090", nil) //设置监听的端口
+ if err != nil {
+ log.Fatal("ListenAndServe: ", err)
+ }
+ }
+
+
+通过上面的代码我们可以看出获取请求方法是通过`r.Method`来完成的,这是个字符串类型的变量,返回GET, POST, PUT等method信息。
+
+login函数中我们根据`r.Method`来判断是显示登录界面还是处理登录逻辑。当GET方式请求时显示登录界面,其他方式请求时则处理登录逻辑,如查询数据库、验证登录信息等。
+
+当我们在浏览器里面打开`http://127.0.0.1:9090/login`的时候,出现如下界面
+
+
+
+如果你看到一个空页面,可能是你写的 login.gtpl 文件中有错误,请根据控制台中的日志进行修复。
+
+图4.1 用户登录界面
+
+我们输入用户名和密码之后发现在服务器端是不会打印出来任何输出的,为什么呢?默认情况下,Handler里面是不会自动解析form的,必须显式的调用`r.ParseForm()`后,你才能对这个表单数据进行操作。我们修改一下代码,在`fmt.Println("username:", r.Form["username"])`之前加一行`r.ParseForm()`,重新编译,再次测试输入递交,现在是不是在服务器端有输出你的输入的用户名和密码了。
+
+`r.Form`里面包含了所有请求的参数,比如URL中query-string、POST的数据、PUT的数据,所有当你在URL的query-string字段和POST冲突时,会保存成一个slice,里面存储了多个值,Go官方文档中说在接下来的版本里面将会把POST、GET这些数据分离开来。
+
+现在我们修改一下login.gtpl里面form的action值`http://127.0.0.1:9090/login`修改为`http://127.0.0.1:9090/login?username=astaxie`,再次测试,服务器的输出username是不是一个slice。服务器端的输出如下:
+
+
+
+图4.2 服务器端打印接受到的信息
+
+`request.Form`是一个url.Values类型,里面存储的是对应的类似`key=value`的信息,下面展示了可以对form数据进行的一些操作:
+
+ v := url.Values{}
+ v.Set("name", "Ava")
+ v.Add("friend", "Jess")
+ v.Add("friend", "Sarah")
+ v.Add("friend", "Zoe")
+ // v.Encode() == "name=Ava&friend=Jess&friend=Sarah&friend=Zoe"
+ fmt.Println(v.Get("name"))
+ fmt.Println(v.Get("friend"))
+ fmt.Println(v["friend"])
+
+>**Tips**:
+Request本身也提供了FormValue()函数来获取用户提交的参数。如r.Form["username"]也可写成r.FormValue("username")。调用r.FormValue时会自动调用r.ParseForm,所以不必提前调用。r.FormValue只会返回同名参数中的第一个,若参数不存在则返回空字符串。
+
+## links
+ * [目录]()
+ * 上一节: [表单](<04.0.md>)
+ * 下一节: [验证表单的输入](<04.2.md>)
diff --git a/zh/04.1.md~eead24cf064976b648de5826eab51880c803b0fa b/zh/04.1.md~eead24cf064976b648de5826eab51880c803b0fa
new file mode 100644
index 00000000..1f364713
--- /dev/null
+++ b/zh/04.1.md~eead24cf064976b648de5826eab51880c803b0fa
@@ -0,0 +1,109 @@
+# 4.1 处理表单的输入
+
+先来看一个表单递交的例子,我们有如下的表单内容,命名成文件login.gtpl(放入当前新建项目的目录里面)
+
+
+
+
+
+
+
+
+
+
+上面递交表单到服务器的`/login`,当用户输入信息点击登陆之后,会跳转到服务器的路由`login`里面,我们首先要判断这个是什么方式传递过来,POST还是GET呢?
+
+http包里面有一个很简单的方式就可以获取,我们在前面web的例子的基础上来看看怎么处理login页面的form数据
+
+
+ package main
+
+ import (
+ "fmt"
+ "html/template"
+ "log"
+ "net/http"
+ "strings"
+ )
+
+ func sayhelloName(w http.ResponseWriter, r *http.Request) {
+ r.ParseForm() //解析url传递的参数,对于POST则解析响应包的主体(request body)
+ //注意:如果没有调用ParseForm方法,下面无法获取表单的数据
+ fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
+ fmt.Println("path", r.URL.Path)
+ fmt.Println("scheme", r.URL.Scheme)
+ fmt.Println(r.Form["url_long"])
+ for k, v := range r.Form {
+ fmt.Println("key:", k)
+ fmt.Println("val:", strings.Join(v, ""))
+ }
+ fmt.Fprintf(w, "Hello astaxie!") //这个写入到w的是输出到客户端的
+ }
+
+ func login(w http.ResponseWriter, r *http.Request) {
+ fmt.Println("method:", r.Method) //获取请求的方法
+ if r.Method == "GET" {
+ t, _ := template.ParseFiles("login.gtpl")
+ log.Println(t.Execute(w, nil))
+ } else {
+ //请求的是登陆数据,那么执行登陆的逻辑判断
+ fmt.Println("username:", r.Form["username"])
+ fmt.Println("password:", r.Form["password"])
+ }
+ }
+
+ func main() {
+ http.HandleFunc("/", sayhelloName) //设置访问的路由
+ http.HandleFunc("/login", login) //设置访问的路由
+ err := http.ListenAndServe(":9090", nil) //设置监听的端口
+ if err != nil {
+ log.Fatal("ListenAndServe: ", err)
+ }
+ }
+
+
+通过上面的代码我们可以看出获取请求方法是通过`r.Method`来完成的,这是个字符串类型的变量,返回GET, POST, PUT等method信息。
+
+login函数中我们根据`r.Method`来判断是显示登录界面还是处理登录逻辑。当GET方式请求时显示登录界面,其他方式请求时则处理登录逻辑,如查询数据库、验证登录信息等。
+
+当我们在浏览器里面打开`http://127.0.0.1:9090/login`的时候,出现如下界面
+
+
+
+如果你看到一个空页面,可能是你写的 login.gtpl 文件中有错误,请根据控制台中的日志进行修复。
+
+图4.1 用户登录界面
+
+我们输入用户名和密码之后发现在服务器端是不会打印出来任何输出的,为什么呢?默认情况下,Handler里面是不会自动解析form的,必须显式的调用`r.ParseForm()`后,你才能对这个表单数据进行操作。我们修改一下代码,在`fmt.Println("username:", r.Form["username"])`之前加一行`r.ParseForm()`,重新编译,再次测试输入递交,现在是不是在服务器端有输出你的输入的用户名和密码了。
+
+`r.Form`里面包含了所有请求的参数,比如URL中query-string、POST的数据、PUT的数据,所有当你在URL的query-string字段和POST冲突时,会保存成一个slice,里面存储了多个值,Go官方文档中说在接下来的版本里面将会把POST、GET这些数据分离开来。
+
+现在我们修改一下login.gtpl里面form的action值`http://127.0.0.1:9090/login`修改为`http://127.0.0.1:9090/login?username=astaxie`,再次测试,服务器的输出username是不是一个slice。服务器端的输出如下:
+
+
+
+图4.2 服务器端打印接受到的信息
+
+`request.Form`是一个url.Values类型,里面存储的是对应的类似`key=value`的信息,下面展示了可以对form数据进行的一些操作:
+
+ v := url.Values{}
+ v.Set("name", "Ava")
+ v.Add("friend", "Jess")
+ v.Add("friend", "Sarah")
+ v.Add("friend", "Zoe")
+ // v.Encode() == "name=Ava&friend=Jess&friend=Sarah&friend=Zoe"
+ fmt.Println(v.Get("name"))
+ fmt.Println(v.Get("friend"))
+ fmt.Println(v["friend"])
+
+>**Tips**:
+Request本身也提供了FormValue()函数来获取用户提交的参数。如r.Form["username"]也可写成r.FormValue("username")。调用r.FormValue时会自动调用r.ParseForm,所以不必提前调用。r.FormValue只会返回同名参数中的第一个,若参数不存在则返回空字符串。
+
+## links
+ * [目录]()
+ * 上一节: [表单](<04.0.md>)
+ * 下一节: [验证表单的输入](<04.2.md>)
diff --git a/zh/04.2.md b/zh/04.2.md~HEAD
similarity index 97%
rename from zh/04.2.md
rename to zh/04.2.md~HEAD
index 2792454e..3984de75 100644
--- a/zh/04.2.md
+++ b/zh/04.2.md~HEAD
@@ -1,162 +1,162 @@
-# 4.2 验证表单的输入
-
-开发Web的一个原则就是,不能信任用户输入的任何信息,所以验证和过滤用户的输入信息就变得非常重要,我们经常会在微博、新闻中听到某某网站被入侵了,存在什么漏洞,这些大多是因为网站对于用户输入的信息没有做严格的验证引起的,所以为了编写出安全可靠的Web程序,验证表单输入的意义重大。
-
-我们平常编写Web应用主要有两方面的数据验证,一个是在页面端的js验证(目前在这方面有很多的插件库,比如ValidationJS插件),一个是在服务器端的验证,我们这小节讲解的是如何在服务器端验证。
-
-## 必填字段
-你想要确保从一个表单元素中得到一个值,例如前面小节里面的用户名,我们如何处理呢?Go有一个内置函数`len`可以获取字符串的长度,这样我们就可以通过len来获取数据的长度,例如:
-
- if len(r.Form["username"][0])==0{
- //为空的处理
- }
-
-`r.Form`对不同类型的表单元素的留空有不同的处理, 对于空文本框、空文本区域以及文件上传,元素的值为空值,而如果是未选中的复选框和单选按钮,则根本不会在r.Form中产生相应条目,如果我们用上面例子中的方式去获取数据时程序就会报错。所以我们需要通过`r.Form.Get()`来获取值,因为如果字段不存在,通过该方式获取的是空值。但是通过`r.Form.Get()`只能获取单个的值,如果是map的值,必须通过上面的方式来获取。
-
-## 数字
-你想要确保一个表单输入框中获取的只能是数字,例如,你想通过表单获取某个人的具体年龄是50岁还是10岁,而不是像“一把年纪了”或“年轻着呢”这种描述
-
-如果我们是判断正整数,那么我们先转化成int类型,然后进行处理
-
- getint,err:=strconv.Atoi(r.Form.Get("age"))
- if err!=nil{
- //数字转化出错了,那么可能就不是数字
- }
-
- //接下来就可以判断这个数字的大小范围了
- if getint >100 {
- //太大了
- }
-
-还有一种方式就是正则匹配的方式
-
- if m, _ := regexp.MatchString("^[0-9]+$", r.Form.Get("age")); !m {
- return false
- }
-
-对于性能要求很高的用户来说,这是一个老生常谈的问题了,他们认为应该尽量避免使用正则表达式,因为使用正则表达式的速度会比较慢。但是在目前机器性能那么强劲的情况下,对于这种简单的正则表达式效率和类型转换函数是没有什么差别的。如果你对正则表达式很熟悉,而且你在其它语言中也在使用它,那么在Go里面使用正则表达式将是一个便利的方式。
-
->Go实现的正则是[RE2](http://code.google.com/p/re2/wiki/Syntax),所有的字符都是UTF-8编码的。
-
-## 中文
-有时候我们想通过表单元素获取一个用户的中文名字,但是又为了保证获取的是正确的中文,我们需要进行验证,而不是用户随便的一些输入。对于中文我们目前有两种方式来验证,可以使用 `unicode` 包提供的 `func Is(rangeTab *RangeTable, r rune) bool` 来验证,也可以使用正则方式来验证,这里使用最简单的正则方式,如下代码所示
-
- if m, _ := regexp.MatchString("^\\p{Han}+$", r.Form.Get("realname")); !m {
- return false
- }
-
-## 英文
-我们期望通过表单元素获取一个英文值,例如我们想知道一个用户的英文名,应该是astaxie,而不是asta谢。
-
-我们可以很简单的通过正则验证数据:
-
- if m, _ := regexp.MatchString("^[a-zA-Z]+$", r.Form.Get("engname")); !m {
- return false
- }
-
-
-## 电子邮件地址
-你想知道用户输入的一个Email地址是否正确,通过如下这个方式可以验证:
-
- if m, _ := regexp.MatchString(`^([\w\.\_]{2,10})@(\w{1,}).([a-z]{2,4})$`, r.Form.Get("email")); !m {
- fmt.Println("no")
- }else{
- fmt.Println("yes")
- }
-
-
-## 手机号码
-你想要判断用户输入的手机号码是否正确,通过正则也可以验证:
-
- if m, _ := regexp.MatchString(`^(1[3|4|5|8][0-9]\d{4,8})$`, r.Form.Get("mobile")); !m {
- return false
- }
-
-## 下拉菜单
-如果我们想要判断表单里面`