[
  {
    "path": ".gitignore",
    "content": "# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# Folders\n_obj\n_test\n.idea\n\n# Architecture specific extensions/prefixes\n*.[568vq]\n[568vq].out\n\n*.cgo1.go\n*.cgo2.c\n_cgo_defun.c\n_cgo_gotypes.go\n_cgo_export.*\n\n_testmain.go\n\n*.exe\n.DS_Store\nthe-way-to-go.sublime-project\nthe-way-to-go.sublime-workspace\n\n*.pdf\n\n*.html\n*.htm\n"
  },
  {
    "path": "README.md",
    "content": "《Go入门指南》\r\n===================\r\n\r\n在接触 Go 语言之后，对这门编程语言非常着迷，期间也陆陆续续开始一些帮助国内编程爱好者了解和发展 Go 语言的工作，比如开始录制视频教程[《Go编程基础》](https://github.com/Unknwon/go-fundamental-programming)。但由于目前国内并没有比较好的 Go 语言书籍，而国外的优秀书籍因为英文的缘故在一定程度上也为不少 Go 语言爱好者带来了一些学习上的困扰，不仅为了加快扩散 Go 爱好者的国内群体，本人在完成阅读这本名叫 《The Way to Go》 之后，决定每天抽出一点时间来进行翻译的工作，并且以开源的形式免费分享给有需要的 Go 语言爱好者。\r\n\r\n尽管该书对目前 Go 语言版本来说有小部分内容相对过时，但是为当下不可多得的好书，相关内容已获得作者同意根据当前 Go 语言版本进行修改而不作出特别声明。\r\n\r\n该翻译版本已获得原作者（Ivo Balbaert）本人授权，并表示支持开源事业的发展！\r\n\r\n## 翻译进度\r\n\r\n19.10 [总结和增强](eBook/19.10.md)\r\n\r\n## 支持本书\r\n\r\n如果你喜欢本书 《Go入门指南》，你可以参与到本书的翻译或纠正工作中来，具体请联系【无闻 E-mail：u#gogs.io】，一同完善本书并帮助壮大 Go 语言在国内的学习群体，给大家提供更好的学习资源。\r\n\r\n## 交流社区\r\n\r\n参见 [Go 语言学习资料与社区索引](https://github.com/Unknwon/go-study-index)。\r\n\r\n关注 Go 语言中文网公众号学习 Go\r\n\r\n![](studygolang-qrcode.jpg)\r\n\r\n### 新人守则\r\n\r\n- 2012 年 3 月 28 日以前的博文中的内容基本过时，不要再看\r\n- 符合等式 ***百度+思考+失败+翻墙+谷歌+尝试=解决*** 的问题最好不要发问\r\n\r\n## 致谢\r\n\r\n- 本书原作者：Ivo Balbaert\r\n- 参与翻译人员：\r\n\t- [@zhanming](https://github.com/zhanming)\r\n\t- themorecolor\r\n\t- [@everyx](https://github.com/everyx)\r\n\t- [@chidouhu](https://github.com/chidouhu)\r\n\t- [@spawnris](https://github.com/spawnris)\r\n\t- [@domainname](https://github.com/domainname)\r\n\t- [@leisore](https://github.com/leisore)\r\n\t- [@dake](https://github.com/dake)\r\n\t- [@glight2000](https://github.com/glight2000)\r\n\t- [@songleo](https://github.com/songleo)\r\n\t- [@marjune163](https://github.com/marjune163)\r\n\r\n## 授权许可\r\n\r\n除特别声明外，本书中的内容使用 [CC BY-SA 3.0 License](http://creativecommons.org/licenses/by-sa/3.0/)（创作共用 署名-相同方式共享3.0 许可协议）授权，代码遵循 [BSD 3-Clause License](https://github.com/astaxie/build-web-application-with-golang/blob/master/LICENSE.md)（3 项条款的 BSD 许可协议）。\r\n\r\n## 开始阅读\r\n\r\n- 您可以选择以下方式阅读本书：\r\n  - [GitHub在线](./eBook/preface.md)\r\n\r\n想读书的人，不会找不到 [目录](eBook/directory.md) :)\r\n\r\n"
  },
  {
    "path": "README_gc.md",
    "content": "# Go 入门指南\n\n## 本书介绍\n\n在接触 Go 语言之后，对这门编程语言非常着迷，期间也陆陆续续开始一些帮助国内编程爱好者了解和发展 Go 语言的工作，比如开始录制视频教程[《Go编程基础》](https://github.com/Unknwon/go-fundamental-programming)。但由于目前国内并没有比较好的 Go 语言书籍，而国外的优秀书籍因为英文的缘故在一定程度上也为不少 Go 语言爱好者带来了一些学习上的困扰，不仅为了加快扩散 Go 爱好者的国内群体，本人在完成阅读这本名叫 《The Way to Go》 之后，决定每天抽出一点时间来进行翻译的工作，并且以开源的形式免费分享给有需要的 Go 语言爱好者。\n\n## 关于译者\n\n本书的主要译者是 [@无闻Unknwon](http://www.weibo.com/Obahua)，是一名 Go 语言爱好者和传播者，目前是 [Go Walker](https://gowalker.org)、Gopm、[Gogs](http://gogs.io) 和 [Macaron](https://github.com/Unknwon/macaron) 创始人，极客学院签约讲师。\n\n目前已由多位 Go 语言爱好者共同提交翻译内容，主要包括：\n\n- [@zhanming](https://github.com/zhanming)\n- themorecolor\n- [@everyx](https://github.com/everyx)\n- [@chidouhu](https://github.com/chidouhu)\n- [@spawnris](https://github.com/spawnris)\n- [@domainname](https://github.com/domainname)\n- [@leisore](https://github.com/leisore)\n- [@dake](https://github.com/dake)\n- [@glight2000](https://github.com/glight2000)\n- [@songleo](https://github.com/songleo)\n\n## 适用人群\n\n适合有一定编程基础，初学 Go 语言的爱好者。\n\n>\nMartini&Macaron 交流群：371440803\n>\nGolang 编程：245386165\n\n|更新日期    |更新内容\n|----------|------------------\n|2015-1-6|14.6 协程和恢复（recover）\n"
  },
  {
    "path": "TOC.md",
    "content": "- [前言](eBook/preface.md)\r\n- 第一部分：学习 Go 语言\r\n    - 第1章：Go 语言的起源，发展与普及\r\n        - 1.1 [起源与发展](eBook/01.1.md)\r\n\t    - 1.2 [语言的主要特性与发展的环境和影响因素](eBook/01.2.md)\r\n    - 第2章：安装与运行环境\r\n\t    - 2.1 [平台与架构](eBook/02.1.md)\r\n\t    - 2.2 [Go 环境变量](eBook/02.2.md)\r\n\t    - 2.3 [在 Linux 上安装 Go](eBook/02.3.md)\r\n\t    - 2.4 [在 Mac OS X 上安装 Go](eBook/02.4.md)\r\n\t    - 2.5 [在 Windows 上安装 Go](eBook/02.5.md)\r\n\t    - 2.6 [安装目录清单](eBook/02.6.md)\r\n\t    - 2.7 [Go 运行时（runtime）](eBook/02.7.md)\r\n\t    - 2.8 [Go 解释器](eBook/02.8.md)\r\n    - 第3章：[编辑器、集成开发环境与其它工具](eBook/03.0.md)\r\n\t    - 3.1 [Go 开发环境的基本要求](eBook/03.1.md)\r\n\t    - 3.2 [编辑器和集成开发环境](eBook/03.2.md)\r\n\t    - 3.3 [调试器](eBook/03.3.md)\r\n\t    - 3.4 [构建并运行 Go 程序](eBook/03.4.md)\r\n\t    - 3.5 [格式化代码](eBook/03.5.md)\r\n\t    - 3.6 [生成代码文档](eBook/03.6.md)\r\n\t    - 3.7 [其它工具](eBook/03.7.md)\r\n\t    - 3.8 [Go 性能说明](eBook/03.8.md)\r\n\t    - 3.9 [与其它语言进行交互](eBook/03.9.md)\r\n- 第二部分：语言的核心结构与技术\r\n    - 第4章：基本结构和基本数据类型\r\n\t    - 4.1 [文件名、关键字与标识符](eBook/04.1.md)\r\n\t    - 4.2 [Go 程序的基本结构和要素](eBook/04.2.md)\r\n\t    - 4.3 [常量](eBook/04.3.md)\r\n\t    - 4.4 [变量](eBook/04.4.md)\r\n\t    - 4.5 [基本类型和运算符](eBook/04.5.md)\r\n\t    - 4.6 [字符串](eBook/04.6.md)\r\n\t    - 4.7 [strings 和 strconv 包](eBook/04.7.md)\r\n\t    - 4.8 [时间和日期](eBook/04.8.md)\r\n\t    - 4.9 [指针](eBook/04.9.md)\r\n    - 第5章：[控制结构](eBook/05.0.md)\r\n\t    - 5.1 [if-else 结构](eBook/05.1.md)\r\n\t    - 5.2 [测试多返回值函数的错误](eBook/05.2.md)\r\n\t    - 5.3 [switch 结构](eBook/05.3.md)\r\n\t    - 5.4 [for 结构](eBook/05.4.md)\r\n\t    - 5.5 [Break 与 continue](eBook/05.5.md)\r\n\t    - 5.6 [标签与 goto](eBook/05.6.md)\r\n    - 第6章：[函数（function）](eBook/06.0.md)\r\n\t    - 6.1 [介绍](eBook/06.1.md)\r\n\t    - 6.2 [函数参数与返回值](eBook/06.2.md)\r\n\t    - 6.3 [传递变长参数](eBook/06.3.md)\r\n\t    - 6.4 [defer 和追踪](eBook/06.4.md)\r\n\t    - 6.5 [内置函数](eBook/06.5.md)\r\n\t    - 6.6 [递归函数](eBook/06.6.md)\r\n\t    - 6.7 [将函数作为参数](eBook/06.7.md)\r\n\t    - 6.8 [闭包](eBook/06.8.md)\r\n\t    - 6.9 [应用闭包：将函数作为返回值](eBook/06.9.md)\r\n\t    - 6.10 [使用闭包调试](eBook/06.10.md)\r\n\t    - 6.11 [计算函数执行时间](eBook/06.11.md)\r\n\t    - 6.12 [通过内存缓存来提升性能](eBook/06.12.md)\r\n    - 第7章：[数组与切片](eBook/07.0.md)\r\n\t    - 7.1 [声明和初始化](eBook/07.1.md)\r\n\t    - 7.2 [切片](eBook/07.2.md)\r\n\t    - 7.3 [For-range 结构](eBook/07.3.md)\r\n\t    - 7.4 [切片重组（reslice）](eBook/07.4.md)\r\n\t    - 7.5 [切片的复制与追加](eBook/07.5.md)\r\n\t\t- 7.6 [字符串、数组和切片的应用](eBook/07.6.md)\r\n\t- 第8章：[Map](eBook/08.0.md)\r\n\t\t- 8.1 [声明、初始化和 make](eBook/08.1.md)\r\n\t\t- 8.2 [测试键值对是否存在及删除元素](eBook/08.2.md)\r\n\t\t- 8.3 [for-range 的配套用法](eBook/08.3.md)\r\n\t\t- 8.4 [map 类型的切片](eBook/08.4.md)\r\n\t\t- 8.5 [map 的排序](eBook/08.5.md)\r\n\t\t- 8.6 [将 map 的键值对调](eBook/08.6.md)\r\n\t- 第9章：[包（package）](eBook/09.0.md)\r\n\t\t- 9.1 [标准库概述](eBook/09.1.md)\r\n\t\t- 9.2 [regexp 包](eBook/09.2.md)\r\n\t\t- 9.3 [锁和 sync 包](eBook/09.3.md)\r\n\t\t- 9.4 [精密计算和 big 包](eBook/09.4.md)\r\n\t\t- 9.5 [自定义包和可见性](eBook/09.5.md)\r\n\t\t- 9.6 [为自定义包使用 godoc](eBook/09.6.md)\r\n\t\t- 9.7 [使用 go install 安装自定义包](eBook/09.7.md)\r\n\t\t- 9.8 [自定义包的目录结构、go install 和 go test](eBook/09.8.md)\r\n\t\t- 9.9 [通过 Git 打包和安装](eBook/09.9.md)\r\n\t\t- 9.10 [Go 的外部包和项目](eBook/09.10.md)\r\n\t\t- 9.11 [在 Go 程序中使用外部库](eBook/09.11.md)\r\n\t- 第10章：[结构（struct）与方法（method）](eBook/10.0.md)\r\n\t    - 10.1 [结构体定义](eBook/10.1.md)\r\n\t    - 10.2 [使用工厂方法创建结构体实例](eBook/10.2.md)\r\n\t    - 10.3 [使用自定义包中的结构体](eBook/10.3.md)\r\n\t    - 10.4 [带标签的结构体](eBook/10.4.md)\r\n\t    - 10.5 [匿名字段和内嵌结构体](eBook/10.5.md)\r\n\t    - 10.6 [方法](eBook/10.6.md)\r\n\t    - 10.7 [类型的 String() 方法和格式化描述符](eBook/10.7.md)\r\n\t    - 10.8 [垃圾回收和 SetFinalizer](eBook/10.8.md)\r\n\t- 第11章：[接口（interface）与反射（reflection）](eBook/11.0.md)\r\n\t    - 11.1 [接口是什么](eBook/11.1.md)\r\n\t    - 11.2 [接口嵌套接口](eBook/11.2.md)\r\n\t    - 11.3 [类型断言：如何检测和转换接口变量的类型](eBook/11.3.md)\r\n\t    - 11.4 [类型判断：type-switch](eBook/11.4.md)\r\n\t    - 11.5 [测试一个值是否实现了某个接口](eBook/11.5.md)\r\n\t    - 11.6 [使用方法集与接口](eBook/11.6.md)\r\n\t    - 11.7 [第一个例子：使用 Sorter 接口排序](eBook/11.7.md)\r\n\t    - 11.8 [第二个例子：读和写](eBook/11.8.md)\r\n\t    - 11.9 [空接口](eBook/11.9.md)\r\n        - 11.10 [反射包](eBook/11.10.md)\r\n        - 11.11 [Printf 和反射](eBook/11.11.md)\r\n        - 11.12 [接口与动态类型](eBook/11.12.md)\r\n        - 11.13 [总结：Go 中的面向对象](eBook/11.13.md)\r\n        - 11.14 [结构体、集合和高阶函数](eBook/11.14.md)\r\n- 第三部分：Go 高级编程\r\n    - 第12章：[读写数据](eBook/12.0.md)\r\n        - 12.1 [读取用户的输入](eBook/12.1.md)\r\n        - 12.2 [文件读写](eBook/12.2.md)\r\n        - 12.3 [文件拷贝](eBook/12.3.md)\r\n        - 12.4 [从命令行读取参数](eBook/12.4.md)\r\n        - 12.5 [用 buffer 读取文件](eBook/12.5.md)\r\n        - 12.6 [用切片读写文件](eBook/12.6.md)\r\n        - 12.7 [用 defer 关闭文件](eBook/12.7.md)\r\n        - 12.8 [使用接口的实际例子：fmt.Fprintf](eBook/12.8.md)\r\n        - 12.9 [格式化 JSON 数据](eBook/12.9.md)\r\n        - 12.10 [XML 数据格式](eBook/12.10.md)\r\n        - 12.11 [用 Gob 传输数据](eBook/12.11.md)\r\n        - 12.12 [Go 中的密码学](eBook/12.12.md)\r\n    - 第13章：[错误处理与测试](eBook/13.0.md)\r\n        - 13.1 [错误处理](eBook/13.1.md)\r\n        - 13.2 [运行时异常和 panic](eBook/13.2.md)\r\n        - 13.3 [从 panic 中恢复（Recover）](eBook/13.3.md)\r\n        - 13.4 [自定义包中的错误处理和 panicking](eBook/13.4.md)\r\n        - 13.5 [一种用闭包处理错误的模式](eBook/13.5.md)\r\n        - 13.6 [启动外部命令和程序](eBook/13.6.md)\r\n        - 13.7 [Go 中的单元测试和基准测试](eBook/13.7.md)\r\n        - 13.8 [测试的具体例子](eBook/13.8.md)\r\n        - 13.9 [用（测试数据）表驱动测试](eBook/13.9.md)\r\n        - 13.10 [性能调试：分析并优化 Go 程序](eBook/13.10.md)\r\n"
  },
  {
    "path": "config.json",
    "content": "{\n\t\"name\": \"Go 入门指南\",\n\t\"introduction\": \"Go 经典书籍《The Way To Go》的中文译本。\",\n\t\"path\": {\n\t\t\"content\": \"eBook\",\n\t\t\"readme\": \"README_gc.md\"\n\t}\n\n}\n"
  },
  {
    "path": "eBook/01.1.md",
    "content": "# 1.1 起源与发展\n\nGo 语言起源 2007 年，并于 2009 年正式对外发布。它从 2009 年 9 月 21 日开始作为谷歌公司 20% 兼职项目，即相关员工利用 20% 的空余时间来参与 Go 语言的研发工作。该项目的三位领导者均是著名的 IT 工程师：Robert Griesemer，参与开发 Java HotSpot 虚拟机；Rob Pike，Go 语言项目总负责人，贝尔实验室 Unix 团队成员，参与的项目包括 Plan 9，Inferno 操作系统和 Limbo 编程语言；Ken Thompson，贝尔实验室 Unix 团队成员，C 语言、Unix 和 Plan 9 的创始人之一，与 Rob Pike 共同开发了 UTF-8 字符集规范。自 2008 年 1 月起，Ken Thompson 就开始研发一款以 C 语言为目标结果的编译器来拓展 Go 语言的设计思想。\n\n**这是一个由计算机领域 “发明之父” 所组成的黄金团队，他们对系统编程语言，操作系统和并行都有着非常深刻的见解**\n\n![](images/1.1.designers_of_Go.jpg?raw=true)\n\n<center>图 1.1 Go 语言设计者：Griesemer、Thompson 和 Pike</center>\n\n在 2008 年年中，Go 语言的设计工作接近尾声，一些员工开始以全职工作状态投入到这个项目的编译器和运行实现上。Ian Lance Taylor 也加入到了开发团队中，并于 2008 年 5 月创建了一个 gcc 前端。\n\nRuss Cox 加入开发团队后着手语言和类库方面的开发，也就是 Go 语言的标准包。在 2009 年 10 月 30 日，Rob Pike 以 Google Techtalk 的形式第一次向人们宣告了 Go 语言的存在。\n\n直到 2009 年 11 月 10 日，开发团队将 Go 语言项目以 BSD-style 授权（完全开源）正式公布了 Linux 和 Mac OS X 平台上的版本。Hector Chu 于同年 11 月 22 日公布了 Windows 版本。\n\n作为一个开源项目，Go 语言借助开源社区的有生力量达到快速地发展，并吸引更多的开发者来使用并改善它。自该开源项目发布以来，超过 200 名非谷歌员工的贡献者对 Go 语言核心部分提交了超过 1000 个修改建议。在过去的 18 个月里，又有 150 开发者贡献了新的核心代码。这俨然形成了世界上最大的开源团队，并使该项目跻身 [Ohloh](http://www.ohloh.net) 前 2% 的行列。大约在 2011 年 4 月 10 日，谷歌开始抽调员工进入全职开发 Go 语言项目。开源化的语言显然能够让更多的开发者参与其中并加速它的发展速度。Andrew Gerrand 在 2010 年加入到开发团队中成为共同开发者与支持者。\n\n在 Go 语言在 2010 年 1 月 8 日被 [Tiobe](http://www.tiobe.com)（闻名于它的编程语言流行程度排名）宣布为 “2009 年年度语言” 后，引起各界很大的反响。目前 Go 语言在这项排名中的最高记录是在 2017 年 1 月创下的第13名，流行程度 2.325%。\n\n### 时间轴：\n\n- 2007 年 9 月 21 日：雏形设计\n- 2009 年 11 月 10日：首次公开发布\n- 2010 年 1 月 8 日：当选 2009 年年度语言\n- 2010 年 5 月：谷歌投入使用\n- 2011 年 5 月 5 日：Google App Engine 支持 Go 语言\n\n从 2010 年 5 月起，谷歌开始将 Go 语言投入到后端基础设施的实际开发中，例如开发用于管理后端复杂环境的项目。有句话叫 “吃你自己的狗食”，这也体现了谷歌确实想要投资这门语言，并认为它是有生产价值的。\n\nGo 语言的官方网站是 [golang.org](http://golang.org)，这个站点采用 Python 作为前端，并且使用 Go 语言自带的工具 godoc 运行在 Google App Engine 上来作为 Web 服务器提供文本内容。在官网的首页有一个功能叫做 Go Playground，是一个 Go 代码的简单编辑器的沙盒，它可以在没有安装 Go 语言的情况下在你的浏览器中编译并运行 Go，它提供了一些示例，其中包括国际惯例 “Hello, World!”。\n\n更多的信息详见 [github.com/golang/go](https://github.com/golang/go)，Go 项目 Bug 追踪和功能预期详见 [github.com/golang/go/issues](https://github.com/golang/go/issues)。\n\nGo 通过以下的 Logo 来展示它的速度，并以囊地鼠 (Gopher) 作为它的吉祥物。\n\n![](images/1.2.Go_logo.jpg?raw=true)\n\n<center>图1.2 Go 语言 Logo</center>\n\n谷歌邮件列表 [golang-nuts](http://groups.google.com/group/golang-nuts/) 非常活跃，每天的讨论和问题解答数以百计。\n\n关于 Go 语言在 Google App Engine 的应用，这里有一个单独的邮件列表 [google-appengine-go](https://groups.google.com/forum/#!forum/google-appengine-go)，不过 2 个邮件列表的讨论内容并不是分得很清楚，都会涉及到相关的话题。[go-lang.cat-v.org/](http://go-lang.cat-v.org/) 是 Go 语言开发社区的资源站，[irc.freenode.net](http://irc.freenode.net) 的 #go-nuts 是官方的 Go IRC 频道。\n\n[@golang](https://twitter.com/golang) 是 Go 语言在 Twitter 的官方帐号，大家一般使用 #golang 作为话题标签。\n\n这里还有一个在 Linked-in 的小组：[www.linkedin.com/groups?gid=2524765&trk=myg_ugrp_ovr](http://www.linkedin.com/groups?gid=2524765&trk=myg_ugrp_ovr)。\n\nGo 编程语言的维基百科：[en.wikipedia.org/wiki/Go_(programming_language)](http://en.wikipedia.org/wiki/Go_\\(programming_language\\))\n\nGo 语言相关资源的搜索引擎页面：[gowalker.org](https://gowalker.org)\n\nGo 语言还有一个运行在 Google App Engine 上的 [Go Tour](http://tour.golang.org/)，你也可以通过执行命令 `go install go-tour.googlecode.com/hg/gotour` 安装到你的本地机器上。对于中文读者，可以访问该指南的 [中文版本](https://tour.go-zh.org/welcome/1)，或通过命令 `go install https://bitbucket.org/mikespook/go-tour-zh/gotour` 进行安装。\n\n## 链接\n\n- [目录](directory.md)\n- 上一部分：[前言](preface.md)\n- 下一节: [语言的主要特性与发展的环境和影响因素](01.2.md)\n"
  },
  {
    "path": "eBook/01.2.md",
    "content": "# 1.2 语言的主要特性与发展的环境和影响因素\r\n\r\n## 1.2.1 影响 Go 语言发展的早期编程语言\r\n\r\n正如 “21 世纪的 C 语言” 这句话所说，Go 语言并不是凭空而造的，而是和 C++、Java 和 C# 一样属于 C 系。不仅如此，设计者们还汲取了其它编程语言的精粹部分融入到 Go 语言当中。\r\n\r\n在声明和包的设计方面，Go 语言受到 Pascal、Modula 和 Oberon 系语言的影响；在并发原理的设计上，Go 语言从同样受到 Tony Hoare 的 CSP（通信序列进程 *Communicating Sequential Processes*）理论影响的 Limbo 和 Newsqueak 的实践中借鉴了一些经验，并使用了和 Erlang 类似的机制。\r\n\r\n这是一门完全开源的编程语言，因为它使用 BSD 授权许可，所以任何人都可以进行商业软件的开发而不需要支付任何费用。\r\n\r\n尽管为了能够让目前主流的开发者们能够对 Go 语言中的类 C 语言的语法感到非常亲切而易于转型，但是它在极大程度上简化了这些语法，使得它们比 C/C++ 的语法更加简洁和干净。同时，Go 语言也拥有一些动态语言的特性，这使得使用 Python 和 Ruby 的开发者们在使用 Go 语言的时候感觉非常容易上手。\r\n\r\n下图展示了一些其它编程语言对 Go 语言的影响：\r\n\r\n![](images/1.3.influences_on_go.jpg?raw=true)\r\n\r\n图 1.3 其它编程语言对 Go 语言的影响\r\n\r\n## 1.2.2 为什么要创造一门编程语言\r\n\r\n- C/C++ 的发展速度无法跟上计算机发展的脚步，十多年来也没有出现一门与时代相符的主流系统编程语言，因此人们需要一门新的系统编程语言来弥补这个空缺，尤其是在计算机信息时代。\r\n- 相比计算机性能的提升，软件开发领域不被认为发展得足够快或者比硬件发展得更加成功（有许多项目均以失败告终），同时应用程序的体积始终在不断地扩大，这就迫切地需要一门具备更高层次概念的低级语言来突破现状。\r\n- 在 Go 语言出现之前，开发者们总是面临非常艰难的抉择，究竟是使用执行速度快但是编译速度并不理想的语言（如：C++），还是使用编译速度较快但执行效率不佳的语言（如：.NET、Java），或者说开发难度较低但执行速度一般的动态语言呢？显然，Go 语言在这 3 个条件之间做到了最佳的平衡：快速编译，高效执行，易于开发。\r\n\r\n## 1.2.3 Go 语言的发展目标\r\n\r\nGo 语言的主要目标是将静态语言的安全性和高效性与动态语言的易开发性进行有机结合，达到完美平衡，从而使编程变得更加有乐趣，而不是在艰难抉择中痛苦前行。\r\n\r\n因此，Go 语言是一门类型安全和内存安全的编程语言。虽然 Go 语言中仍有指针的存在，但并不允许进行指针运算。\r\n\r\nGo 语言的另一个目标是对于网络通信、并发和并行编程的极佳支持，从而更好地利用大量的分布式和多核的计算机，这一点对于谷歌内部的使用来说就非常重要了。设计者通过 goroutine 这种轻量级线程的概念来实现这个目标，然后通过 channel 来实现各个 goroutine 之间的通信。他们实现了分段栈增长和 goroutine 在线程基础上多路复用技术的自动化。\r\n\r\n这个特性显然是 Go 语言最强有力的部分，不仅支持了日益重要的多核与多处理器计算机，也弥补了现存编程语言在这方面所存在的不足。\r\n\r\nGo 语言中另一个非常重要的特性就是它的构建速度（编译和链接到机器代码的速度），一般情况下构建一个程序的时间只需要数百毫秒到几秒。作为大量使用 C++ 来构建基础设施的谷歌来说，无疑从根本上摆脱了 C++ 在构建速度上非常不理想的噩梦。这不仅极大地提升了开发者的生产力，同时也使得软件开发过程中的代码测试环节更加紧凑，而不必浪费大量的时间在等待程序的构建上。\r\n\r\n依赖管理是现今软件开发的一个重要组成部分，但是 C 语言中“头文件”的概念却导致越来越多因为依赖关系而使得构建一个大型的项目需要长达几个小时的时间。人们越来越需要一门具有严格的、简洁的依赖关系分析系统从而能够快速编译的编程语言。这正是 Go 语言采用包模型的根本原因，这个模型通过严格的依赖关系检查机制来加快程序构建的速度，提供了非常好的可量测性。\r\n\r\n整个 Go 语言标准库的编译时间一般都在 20 秒以内，其它的常规项目也只需要半秒钟的时间来完成编译工作。这种闪电般的编译速度甚至比编译 C 语言或者 Fortran 更加快，使得编译这一环节不再成为在软件开发中困扰开发人员的问题。在这之前，动态语言将快速编译作为自身的一大亮点，像 C++ 那样的静态语言一般都有非常漫长的编译和链接工作。而同样作为静态语言的 Go 语言，通过自身优良的构建机制，成功地去除了这个弊端，使得程序的构建过程变得微不足道，拥有了像脚本语言和动态语言那样的高效开发的能力。\r\n\r\n另外，Go 语言在执行速度方面也可以与 C/C++ 相提并论。\r\n\r\n由于内存问题（通常称为内存泄漏）长期以来一直伴随着 C++ 的开发者们，Go 语言的设计者们认为内存管理不应该是开发人员所需要考虑的问题。因此尽管 Go 语言像其它静态语言一样执行本地代码，但它依旧运行在某种意义上的虚拟机，以此来实现高效快速的垃圾回收（使用了一个简单的标记-清除算法）。\r\n\r\n尽管垃圾回收并不容易实现，但考虑这将是未来并发应用程序发展的一个重要组成部分，Go 语言的设计者们还是完成了这项艰难的任务。\r\n\r\nGo 语言还能够在运行时进行反射相关的操作。\r\n\r\n使用 `go install` 能够很轻松地对第三方包进行部署。\r\n\r\n此外，Go 语言还支持调用由 C 语言编写的海量库文件（[第 3.9 节](03.9.md)），从而能够将过去开发的软件进行快速迁移。\r\n\r\n## 1.2.4 指导设计原则\r\n\r\nGo语言通过减少关键字的数量（25 个）来简化编码过程中的混乱和复杂度。干净、整齐和简洁的语法也能够提高程序的编译速度，因为这些关键字在编译过程中少到甚至不需要符号表来协助解析。\r\n\r\n这些方面的工作都是为了减少编码的工作量，甚至可以与 Java 的简化程度相比较。\r\n\r\nGo 语言有一种极简抽象艺术家的感觉，因为它只提供了一到两种方法来解决某个问题，这使得开发者们的代码都非常容易阅读和理解。众所周知，代码的可读性是软件工程里最重要的一部分（ **译者注：代码是写给人看的，不是写给机器看的** ）。\r\n\r\n这些设计理念没有建立其它概念之上，所以并不会因为牵扯到一些概念而将某个概念复杂化，他们之间是相互独立的。\r\n\r\nGo 语言有一套完整的编码规范，你可以在 [Go 语言编码规范](http://golang.org/doc/go_spec.html) 页面进行查看。\r\n\r\n它不像 Ruby 那样通过实现过程来定义编码规范。作为一门具有明确编码规范的语言，它要求可以采用不同的编译器如 gc 和 gccgo（[第 2.1 节](02.1.md)）进行编译工作，这对语言本身拥有更好的编码规范起到很大帮助。\r\n\r\n[LALR](http://en.wikipedia.org/wiki/LALR_parser) 是 Go 语言的语法标准，你也可以在 [`src/cmd/internal/gc/go.y`](https://github.com/golang/go/blob/master/src%2Fcmd%2Finternal%2Fgc%2Fgo.y) 中查看到，这种语法标准在编译时不需要符号表来协助解析。\r\n\r\n## 1.2.5 语言的特性\r\n\r\nGo 语言从本质上（程序和结构方面）来实现并发编程。\r\n\r\n因为 Go 语言没有类和继承的概念，所以它和 Java 或 C++ 看起来并不相同。但是它通过接口 (interface) 的概念来实现多态性。Go 语言有一个清晰易懂的轻量级类型系统，在类型之间也没有层级之说。因此可以说这是一门混合型的语言。\r\n\r\n在传统的面向对象语言中，使用面向对象编程技术显得非常臃肿，它们总是通过复杂的模式来构建庞大的类型层级，这违背了编程语言应该提升生产力的宗旨。\r\n\r\n函数是 Go 语言中的基本构件，它们的使用方法非常灵活。在[第六章](06.0.md)，我们会看到 Go 语言在函数式编程方面的基本概念。\r\n\r\n\r\nGo 语言使用静态类型，所以它是类型安全的一门语言，加上通过构建到本地代码，程序的执行速度也非常快。\r\n\r\n作为强类型语言，隐式的类型转换是不被允许的，记住一条原则：让所有的东西都是显式的。\r\n\r\nGo 语言其实也有一些动态语言的特性（通过关键字 `var`），所以它对那些逃离 Java 和 .Net 世界而使用 Python、Ruby、PHP 和 JavaScript 的开发者们也具有很大的吸引力。\r\n\r\nGo 语言支持交叉编译，比如说你可以在运行 Linux 系统的计算机上开发运行 Windows 下运行的应用程序。这是第一门完全支持 UTF-8 的编程语言，这不仅体现在它可以处理使用 UTF-8 编码的字符串，就连它的源码文件格式都是使用的 UTF-8 编码。Go 语言做到了真正的国际化！\r\n\r\n## 1.2.6 语言的用途\r\n\r\nGo 语言被设计成一门应用于搭载 Web 服务器，存储集群或类似用途的巨型中央服务器的系统编程语言。对于高性能分布式系统领域而言，Go 语言无疑比大多数其它语言有着更高的开发效率。它提供了海量并行的支持，这对于游戏服务端的开发而言是再好不过了。\r\n\r\nGo 语言一个非常好的目标就是实现所谓的复杂事件处理（[CEP](http://en.wikipedia.org/wiki/Complex_event_processing)），这项技术要求海量并行支持，高度的抽象化和高性能。当我们进入到物联网时代，CEP 必然会成为人们关注的焦点。\r\n\r\n但是 Go 语言同时也是一门可以用于实现一般目标的语言，例如对于文本的处理，前端展现，甚至像使用脚本一样使用它。\r\n\r\n值得注意的是，因为垃圾回收和自动内存分配的原因，Go 语言不适合用来开发对实时性要求很高的软件。\r\n\r\n越来越多的谷歌内部的大型分布式应用程序都开始使用 Go 语言来开发，例如谷歌地球的一部分代码就是由 Go 语言完成的。\r\n\r\n如果你想知道一些其它组织使用Go语言开发的实际应用项目，你可以到 [使用 Go 的组织](http://go-lang.cat-v.org/organizations-using-go) 页面进行查看。出于隐私保护的考虑，许多公司的项目都没有展示在这个页面。我们将会在[第 21 章](21.0.md) 讨论到一个使用 Go 语言开发的大型存储区域网络 (SAN) 案例。\r\n\r\n\r\n在 Chrome 浏览器中内置了一款 Go 语言的编译器用于本地客户端 (NaCl)，这很可能会被用于在 Chrome OS 中执行 Go 语言开发的应用程序。\r\n\r\nGo 语言可以在 Intel 或 ARM 处理器上运行，因此它也可以在安卓系统下运行，例如 Nexus 系列的产品。\r\n\r\n在 Google App Engine 中使用 Go 语言：2011 年 5 月 5 日，官方发布了用于开发运行在 Google App Engine 上的 Web 应用的 Go SDK，在此之前，开发者们只能选择使用 Python 或者 Java。这主要是 David Symonds 和 Nigel Tao 努力的成果。目前最新的稳定版是基于 Go 1.4 的 SDK 1.9.18，于 2015 年 2 月 18 日发布。当前 Go 语言的稳定版本是 Go 1.4.2。\r\n\r\n## 1.2.7 关于特性缺失\r\n\r\n许多能够在大多数面向对象语言中使用的特性 Go 语言都没有支持，但其中的一部分可能会在未来被支持。\r\n\r\n- 为了简化设计，不支持函数重载和操作符重载\r\n- 为了避免在 C/C++ 开发中的一些 Bug 和混乱，不支持隐式转换\r\n- Go 语言通过另一种途径实现面向对象设计（第 [10](10.0.md)-[11](11.0.md) 章）来放弃类和类型的继承\r\n- 尽管在接口的使用方面（[第 11 章](11.0.md)）可以实现类似变体类型的功能，但本身不支持变体类型\r\n- 不支持动态加载代码\r\n- 不支持动态链接库\r\n- 不支持泛型\r\n- 通过 `recover()` 和 `panic()` 来替代异常机制（第 [13.2](13.2.md)-[13.3](13.3.md) 节）\r\n- 不支持静态变量\r\n\r\n关于 Go 语言开发团队对于这些方面的讨论，你可以通过 [Go 常见问题](http://golang.org/doc/go_faq.html) 页面查看。\r\n\r\n## 1.2.8 使用 Go 语言编程\r\n\r\n如果你有其它语言的编程经历（面向对象编程语言，如：Java、C#、Object-C、Python、Ruby），在你进入到 Go 语言的世界之后，你将会像迷恋你的 X 语言一样无法自拔。Go 语言使用了与其它语言不同的设计模式，所以当你尝试将你的X语言的代码迁移到 Go 语言时，你将会非常失望，所以你需要从头开始，用 Go 的理念来思考。\r\n\r\n如果你在至高点使用 Go 的理念来重新审视和分析一个问题，你通常会找到一个适用于 Go 语言的优雅的解决方案。\r\n\r\n## 1.2.9 小结\r\n\r\n这里列举一些 Go 语言的必杀技：\r\n\r\n- 简化问题，易于学习\r\n- 内存管理，简洁语法，易于使用\r\n- 快速编译，高效开发\r\n- 高效执行\r\n- 并发支持，轻松驾驭\r\n- 静态类型\r\n- 标准类库，规范统一\r\n- 易于部署\r\n- 文档全面\r\n- 免费开源\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[起源与发展](01.1.md)\r\n- 下一章：[安装与运行环境](02.1.md)\r\n"
  },
  {
    "path": "eBook/02.1.md",
    "content": "# 2.1 平台与架构\r\n\r\nGo 语言开发团队开发了适用于以下操作系统的编译器：\r\n\r\n- Linux\r\n- FreeBSD\r\n- Mac OS X（也称为 Darwin）\r\n\r\n目前有2个版本的编译器：Go 原生编译器 gc 和非原生编译器 gccgo，这两款编译器都是在类 Unix 系统下工作 。其中，gc 版本的编译器已经被移植到 Windows 平台上，并集成在主要发行版中，你也可以通过安装 MinGW 从而在 Windows 平台下使用 gcc 编译器。这两个编译器都是以单通道的形式工作。\r\n\r\n你可以获取以下平台上的 Go 1.4 源码和二进制文件：\r\n\r\n- Linux 2.6+：amd64、386 和 arm 架构\r\n- Mac OS X (Snow Leopard + Lion) ：amd64 和 386 架构\r\n- Windows 2000+：amd64 和 386 架构\r\n\r\n对于非常底层的纯 Go 语言代码或者包而言，在各个操作系统平台上的可移植性是非常强的，只需要将源码拷贝到相应平台上进行编译即可，或者可以使用交叉编译来构建目标平台的应用程序（[第 2.2 节](02.2.md)）。但如果你打算使用 cgo 或者类似文件监控系统的软件，就需要根据实际情况进行相应地修改了。\r\n\r\n1. Go 原生编译器 gc：\r\n\r\n\t主要基于 Ken Thompson 先前在 Plan 9 操作系统上使用的 C 工具链。\r\n\r\n    Go 语言的编译器和链接器都是使用 C 语言编写并产生本地代码，Go 不存在自我引导之类的功能。因此如果使用一个有不同指令集的编译器来构建 Go 程序，就需要针对操作系统和处理器架构（32 位操作系统或 64 位操作系统）进行区别对待。（ **译者注：Go从1.5版本开始已经实现自举。Go语言的编译器和链接器都是Go语言编写的**）\r\n\r\n\t这款编译器使用非分代、无压缩和并行的方式进行编译，它的编译速度要比 gccgo 更快，产生更好的本地代码，但编译后的程序不能够使用 gcc 进行链接。\r\n\r\n\t编译器目前支持以下基于 Intel 或 AMD 处理器架构的程序构建。\r\n\r\n\t![](images/2.1.gc.jpg?raw=true)\r\n\r\n\t<center>图2.1 gc 编译器支持的处理器架构</center>\r\n\r\n\t当你第一次看到这套命名系统的时候你会觉得很奇葩，不过这些命名都是来自于 Plan 9 项目。\r\n\r\n\t\tg = 编译器：将源代码编译为项目代码（程序文本）\r\n\t\tl = 链接器：将项目代码链接到可执行的二进制文件（机器代码）\r\n\r\n\t（相关的 C 编译器名称为 6c、8c 和 5c，相关的汇编器名称为 6a、8a 和 5a）\r\n\r\n\t**标记 (Flags)** 是指可以通过命令行设置可选参数来影响编译器或链接器的构建过程或得到一个特殊的目标结果。\r\n\r\n\t可用的编译器标记如下：\r\n\r\n\t\tflags:\r\n\t\t-I 针对包的目录搜索\r\n\t\t-d 打印声明信息\r\n\t\t-e 不限制错误打印的个数\r\n\t\t-f 打印栈结构\r\n\t\t-h 发生错误时进入恐慌（panic）状态\r\n\t\t-o 指定输出文件名 // 详见第3.4节\r\n\t\t-S 打印产生的汇编代码\r\n\t\t-V 打印编译器版本 // 详见第2.3节\r\n\t\t-u 禁止使用 unsafe 包中的代码\r\n\t\t-w 打印归类后的语法解析树\r\n\t\t-x 打印 lex tokens\r\n\r\n\t从 Go 1.0.3 版本开始，不再使用 `8g`，`8l` 之类的指令进行程序的构建，取而代之的是统一的 `go build` 和 `go install` 等命令，而这些指令会自动调用相关的编译器或链接器。\r\n\r\n\r\n\t如果你想获得更深层次的信息，你可以在目录 [`$GOROOT/src/cmd`](https://github.com/golang/go/tree/master/src/cmd) 下找到编译器和链接器的源代码。Go 语言本身是由 C 语言开发的，而不是 Go 语言（Go 1.5 开始自举）。词法分析程序是 GNU bison，语法分析程序是名为 [`$GOROOT/src/cmd/gc/go.y`](https://github.com/golang/go/blob/master/src%2Fcmd%2Finternal%2Fgc%2Fgo.y) 的 yacc 文件，它会在同一目录输出 `y.tab.{c,h}` 文件。如果你想知道更多有关构建过程的信息，你可以在 [`$GOROOT/src/make.bash`](https://github.com/golang/go/blob/master/src/make.bash) 中找到。\r\n\r\n\t大部分的目录都包含了名为 `doc.go` 的文件，这个文件提供了更多详细的信息。\r\n\r\n2. gccgo 编译器：\r\n\r\n\t一款相对于 gc 而言更加传统的编译器，使用 GCC 作为后端。GCC 是一款非常流行的 GNU 编译器，它能够构建基于众多处理器架构的应用程序。编译速度相对 gc 较慢，但产生的本地代码运行要稍微快一点。它同时也提供一些与 C 语言之间的互操作性。\r\n\r\n\t从 Go 1 版本开始，gc 和 gccgo 在编译方面都有等价的功能。\r\n\r\n3. 文件扩展名与包 (package)：\r\n\r\n\tGo 语言源文件的扩展名很显然就是 `.go`。\r\n\r\n\tC 文件使用后缀名 `.c`，汇编文件使用后缀名 `.s`。所有的源代码文件都是通过包 (packages) 来组织。包含可执行代码的包文件在被压缩后使用扩展名 `.a`（AR 文档）。\r\n\r\n\tGo 语言的标准库（[第 9.1 节](09.1.md)）包文件在被安装后就是使用这种格式的文件。\r\n\r\n\t**注意** 当你在创建目录时，文件夹名称永远不应该包含空格，而应该使用下划线 \"_\" 或者其它一般符号代替。\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一章：[语言的主要特性与发展的环境和影响因素](01.2.md)\r\n- 下一节：[Go 环境变量](02.2.md)\r\n"
  },
  {
    "path": "eBook/02.2.md",
    "content": "# 2.2 Go 环境变量\n\nGo 开发环境依赖于一些操作系统环境变量，你最好在安装 Go 之前就已经设置好他们。如果你使用的是 Windows 的话，你完全不用进行手动设置，Go 将被默认安装在目录 `c:/go` 下。这里列举几个最为重要的环境变量：\n\n- **$GOROOT** 表示 Go 在你的电脑上的安装位置，它的值一般都是 `$HOME/go`，当然，你也可以安装在别的地方。\n- **$GOARCH** 表示目标机器的处理器架构，它的值可以是 386、amd64 或 arm。\n- **$GOOS** 表示目标机器的操作系统，它的值可以是 darwin、freebsd、linux 或 windows。\n- **$GOBIN** 表示编译器和链接器的安装位置，默认是 `$GOROOT/bin`，如果你使用的是 Go 1.0.3 及以后的版本，一般情况下你可以将它的值设置为空，Go 将会使用前面提到的默认值。\n\n目标机器是指你打算运行你的 Go 应用程序的机器。\n\nGo 编译器支持交叉编译，也就是说你可以在一台机器上构建能够在不同操作系统和处理器架构上运行的应用程序，也就是说编写源代码的机器可以和目标机器有完全不同的特性（操作系统与处理器架构）。\n\n为了区分本地机器和目标机器，你可以使用 `$GOHOSTOS` 和 `$GOHOSTARCH` 设置本地机器的操作系统名称和编译体系结构，这两个变量只有在进行交叉编译的时候才会用到，如果你不进行显示设置，他们的值会和本地机器（`$GOOS` 和 `$GOARCH`）一样。\n\n- **$GOPATH** 默认采用和 `$GOROOT` 一样的值，但从 Go 1.1 版本开始，你必须修改为其它路径。它可以包含多个 Go 语言源码文件、包文件和可执行文件的路径，而这些路径下又必须分别包含三个规定的目录：`src`、`pkg` 和 `bin`，这三个目录分别用于存放源码文件、包文件和可执行文件。\n- **$GOARM** 专门针对基于 arm 架构的处理器，它的值可以是 5 或 6，默认为 6。\n- **$GOMAXPROCS** 用于设置应用程序可使用的处理器个数与核数，详见 [第 14.1.3 节](14.1.md)。\n\n在接下来的章节中，我们将会讨论如何在 Linux、Mac OS X 和 Windows 上安装 Go 语言。在 FreeBSD 上的安装和 Linux 非常类似。开发团队正在尝试将 Go 语言移植到其它例如 OpenBSD、DragonFlyBSD、NetBSD、Plan 9、Haiku 和 Solaris 操作系统上，你可以在这个页面找到最近的动态：[Go Porting Efforts](http://go-lang.cat-v.org/os-ports)。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[平台与架构](02.1.md)\n- 下一节：[在 Linux 上安装 Go](02.3.md)\n"
  },
  {
    "path": "eBook/02.3.md",
    "content": "# 2.3 在 Linux 上安装 Go\n\n如果你能够自己下载并编译 Go 的源代码的话,对你来说是非常有教育意义的，你可以根据这个页面找到安装指南和下载地址：[Download the Go distribution](http://golang.org/doc/install)。\n\n我们接下来也会带你一步步地完成安装过程。\n\n1. 设置 Go 环境变量\n\n\t我们在 Linux 系统下一般通过文件 `$HOME/.bashrc` 配置自定义环境变量，根据不同的发行版也可能是文件 `$HOME/.profile`，然后使用 gedit 或 vi 来编辑文件内容。\n\n\t\texport GOROOT=$HOME/go\n\n\t为了确保相关文件在文件系统的任何地方都能被调用，你还需要添加以下内容：\n\n\t\texport PATH=$PATH:$GOROOT/bin\n\n\t在开发 Go 项目时，你还需要一个环境变量来保存你的工作目录。\n\n\t\texport GOPATH=$HOME/Applications/Go\n\n\t`$GOPATH` 可以包含多个工作目录，取决于你的个人情况。如果你设置了多个工作目录，那么当你在之后使用 `go get`（远程包安装命令）时远程包将会被安装在第一个目录下。\n\n\t在完成这些设置后，你需要在终端输入指令 `source .bashrc` 以使这些环境变量生效。然后重启终端，输入 `go env` 和 `env` 来检查环境变量是否设置正确。\n\n2. 安装 C 工具\n\n\tGo 的工具链是用 C 语言编写的，因此在安装 Go 之前你需要先安装相关的 C 工具。如果你使用的是 Ubuntu 的话，你可以在终端输入以下指令（ **译者注：由于网络环境的特殊性，你可能需要将每个工具分开安装** ）。\n\n\t\tsudo apt-get install bison ed gawk gcc libc6-dev make\n\n\t你可以在其它发行版上使用 RPM 之类的工具。\n\n3. 获取 Go 源代码\n\n\t从 [官方页面](https://golang.org/dl/) 或 [国内镜像](http://www.golangtc.com/download) 下载 Go 的源码包到你的计算机上，然后将解压后的目录 `go` 通过命令移动到 `$GOROOT` 所指向的位置。\n\n\t\twget https://storage.googleapis.com/golang/go<VERSION>.src.tar.gz\n\t\ttar -zxvf go<VERSION>.src.tar.gz\n\t\tsudo mv go $GOROOT\n\n4. 构建 Go\n\n\t在终端使用以下指令来进行编译工作。\n\n\t\tcd $GOROOT/src\n\t\t./all.bash\n\t\n\t**编译注意事项**\n\t\n\t编译时如果出现如下报错：\n\t\n\t![](images/2.3.allbasherror.png?raw=true)\n\t\n\t可能是因为 `$GOROOT_BOOTSTRAP` 变量没有设置。这个目录在安装 Go 1.5 版本及之后的版本时需要设置。\n\t\n\t由于在 1.4 版本后，Go 编译器实现了自举，即通过 1.4 版本来编译安装之后版本的编译器。如果不设置该环境变量的话，会产生这样一个错误 `Set $GOROOT_BOOTSTRAP to a working Go tree >= Go 1.4.` 。\n\t\n\t设置 `$GOROOT_BOOTSTRAP` 变量：\n\t    \n\t    export GOROOT_BOOTSTRAP=$HOME/go1.4\n\t\n\t设置完成后，下载 1.4 版本的源码到该目录：\n\t\n\t    git clone https://github.com/golang/go.git $HOME/go1.4\n\t    git checkout -b release-branch.go1.4 origin/release-branch.go1.4\n\t\n\t进入 1.4 的文件夹后，进行编译：\n\t\n\t    cd $HOME/go1.4/src\n\t    ./make.bash\n\t\n\t1.4 编译安装好之后，进入 `$GOROOT` 文件夹，真正开始编译安装 Go：\n\t\n\t    cd $HOME/go/src\n\t    ./all.bash\n\t\n\t在完成编译之后（通常在 1 分钟以内，如果你在 B 型树莓派上编译，一般需要 1 个小时），你会在终端看到如下信息被打印：\n\n\t![](images/2.3.allbash.png?raw=true)\n\n\t<center>图 2.3 完成编译后在终端打印的信息</center>\n\n\t**注意事项** \n\n\t在测试 `net/http` 包时有一个测试会尝试连接 `google.com`，你可能会看到如下所示的一个无厘头的错误报告：\n\n\t\t‘make[2]: Leaving directory `/localusr/go/src/pkg/net’\n\n\t如果你正在使用一个带有防火墙的机器，我建议你可以在编译过程中暂时关闭防火墙，以避免不必要的错误。\n\n\t解决这个问题的另一个办法是通过设置环境变量 `$DISABLE_NET_TESTS` 来告诉构建工具忽略 `net/http` 包的相关测试：\n\n\t\texport DISABLE_NET_TESTS=1\n\n\t如果你完全不想运行包的测试，你可以直接运行 `./make.bash` 来进行单纯的构建过程。\n\n5. 测试安装\n\n\t使用你最喜爱的编辑器来输入以下内容，并保存为文件名 `hello_world1.go`。\n\n\t示例 2.1 [hello_world1.go](examples/chapter_2/hello_world1.go)\n\n\t```go\n\tpackage main\n\t\n\tfunc main() {\n\t\tprintln(\"Hello\", \"world\")\n\t}\n\t```\n\n\t切换相关目录到下，然后执行指令 `go run hello_world1.go`，将会打印信息：`Hello, world`。\n\n6. 验证安装版本\n\n\t你可以通过在终端输入指令 `go version` 来打印 Go 的版本信息。\n\n\t如果你想要通过 Go 代码在运行时检测版本，可以通过以下例子实现。\n\n\t示例 2.2 [version.go](examples/chapter_2/version.go)\n\n\t```go\n\tpackage main\n\n\timport (\n\t\t\"fmt\"\n\t\t\"runtime\"\n\t)\n\n\tfunc main() {\n\t\tfmt.Printf(\"%s\", runtime.Version())\n\t}\n\t```\n\n\t这段代码将会输出 `go1.4.2` 或类似字符串。\n\n7. 更新版本\n\n\t你可以在 [发布历史](http://golang.org/doc/devel/release.html) 页面查看到最新的稳定版。\n\n\t当前最新的稳定版 Go 1 系列于 2012 年 3 月 28 日发布。\n\n\tGo 的源代码有以下三个分支：\n\n\t\t- Go release：最新稳定版，实际开发最佳选择\n\t\t- Go weekly：包含最近更新的版本，一般每周更新一次\n\t\t- Go tip：永远保持最新的版本，相当于内测版\n\n\t当你在使用不同的版本时，注意官方博客发布的信息，因为你所查阅的文档可能和你正在使用的版本不相符。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[Go 环境变量](02.2.md)\n- 下一节：[在 Mac OS X 上安装 Go](02.4.md)\n"
  },
  {
    "path": "eBook/02.4.md",
    "content": "# 2.4 在 Mac OS X 上安装 Go\n\n如果你想要在你的 Mac 系统上安装 Go，则必须使用 Intel 64 位处理器，Go 不支持 PowerPC 处理器。\n\n你可以通过该页面查看有关在 PowerPC 处理器上的移植进度：[https://codedr-go-ppc.googlecode.com/hg/](https://codedr-go-ppc.googlecode.com/hg/)。\n\n**注意事项**\n\n在 Mac 系统下使用到的 C 工具链是 Xcode 的一部分，因此你需要通过安装 Xcode 来完成这些工具的安装。你并不需要安装完整的 Xcode，而只需要安装它的命令行工具部分。\n\n你可以在 [下载页面](http://golang.org/dl/) 页面下载到 Mac 系统下的一键安装包或源代码自行编译。\n\n通过源代码编译安装的过程与环境变量的配置与在 Linux 系统非常相似，因此不再赘述。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[在 Linux 上安装 Go](02.3.md)\n- 下一节：[在 Windows 上安装 Go](02.5.md)\n"
  },
  {
    "path": "eBook/02.5.md",
    "content": "# 2.5 在 Windows 上安装 Go\n\n你可以在 [下载页面](http://golang.org/dl/) 页面下载到 Windows 系统下的一键安装包。\n\n前期的 Windows 移植工作由 Hector Chu 完成，但目前的发行版已经由 Joe Poirier 全职维护。\n\n在完成安装包的安装之后，你只需要配置 `$GOPATH` 这一个环境变量就可以开始使用 Go 语言进行开发了，其它的环境变量安装包均会进行自动设置。在默认情况下，Go 将会被安装在目录 `c:\\go` 下，但如果你在安装过程中修改安装目录，则可能需要手动修改所有的环境变量的值。\n\n如果你想要测试安装，则可以使用指令 `go run` 运行 [hello_world1.go](examples/chapter_2/hello_world1.go)。\n\n如果发生错误 `fatal error: can’t find import: fmt` 则说明你的环境变量没有配置正确。\n\n如果你想要在 Windows 下使用 cgo （调用 C 语言写的代码），则需要安装 [MinGW](http://sourceforge.net/projects/mingw/files/Automated%20MinGW%20Installer/)，一般推荐安装 [TDM-GCC](http://tdm-gcc.tdragon.net/)。如果你使用的是 64 位操作系统，请务必安装 64 位版本的 MinGW。安装完成进行环境变量等相关配置即可使用。\n\n**在 Windows 下运行在虚拟机里的 Linux 系统上安装 Go**：\n\n如果你想要在 Windows 下的虚拟机里的 Linux 系统上安装 Go，你可以选择使用虚拟机软件 [VMware](http://www.vmware.com)，下载 [VMware player](http://www.vmware.com/products/player/)，搜索并下载一个你喜欢的 Linux 发行版镜像，然后安装到虚拟机里，安装 Go 的流程参考第 2.3 节中的内容。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[在 Mac OS X 上安装 Go](02.4.md)\n- 下一节：[安装目录清单](02.6.md)\n"
  },
  {
    "path": "eBook/02.6.md",
    "content": "# 2.6 安装目录清单\n\n你的 Go 安装目录 (`$GOROOT`) 的文件夹结构应该如下所示：\n\nREADME.md, AUTHORS, CONTRIBUTORS, LICENSE\n\n- `/bin`：包含可执行文件，如：编译器，Go 工具\n- `/doc`：包含文档模版\n- `/lib`：包含示例程序，代码工具，本地文档等\n- `/misc`：包含与支持 Go 编辑器有关的配置文件以及 cgo 的示例\n- `/os_arch`：包含标准库的包的对象文件 (`.a`)\n- `/src`：包含源代码构建脚本和标准库的包的完整源代码（Go 是一门开源语言）\n- `/src/cmd`：包含 Go 和 C 的编译器和命令行脚本\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[在 Windows 上安装 Go](02.5.md)\n- 下一节：[Go 运行时 (runtime)](02.7.md)\n"
  },
  {
    "path": "eBook/02.7.md",
    "content": "# 2.7 Go 运行时 (runtime)\n\n尽管 Go 编译器产生的是本地可执行代码，这些代码仍旧运行在 Go 的 runtime（这部分的代码可以在 `runtime` 包中找到）当中。这个 runtime 类似 Java 和 .NET 语言所用到的虚拟机，它负责管理包括内存分配、垃圾回收（[第 10.8 节](10.8.md)）、栈处理、goroutine、channel、切片 (slice)、map 和反射 (reflection) 等等。\n\nruntime 主要由 C 语言编写（Go 1.5 开始自举），并且是每个 Go 包的最顶级包。你可以在目录 [`$GOROOT/src/runtime`](https://github.com/golang/go/tree/master/src/runtime) 中找到相关内容。\n\n**垃圾回收器** Go 拥有简单却高效的标记-清除回收器。它的主要思想来源于 IBM 的可复用垃圾回收器，旨在打造一个高效、低延迟的并发回收器。目前 gccgo 还没有回收器，同时适用 gc 和 gccgo 的新回收器正在研发中。使用一门具有垃圾回收功能的编程语言不代表你可以避免内存分配所带来的问题，分配和回收内容都是消耗 CPU 资源的一种行为。\n\nGo 的可执行文件都比相对应的源代码文件要大很多，这恰恰说明了 Go 的 runtime 嵌入到了每一个可执行文件当中。当然，在部署到数量巨大的集群时，较大的文件体积也是比较头疼的问题。但总的来说，Go 的部署工作还是要比 Java 和 Python 轻松得多。因为 Go 不需要依赖任何其它文件，它只需要一个单独的静态文件，这样你也不会像使用其它语言一样在各种不同版本的依赖文件之间混淆。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[安装目录清单](02.6.md)\n- 下一节：[Go 解释器](02.8.md)\n"
  },
  {
    "path": "eBook/02.8.md",
    "content": "# 2.8 Go 解释器\n\n因为 Go 具有像动态语言那样快速编译的能力，自然而然地就有人会问 Go 语言能否在 REPL (read-eval-print loop) 编程环境下实现。Sebastien Binet 已经使用这种环境实现了一个 Go 解释器，你可以在这个页面找到：[https://github.com/sbinet/igo](https://github.com/sbinet/igo)。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[Go 运行时 (runtime)](02.7.md)\n- 下一章：[编辑器、集成开发环境与其它工具](03.0.md)\n"
  },
  {
    "path": "eBook/03.0.md",
    "content": "# 3.0 编辑器、集成开发环境与其它工具\n\n因为 Go 语言还是一门相对年轻的编程语言，所以不管是在集成开发环境 (IDE) 还是相关的插件方面，发展都不是很成熟。不过目前还是有一些 IDE 能够较好地支持 Go 的开发，有些开发工具甚至是跨平台的，你可以在 Linux、Mac OS X 或者 Windows 下工作。\n\n你可以通过查阅 [编辑器和 IDE 扩展](http://go-lang.cat-v.org/text-editors/) 页面来获取 Go 开发工具的最新信息。\n\n## 链接\n\n- [目录](directory.md)\n- 上一章：[Go 解释器](02.8.md)\n- 下一节：[Go 开发环境的基本要求](03.1.md)\n"
  },
  {
    "path": "eBook/03.1.md",
    "content": "# 3.1 Go 开发环境的基本要求\n\n这里有一个可以用来开发 Go 的集成开发环境，你期待有以下哪些特性，从而替代你使用文本编辑器写代码和命令行编译与链接程序的方式？\n\n1. 语法高亮是必不可少的功能，这也是每个开发工具都提供配置文件来实现自定义配置的原因。\n2. 可以自动保存代码，至少在每次编译前都会保存。\n3. 可以显示代码所在的行数。\n4. 拥有较好的项目文件纵览和导航能力，可以同时编辑多个源文件并设置书签，能够匹配括号，能够跳转到某个函数或类型的定义部分。\n5. 完美的查找和替换功能，替换之前最好还能预览结果。\n6. 可以注释或取消注释选中的一行或多行代码。\n7. 当有编译错误时，双击错误提示可以跳转到发生错误的位置。\n8. 跨平台，能够在 Linux、Mac OS X 和 Windows 下工作，这样就可以专注于一个开发环境。\n9. 最好是免费的，不过有些开发者还是希望能够通过支付一定金额以获得更好的开发环境。\n10. 最好是开源的。\n11. 能够通过插件架构来轻易扩展和替换某个功能。\n12. 尽管集成开发环境本身就是非常复杂的，但一定要让人感觉操作方便。\n13. 能够通过代码模版来简化编码过程从而提升编码速度。\n14. 使用 Go 项目的概念来浏览和管理项目中的文件，同时还要拥有构建系统的概念，这样才能更加方便的构建、清理或运行我们建立的程序或项目。构建出的程序需要能够通过命令行或 IDE 内部的控制台运行。\n15. 拥有断点、检查变量值、单步执行、逐过程执行标识库中代码的能力。\n16. 能够方便的存取最近使用过的文件或项目。\n17. 拥有对包、类型、变量、函数和方法的智能代码补全的功能。\n18. 能够对项目或包中的代码建立抽象语法树视图 (AST-view)。\n19. 内置 Go 的相关工具。\n20. 能够方便完整地查阅 Go 文档。\n21. 能够方便地在不同的 Go 环境之间切换。\n22. 能够导出不同格式的代码文件，如：PDF，HTML 或格式化后的代码。\n23. 针对一些特定的项目有项目模板，如：Web 应用，App Engine 项目，从而能够更快地开始开发工作。\n24. 具备代码重构的能力。\n25. 集成像 hg 或 git 这样的版本控制工具。\n26. 集成 Google App Engine 开发及调试的功能。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[编辑器、集成开发环境与其它工具](03.0.md)\n- 下一节：[编辑器和集成开发环境](03.2.md)\n"
  },
  {
    "path": "eBook/03.2.md",
    "content": "# 3.2 编辑器和集成开发环境\n\n这些编辑器包含了代码高亮和其它与 Go 有关的一些使用工具：Emacs、Vim、Xcode 6、KD Kate、TextWrangler、BBEdit、McEdit、TextMate、TextPad、JEdit、SciTE、Nano、Notepad++、Geany、SlickEdit、Visual Studio Code、IntelliJ IDEA 和 Sublime Text 2。\n\n你可以将 Linux 的文本编辑器 GEdit 改造成一个很好的 Go 开发工具。\n\n**[Sublime Text](http://www.sublimetext.com)** 是一个革命性的跨平台 (Linux、Mac OS X、Windows)文本编辑器，它支持编写非常多的编程语言代码。对于 Go 而言，它有一个插件叫做 [GoSublime](https://github.com/DisposaBoy/GoSublime) 来支持代码补全和代码模版。\n\n这里还有一些更加高级的 Go 开发工具，其中一些是以插件的形式利用本身是作为开发 Java 的工具。\n\n**[IntelliJ Idea Plugin](https://github.com/go-lang-plugin-org/go-lang-idea-plugin)** 是一个 IntelliJ IDEA 的插件，具有很好的操作体验和代码补全功能。\n\n**[LiteIDE](https://github.com/visualfc/liteide)** 这是一款专门针对 Go 开发的集成开发环境，在编辑、编译和运行 Go 程序和项目方面都有非常好的支持。同时还包括了对源代码的抽象语法树视图和一些内置工具（此开发环境由国人 vfc 大叔开发）。\n\n**[GoClipse](https://github.com/GoClipse/goclipse)** 是一款 Eclipse IDE 的插件，拥有非常多的特性以及通过 GoCode 来实现代码补全功能。\n\n如果你对集成开发环境都不是很熟悉，那就使用 LiteIDE 吧，另外使用 GoClipse 或者 IntelliJ Idea Plugin 也是不错的选择。\n\n**代码补全** 一般都是通过内置 GoCode 实现的（如：LieteIDE、GoClipse），如果需要手动安装 GoCode，在命令行输入指令 `go get -u github.com/nsf/gocode` 即可（务必事先配置好 Go 环境变量）\n。\n\n接下来会对这三个集成开发环境做更加详细的说明。\n\n## 3.2.1 LiteIDE\n\n这款 IDE 的当前最新版本号为 X27，你可以从 [GitHub](https://github.com/visualfc/liteide) 页面获取详情。\n\nLiteIDE 是一款非常好用的轻量级 Go 集成开发环境（基于 QT、Kate 和 SciTE），包含了跨平台开发及其它必要的特性，对代码编写、自动补全和运行调试都有极佳的支持。它采用了 Go 项目的概念来对项目文件进行浏览和管理，它还支持在各个 Go 开发环境之间随意切换以及交叉编译的功能。\n\n同时，它具备了抽象语法树视图的功能，可以清楚地纵览项目中的常量、变量、函数、不同类型以及他们的属性和方法。\n\n![](images/3.2.liteide.jpg?raw=true)\n\n图 3.1 LiteIDE 代码编辑界面和抽象语法树视图\n\n## 3.2.2 GoClipse\n\n该款插件的当前最新版本号为 0.9.1，你可以从 [GitHub](https://github.com/GoClipse/goclipse) 页面获取详情。\n\n其依附于著名的 Eclipse 这个大型开发环境，虽然需要安装 JVM 运行环境，但却可以很容易地享有 Eclipse 本身所具有的诸多功能。这是一个非常好的编辑器，完善的代码补全、抽象语法树视图、项目管理和程序调试功能。\n\n![](images/3.2.goclipse.jpg?raw=true)\n\n图 3.2 GoClipse 代码编辑界面、抽象语法树视图和项目管理\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[Go 开发环境的基本要求](03.1.md)\n- 下一节：[调试器](03.3.md)\n"
  },
  {
    "path": "eBook/03.3.md",
    "content": "# 3.3 调试器\n\n应用程序的开发过程中调试是必不可少的一个环节，因此有一个好的调试器是非常重要的，可惜的是，Go 在这方面的发展还不是很完善。目前可用的调试器是 gdb，最新版均以内置在集成开发环境 LiteIDE 和 GoClipse 中，但是该调试器的调试方式并不灵活且操作难度较大。\n\n如果你不想使用调试器，你可以按照下面的一些有用的方法来达到基本调试的目的：\n\n1. 在合适的位置使用打印语句输出相关变量的值（`print`/`println` 和 `fmt.Print`/`fmt.Println`/`fmt.Printf`）。\n2. 在 `fmt.Printf` 中使用下面的说明符来打印有关变量的相关信息：\n\n\t- `%+v` 打印包括字段在内的实例的完整信息\n\t- `%#v` 打印包括字段和限定类型名称在内的实例的完整信息\n\t- `%T` 打印某个类型的完整说明\n\n3. 使用 `panic()` 语句（[第 13.2 节](13.2.md)）来获取栈跟踪信息（直到 `panic()` 时所有被调用函数的列表）。\n4. 使用关键字 `defer` 来跟踪代码执行过程（[第 6.4 节](06.4.md)）。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[编辑器和集成开发环境](03.2.md)\n- 下一节：[构建并运行 Go 程序](03.4.md)\n"
  },
  {
    "path": "eBook/03.4.md",
    "content": "# 3.4 构建并运行 Go 程序\n\n在大多数 IDE 中，每次构建程序之前都会自动调用源码格式化工具 `gofmt` 并保存格式化后的源文件。如果构建成功则不会输出任何信息，而当发生编译时错误时，则会指明源码中具体第几行出现了什么错误，如：`a declared and not used`。一般情况下，你可以双击 IDE 中的错误信息直接跳转到发生错误的那一行。\n\n如果程序执行一切顺利并成功退出后，将会在控制台输出 `Program exited with code 0`。\n\n从 Go 1 版本开始，使用 Go 自带的更加方便的工具来构建应用程序：\n\n- `go build` 编译自身包和依赖包\n- `go install` 编译并安装自身包和依赖包\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[调试器](03.3.md)\n- 下一节：[格式化代码](03.5.md)\n"
  },
  {
    "path": "eBook/03.5.md",
    "content": "# 3.5 格式化代码\n\nGo 开发团队不想要 Go 语言像许多其它语言那样总是在为代码风格而引发无休止的争论，浪费大量宝贵的开发时间，因此他们制作了一个工具：`go fmt` (gofmt)。这个工具可以将你的源代码格式化成符合官方统一标准的风格，属于语法风格层面上的小型重构。遵循统一的代码风格是 Go 开发中无可撼动的铁律，因此你必须在编译或提交版本管理系统之前使用 gofmt 来格式化你的代码。\n\n尽管这种做法也存在一些争论，但使用 gofmt 后你不再需要自成一套代码风格而是和所有人使用相同的规则。这不仅增强了代码的可读性，而且在接手外部 Go 项目时，可以更快地了解其代码的含义。此外，大多数开发工具也都内置了这一功能。\n\nGo 对于代码的缩进层级方面使用 tab 还是空格并没有强制规定，一个 tab 可以代表 4 个或 8 个空格。在实际开发中，1 个 tab 应该代表 4 个空格，而在本身的例子当中，每个 tab 代表 8 个空格。至于开发工具方面，一般都是直接使用 tab 而不替换成空格。\n\n在命令行输入 `gofmt –w program.go` 会格式化该源文件的代码然后将格式化后的代码覆盖原始内容（如果不加参数 `-w` 则只会打印格式化后的结果而不重写文件）；`gofmt -w *.go` 会格式化并重写所有 Go 源文件；`gofmt map1` 会格式化并重写 `map1` 目录及其子目录下的所有 Go 源文件。\n\n`gofmt` 也可以通过在参数 `-r` 后面加入用双引号括起来的替换规则实现代码的简单重构，规则的格式：`<原始内容> -> <替换内容>`。\n\n实例：\n\n\tgofmt -r '(a) -> a' –w *.go\n\n上面的代码会将源文件中没有意义的括号去掉。\n\n\tgofmt -r 'a[n:len(a)] -> a[n:]' –w *.go\n\n上面的代码会将源文件中多余的 `len(a)` 去掉。（ **译者注：了解切片 (slice) 之后就明白这为什么是多余的了** ）\n\n\tgofmt –r 'A.Func1(a,b) -> A.Func2(b,a)' –w *.go\n\n上面的代码会将源文件中符合条件的函数的参数调换位置。\n\n如果想要了解有关 `gofmt` 的更多信息，请访问该页面：[http://golang.org/cmd/gofmt/](http://golang.org/cmd/gofmt/)。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[构建并运行 Go 程序](03.4.md)\n- 下一节：[生成代码文档](03.6.md)\n"
  },
  {
    "path": "eBook/03.6.md",
    "content": "# 3.6 生成代码文档\n\ngodoc 工具会从 Go 程序和包文件中提取顶级声明的首行注释以及每个对象的相关注释，并生成相关文档。\n\n它也可以作为一个提供在线文档浏览的 web 服务器，[http://golang.org](http://golang.org) 就是通过这种形式实现的。\n\n**一般用法**\n\n- `go doc package` 获取包的文档注释，例如：`go doc fmt` 会显示使用 godoc 生成的 `fmt` 包的文档注释。\n- `go doc package/subpackage` 获取子包的文档注释，例如：`go doc container/list`。\n- `go doc package function` 获取某个函数在某个包中的文档注释，例如：`go doc fmt Printf` 会显示有关 `fmt.Printf()` 的使用说明。\n\n这个工具只能获取在 Go 安装目录下 `../go/src` 中的注释内容。此外，它还可以作为一个本地文档浏览 web 服务器。在命令行输入 `godoc -http=:6060`，然后使用浏览器打开 [http://localhost:6060](http://localhost:6060) 后，你就可以看到本地文档浏览服务器提供的页面。\n\ngodoc 也可以用于生成非标准库的 Go 源码文件的文档注释（[第 9.6 章](09.6.md)）。\n\n如果想要获取更多有关 `godoc` 的信息，请访问该页面：[http://golang.org/cmd/godoc/](http://golang.org/cmd/godoc/)（在线版的第三方包 godoc 可以使用 [Go Walker](https://gowalker.org)）。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[格式化代码](03.5.md)\n- 下一节：[其它工具](03.7.md)\n"
  },
  {
    "path": "eBook/03.7.md",
    "content": "# 3.7 其它工具\n\nGo 自带的工具集主要使用脚本和 Go 语言自身编写的，目前版本的 Go 实现了以下三个工具：\n\n- `go install` 是安装 Go 包的工具，类似 Ruby 中的 rubygems。主要用于安装非标准库的包文件，将源代码编译成对象文件。\n- `go fix` 用于将你的 Go 代码从旧的发行版迁移到最新的发行版，它主要负责简单的、重复的、枯燥无味的修改工作，如果像 API 等复杂的函数修改，工具则会给出文件名和代码行数的提示以便让开发人员快速定位并升级代码。Go 开发团队一般也使用这个工具升级 Go 内置工具以及 谷歌内部项目的代码。`go fix` 之所以能够正常工作是因为 Go 在标准库就提供生成抽象语法树和通过抽象语法树对代码进行还原的功能。该工具会尝试更新当前目录下的所有 Go 源文件，并在完成代码更新后在控制台输出相关的文件名称。\n- `go test` 是一个轻量级的单元测试框架（[第 13 章](13.0.md)）。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[生成代码文档](03.6.md)\n- 下一节：[Go 性能说明](03.8.md)\n"
  },
  {
    "path": "eBook/03.8.md",
    "content": "# 3.8 Go 性能说明\n\n根据 Go 开发团队和基本的算法测试，Go 语言与 C 语言的性能差距大概在 10%~20% 之间（ **译者注：由于出版时间限制，该数据应为 2013 年 3 月 28 日之前产生** ）。虽然没有官方的性能标准，但是与其它各个语言相比已经拥有非常出色的表现。\n\n如果说 Go 语言的执行效率大约比 C++ 慢 20% 也许更有实际意义。保守估计在相同的环境和执行目标的情况下，Go 程序比 Java 或 Scala 应用程序要快上 2 倍，并比这两门语言占用的内存降低了 70% 。在很多情况下这种比较是没有意义的，而像谷歌这样拥有成千上万台服务器的公司都抛弃 C++ 而开始将 Go 用于生产环境才足够说明它本身所具有的优势。\n\n时下流行的语言大都是运行在虚拟机上，如：Java 和 Scala 使用的 JVM，C# 和 VB.NET 使用的 .NET CLR。尽管虚拟机的性能已经有了很大的提升，但任何使用 JIT 编译器和脚本语言解释器的编程语言（Ruby、Python、Perl 和 JavaScript）在 C 和 C++ 的绝对优势下甚至都无法在性能上望其项背。\n\n如果说 Go 比 C++ 要慢 20%，那么 Go 就要比任何非静态和编译型语言快 2 到 10 倍，并且能够更加高效地使用内存。\n\n其实比较多门语言之间的性能是一种非常猥琐的行为，因为任何一种语言都有其所擅长和薄弱的方面。例如在处理文本方面，那些只处理纯字节的语言显然要比处理 Unicode 这种更为复杂编码的语言要出色的多。有些人可能认为使用两种不同的语言实现同一个目标能够得出正确的结论，但是很多时候测试者可能对一门语言非常了解而对另一门语言只是大概明白，测试者对程序编写的手法在一定程度也会影响结果的公平性，因此测试程序应该分别由各自语言的擅长者来编写，这样才能得到具有可比性的结果。另外，像在统计学方面，人们很难避免人为因素对结果的影响，所以这在严格意义上并不是科学。还要注意的是，测试结果的可比性还要根据测试目标来区别，例如很多发展十多年的语言已经针对各类问题拥有非常成熟的类库，而作为一门新生语言的 Go 语言，并没有足够的时间来推导各类问题的最佳解决方案。如果你想获取更多有关性能的资料，请访问 [Computer Language Benchmark Game](http://shootout.alioth.debian.org/)（详见引用 27）。\n\n这里有一些评测结果：\n\n- 比较 Go 和 Python 在简单的 web 服务器方面的性能，单位为传输量每秒：\n\t\n\t原生的 Go http 包要比 web.py 快 7 至 8 倍，如果使用 web.go 框架则稍微差点，比 web.py 快 6 至 7 倍。在 Python 中被广泛使用的 tornado 异步服务器和框架在 web 环境下要比 web.py 快很多，Go 大概只比它快 1.2 至 1.5 倍（详见引用 26）。\n\n- Go 和 Python 在一般开发的平均水平测试中，Go 要比 Python 3 快 25 倍左右，少占用三分之二的内存，但比 Python 大概多写一倍的代码（详见引用 27）。\n- 根据 Robert Hundt（2011 年 6 月，详见引用 28）的文章对 C++、Java、Go 和 Scala，以及 Go 开发团队的反应（详见引用 29），可以得出以下结论：\n\t\n\t- Go 和 Scala 之间具有更多的可比性（都使用更少的代码），而 C++ 和 Java 都使用非常冗长的代码。\n\t- Go 的编译速度要比绝大多数语言都要快，比 Java 和 C++ 快 5 至 6 倍，比 Scala 快 10 倍。\t\n\t- Go 的二进制文件体积是最大的（每个可执行文件都包含 runtime）。\t\n\t- 在最理想的情况下，Go 能够和 C++ 一样快，比 Scala 快 2 至 3 倍，比 Java 快 5 至 10 倍。\t\n\t- Go 在内存管理方面也可以和 C++ 相媲美，几乎只需要 Scala 所使用的一半，是Java的五分之一左右。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[其它工具](03.7.md)\n- 下一节：[与其它语言进行交互](03.9.md)\n"
  },
  {
    "path": "eBook/03.9.md",
    "content": "# 3.9 与其它语言进行交互\n\n## 3.9.1 与 C 进行交互\n\n工具 cgo 提供了对 FFI（外部函数接口）的支持，能够使用 Go 代码安全地调用 C 语言库，你可以访问 cgo 文档主页：[http://golang.org/cmd/cgo](http://golang.org/cmd/cgo)。cgo 会替代 Go 编译器来产生可以组合在同一个包中的 Go 和 C 代码。在实际开发中一般使用 cgo 创建单独的 C 代码包。\n\n如果你想要在你的 Go 程序中使用 cgo，则必须在单独的一行使用 `import \"C\"` 来导入，一般来说你可能还需要 `import \"unsafe\"`。\n\n然后，你可以在 `import \"C\"` 之前使用注释（单行或多行注释均可）的形式导入 C 语言库（甚至有效的 C 语言代码），它们之间没有空行，例如：\n\n\t// #include <stdio.h>\n\t// #include <stdlib.h>\n\timport \"C\"\n\n名称 \"C\" 并不属于标准库的一部分，这只是 cgo 集成的一个特殊名称用于引用 C 的命名空间。在这个命名空间里所包含的 C 类型都可以被使用，例如 `C.uint`、`C.long` 等等，还有 libc 中的函数 `C.random()` 等也可以被调用。\n\n当你想要使用某个类型作为 C 中函数的参数时，必须将其转换为 C 中的类型，反之亦然，例如：\n\n```go\nvar i int\nC.uint(i) \t\t// 从 Go 中的 int 转换为 C 中的无符号 int\nint(C.random()) // 从 C 中 random() 函数返回的 long 转换为 Go 中的 int\n```\n\n下面的 2 个 Go 函数 `Random()` 和 `Seed()` 分别调用了 C 中的 `C.random()` 和 `C.srandom()`。\n\n示例 3.2 [c1.go](examples/chapter_3/CandGo/c1.go)\n\n```go\npackage rand\n\n// #include <stdlib.h>\nimport \"C\"\n\nfunc Random() int {\n\treturn int(C.random())\n}\n\nfunc Seed(i int) {\n\tC.srandom(C.uint(i))\n}\n```\n\nC 当中并没有明确的字符串类型，如果你想要将一个 `string` 类型的变量从 Go 转换到 C 时，可以使用 `C.CString(s)`；同样，可以使用 `C.GoString(cs)` 从 C 转换到 Go 中的 `string` 类型。\n\nGo 的内存管理机制无法管理通过 C 代码分配的内存。\n\n开发人员需要通过手动调用 `C.free` 来释放变量的内存：\n\n```go\ndefer C.free(unsafe.Pointer(Cvariable))\n```\n\n这一行最好紧跟在使用 C 代码创建某个变量之后，这样就不会忘记释放内存了。下面的代码展示了如何使用 cgo 创建变量、使用并释放其内存：\n\n示例 3.3 [c2.go](examples/chapter_3/CandGo/c2.go)\n\n```go\npackage print\n\n// #include <stdio.h>\n// #include <stdlib.h>\nimport \"C\"\nimport \"unsafe\"\n\nfunc Print(s string) {\n\tcs := C.CString(s)\n\tdefer C.free(unsafe.Pointer(cs))\n\tC.fputs(cs, (*C.FILE)(C.stdout))\n}\n```\n\n**构建 cgo 包**\n\n你可以在使用将会在第 9.5 节讲到的 Makefile 文件（因为我们使用了一个独立的包），除了使用变量 `GOFILES` 之外，还需要使用变量 `CGOFILES` 来列出需要使用 cgo 编译的文件列表。例如，示例 3.2 中的代码就可以使用包含以下内容的 `Makefile` 文件来编译，你可以使用 `gomake` 或 `make`：\n\n\tinclude $(GOROOT)/src/Make.inc\n\tTARG=rand\n\tCGOFILES=\\\n\tc1.go\\\n\tinclude $(GOROOT)/src/Make.pkg\n\n# 3.9.2 与 C++ 进行交互\n\nSWIG（简化封装器和接口生成器）支持在 Linux 系统下使用 Go 代码调用 C 或者 C++ 代码。这里有一些使用 SWIG 的注意事项：\n\n- 编写需要封装的库的 SWIG 接口。\n- SWIG 会产生 C 的存根函数。\n- 这些库可以使用 cgo 来调用。\n- 相关的 Go 文件也可以被自动生成。\n\n这类接口支持方法重载、多重继承以及使用 Go 代码实现 C++ 的抽象类。\n\n目前使用 SWIG 存在的一个问题是它无法支持所有的 C++ 库，比如说它就无法解析 TObject.h。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[Go 性能说明](03.8.md)\n- 下一部分：[语言的核心结构与技术](04.1.md)\n"
  },
  {
    "path": "eBook/04.1.md",
    "content": "# 4.1 文件名、关键字与标识符\r\n\r\nGo 的源文件以 `.go` 为后缀名存储在计算机中，这些文件名均由小写字母组成，如 `scanner.go` 。如果文件名由多个部分组成，则使用下划线 `_` 对它们进行分隔，如 `scanner_test.go` 。文件名不包含空格或其他特殊字符。\r\n\r\n一个源文件可以包含任意多行的代码，Go 本身没有对源文件的大小进行限制。\r\n\r\n你会发现在 Go 代码中的几乎所有东西都有一个名称或标识符。另外，Go 语言也是区分大小写的，这与 C 家族中的其它语言相同。有效的标识符必须以字母（可以使用任何 UTF-8 编码的字符或 `_`）开头，然后紧跟着 0 个或多个字符或 Unicode 数字，如：X56、group1、_x23、i、өԑ12。\r\n\r\n以下是无效的标识符： \r\n\r\n- 1ab（以数字开头）\r\n- case（Go 语言的关键字）\r\n- a+b（运算符是不允许的）\r\n\r\n`_` 本身就是一个特殊的标识符，被称为空白标识符。它可以像其他标识符那样用于变量的声明或赋值（任何类型都可以赋值给它），但任何赋给这个标识符的值都将被抛弃，因此这些值不能在后续的代码中使用，也不可以使用这个标识符作为变量对其它变量进行赋值或运算。\r\n\r\n在编码过程中，你可能会遇到没有名称的变量、类型或方法。虽然这不是必须的，但有时候这样做可以极大地增强代码的灵活性，这些变量被统称为匿名变量。\r\n\r\n下面列举了 Go 代码中会使用到的 25 个关键字或保留字：\r\n\r\n<table class=\"table table-bordered table-striped table-condensed\">\r\n  <tr>\r\n    <td>break</td>\r\n    <td>default</td>\r\n    <td>func</td>\r\n    <td>interface</td>\r\n    <td>select</td>\r\n  </tr>\r\n  <tr>\r\n    <td>case</td>\r\n    <td>defer</td>\r\n    <td>go</td>\r\n    <td>map</td>\r\n    <td>struct</td>\r\n  </tr>\r\n  <tr>\r\n    <td>chan</td>\r\n    <td>else</td>\r\n    <td>goto</td>\r\n    <td>package</td>\r\n    <td>switch</td>\r\n  </tr>\r\n  <tr>\r\n    <td>const</td>\r\n    <td>fallthrough</td>\r\n    <td>if</td>\r\n    <td>range</td>\r\n    <td>type</td>\r\n  </tr>\r\n  <tr>\r\n    <td>continue</td>\r\n    <td>for</td>\r\n    <td>import</td>\r\n    <td>return</td>\r\n    <td>var</td>\r\n  </tr>\r\n</table>\r\n\r\n之所以刻意地将 Go 代码中的关键字保持的这么少，是为了简化在编译过程第一步中的代码解析。和其它语言一样，关键字不能够作标识符使用。\r\n\r\n除了以上介绍的这些关键字，Go 语言还有 36 个预定义标识符，其中包含了基本类型的名称和一些基本的内置函数（[第 6.5 节](06.5.md)），它们的作用都将在接下来的章节中进行进一步地讲解。\r\n\r\n<table class=\"table table-bordered table-striped table-condensed\">\r\n  <tr>\r\n    <td>append</td>\r\n    <td>bool</td>\r\n    <td>byte</td>\r\n    <td>cap</td>\r\n    <td>close</td>\r\n    <td>complex</td>\r\n    <td>complex64</td>\r\n    <td>complex128</td>\r\n    <td>uint16</td>\r\n  </tr>\r\n  <tr>\r\n    <td>copy</td>\r\n    <td>false</td>\r\n    <td>float32</td>\r\n    <td>float64</td>\r\n    <td>imag</td>\r\n    <td>int</td>\r\n    <td>int8</td>\r\n    <td>int16</td>\r\n    <td>uint32</td>\r\n  </tr>\r\n  <tr>\r\n    <td>int32</td>\r\n    <td>int64</td>\r\n    <td>iota</td>\r\n    <td>len</td>\r\n    <td>make</td>\r\n    <td>new</td>\r\n    <td>nil</td>\r\n    <td>panic</td>\r\n    <td>uint64</td>\r\n  </tr>\r\n  <tr>\r\n    <td>print</td>\r\n    <td>println</td>\r\n    <td>real</td>\r\n    <td>recover</td>\r\n    <td>string</td>\r\n    <td>true</td>\r\n    <td>uint</td>\r\n    <td>uint8</td>\r\n    <td>uintptr</td>\r\n  </tr>\r\n</table>\r\n\r\n程序一般由关键字、常量、变量、运算符、类型和函数组成。\r\n\r\n程序中可能会使用到这些分隔符：括号 `()`，中括号 `[]` 和大括号 `{}`。\r\n\r\n程序中可能会使用到这些标点符号：`.`、`,`、`;`、`:` 和 `…`。\r\n\r\n程序的代码通过语句来实现结构化。每个语句不需要像 C 家族中的其它语言一样以分号 `;` 结尾，因为这些工作都将由 Go 编译器自动完成。\r\n\r\n如果你打算将多个语句写在同一行，它们则必须使用 `;` 人为区分，但在实际开发中我们并不鼓励这种做法。\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一部分：[与其它语言进行交互](03.9.md)\r\n- 下一节：[Go 程序的基本结构和要素](04.2.md)\r\n"
  },
  {
    "path": "eBook/04.2.md",
    "content": "# 4.2 Go 程序的基本结构和要素\r\n\r\n示例 4.1 [hello_world.go](examples/chapter_4/hello_world.go)\r\n\r\n```go\r\npackage main\r\n\r\nimport \"fmt\"\r\n\r\nfunc main() {\r\n\tfmt.Println(\"hello, world\")\r\n}\r\n```\r\n\r\n## 4.2.1 包的概念、导入与可见性\r\n\r\n包是结构化代码的一种方式：每个程序都由包（通常简称为 pkg）的概念组成，可以使用自身的包或者从其它包中导入内容。\r\n\r\n如同其它一些编程语言中的类库或命名空间的概念，每个 Go 文件都属于且仅属于一个包。一个包可以由许多以 `.go` 为扩展名的源文件组成，因此文件名和包名一般来说都是不相同的。\r\n\r\n你必须在源文件中非注释的第一行指明这个文件属于哪个包，如：`package main`。`package main` 表示一个可独立执行的程序，每个 Go 应用程序都包含一个名为 `main` 的包。\r\n\r\n一个应用程序可以包含不同的包，而且即使你只使用 main 包也不必把所有的代码都写在一个巨大的文件里：你可以用一些较小的文件，并且在每个文件非注释的第一行都使用 `package main` 来指明这些文件都属于 `main` 包。如果你打算编译包名不是为 main 的源文件，如 `pack1`，编译后产生的对象文件将会是 `pack1.a` 而不是可执行程序。另外要注意的是，所有的包名都应该使用小写字母。\r\n\r\n**标准库**\r\n\r\n在 Go 的安装文件里包含了一些可以直接使用的包，即标准库。在 Windows 下，标准库的位置在 Go 根目录下的子目录 `pkg\\windows_386` 中；在 Linux 下，标准库在 Go 根目录下的子目录 `pkg\\linux_amd64` 中（如果是安装的是 32 位，则在 `linux_386` 目录中）。一般情况下，标准包会存放在 `$GOROOT/pkg/$GOOS_$GOARCH/` 目录下。\r\n\r\nGo 的标准库包含了大量的包（如：`fmt` 和 `os`），但是你也可以创建自己的包（[第 9 章](./09.0.md)）。\r\n\r\n如果想要构建一个程序，则包和包内的文件都必须以正确的顺序进行编译。包的依赖关系决定了其构建顺序。\r\n\r\n属于同一个包的源文件必须全部被一起编译，一个包即是编译时的一个单元，因此根据惯例，每个目录都只包含一个包。\r\n\r\n**如果对一个包进行更改或重新编译，所有引用了这个包的客户端程序都必须全部重新编译。**\r\n\r\nGo 中的包模型采用了显式依赖关系的机制来达到快速编译的目的，编译器会从后缀名为 `.o` 的对象文件（需要且只需要这个文件）中提取传递依赖类型的信息。\r\n\r\n如果 `A.go` 依赖 `B.go`，而 `B.go` 又依赖 `C.go`：\r\n\r\n- 编译 `C.go`, `B.go`, 然后是 `A.go`.\r\n- 为了编译 `A.go`, 编译器读取的是 `B.o` 而不是 `C.o`.\r\n\r\n这种机制对于编译大型的项目时可以显著地提升编译速度。\r\n\r\n**每一段代码只会被编译一次**\r\n\r\n一个 Go 程序是通过 `import` 关键字将一组包链接在一起。\r\n\r\n`import \"fmt\"` 告诉 Go 编译器这个程序需要使用 `fmt` 包（的函数，或其他元素），`fmt` 包实现了格式化 IO（输入/输出）的函数。包名被封闭在半角双引号 `\"\"` 中。如果你打算从已编译的包中导入并加载公开声明的方法，不需要插入已编译包的源代码。\r\n\r\n如果需要多个包，它们可以被分别导入：\r\n\r\n```go\r\nimport \"fmt\"\r\nimport \"os\"\r\n```\r\n\r\n或：\r\n\r\n```go\r\nimport \"fmt\"; import \"os\"\r\n```\r\n\r\n但是还有更短且更优雅的方法（被称为因式分解关键字，该方法同样适用于 `const`、`var` 和 `type` 的声明或定义）：\r\n\r\n```go\r\nimport (\r\n   \"fmt\"\r\n   \"os\"\r\n)\r\n```\r\n\r\n它甚至还可以更短的形式，但使用 gofmt 后将会被强制换行：\r\n\r\n```go\r\nimport (\"fmt\"; \"os\")\r\n```\r\n\r\n当你导入多个包时，最好按照字母顺序排列包名，这样做更加清晰易读。\r\n\r\n如果包名不是以 `.` 或 `/` 开头，如 `\"fmt\"` 或者 `\"container/list\"`，则 Go 会在全局文件进行查找；如果包名以 `./` 开头，则 Go 会在相对目录中查找；如果包名以 `/` 开头（在 Windows 下也可以这样使用），则会在系统的绝对路径中查找。\r\n\r\n*译者注：以相对路径在GOPATH下导入包会产生报错信息*\r\n\r\n*报错信息：local import \"./XXX\" in non-local package*\r\n\r\n*引用：[Go programs cannot use relative import paths within a work space.](https://golang.org/cmd/go/#hdr-Relative_import_paths )*\r\n\r\n*注解：在GOPATH外可以以相对路径的形式执行go build（go install 不可以）*\r\n\r\n导入包即等同于包含了这个包的所有的代码对象。\r\n\r\n除了符号 `_`，包中所有代码对象的标识符必须是唯一的，以避免名称冲突。但是相同的标识符可以在不同的包中使用，因为可以使用包名来区分它们。\r\n\r\n包通过下面这个被编译器强制执行的规则来决定是否将自身的代码对象暴露给外部文件：\r\n\r\n**可见性规则**\r\n\r\n当标识符（包括常量、变量、类型、函数名、结构字段等等）以一个大写字母开头，如：Group1，那么使用这种形式的标识符的对象就可以被外部包的代码所使用（客户端程序需要先导入这个包），这被称为导出（像面向对象语言中的 public）；标识符如果以小写字母开头，则对包外是不可见的，但是它们在整个包的内部是可见并且可用的（像面向对象语言中的 private ）。\r\n\r\n（大写字母可以使用任何 Unicode 编码的字符，比如希腊文，不仅仅是 ASCII 码中的大写字母）。\r\n\r\n因此，在导入一个外部包后，能够且只能够访问该包中导出的对象。\r\n\r\n假设在包 `pack1` 中我们有一个变量或函数叫做 `Thing`（以 T 开头，所以它能够被导出），那么在当前包中导入 `pack1` 包，`Thing` 就可以像面向对象语言那样使用点标记来调用：`pack1.Thing`（pack1 在这里是不可以省略的）。\r\n\r\n因此包也可以作为命名空间使用，帮助避免命名冲突（名称冲突）：两个包中的同名变量的区别在于它们的包名，例如 `pack1.Thing` 和 `pack2.Thing`。\r\n\r\n你可以通过使用包的别名来解决包名之间的名称冲突，或者说根据你的个人喜好对包名进行重新设置，如：`import fm \"fmt\"`。下面的代码展示了如何使用包的别名：\r\n\r\n示例 4.2 [alias.go](examples/chapter_4/alias.go)\r\n\r\n```go\r\npackage main\r\n\r\nimport fm \"fmt\" // alias3\r\n\r\nfunc main() {\r\n   fm.Println(\"hello, world\")\r\n}\r\n```\r\n\r\n**注意事项** \r\n\r\n如果你导入了一个包却没有使用它，则会在构建程序时引发错误，如 `imported and not used: os`，这正是遵循了 Go 的格言：“没有不必要的代码！”。\r\n\r\n**包的分级声明和初始化**\r\n\r\n你可以在使用 `import` 导入包之后定义或声明 0 个或多个常量 (const)、变量 (var) 和类型 (type)，这些对象的作用域都是全局的（在本包范围内），所以可以被本包中所有的函数调用（如 [gotemplate.go](examples/chapter_4/gotemplate.go) 源文件中的 `c` 和 `v`），然后声明一个或多个函数 (func)。\r\n\r\n## 4.2.2 函数\r\n\r\n这是定义一个函数最简单的格式：\r\n\r\n```go\r\nfunc functionName()\r\n```\r\n\r\n你可以在括号 `()` 中写入 0 个或多个函数的参数（使用逗号 `,` 分隔），每个参数的名称后面必须紧跟着该参数的类型。\r\n\r\n`main()` 函数是每一个可执行程序所必须包含的，一般来说都是在启动后第一个执行的函数（如果有 `init()` 函数则会先执行该函数）。如果你的 `main` 包的源代码没有包含 `main()` 函数，则会引发构建错误 `undefined: main.main`。`main()` 函数既没有参数，也没有返回类型（与 C 家族中的其它语言恰好相反）。如果你不小心为 `main()` 函数添加了参数或者返回类型，将会引发构建错误： \r\n\r\n\tfunc main must have no arguments and no return values results.\r\n\r\n在程序开始执行并完成初始化后，第一个调用（程序的入口点）的函数是 `main.main()`（如：C 语言），该函数一旦返回就表示程序已成功执行并立即退出。\r\n\r\n函数里的代码（函数体）使用大括号 `{}` 括起来。\r\n\r\n左大括号 `{` 必须与方法的声明放在同一行，这是编译器的强制规定，否则你在使用 gofmt 时就会出现错误提示：\r\n\r\n\t`build-error: syntax error: unexpected semicolon or newline before {`\r\n\r\n（这是因为编译器会产生 `func main() ;` 这样的结果，很明显这是错误的）\r\n\r\n**Go 语言虽然看起来不使用分号作为语句的结束，但实际上这一过程是由编译器自动完成，因此才会引发像上面这样的错误**\r\n\r\n右大括号 `}` 需要被放在紧接着函数体的下一行。如果你的函数非常简短，你也可以将它们放在同一行：\r\n\r\n```go\r\nfunc Sum(a, b int) int { return a + b }\r\n```\r\n\r\n对于大括号 `{}` 的使用规则在任何时候都是相同的（如：`if` 语句等）。\r\n\r\n因此符合规范的函数一般写成如下的形式：\r\n\r\n```go\r\nfunc functionName(parameter_list) (return_value_list) {\r\n   …\r\n}\r\n```\r\n\r\n其中：\r\n\r\n- `parameter_list` 的形式为 `(param1 type1, param2 type2, …)`\r\n- `return_value_list` 的形式为 `(ret1 type1, ret2 type2, …)`\r\n\r\n只有当某个函数需要被外部包调用的时候才使用大写字母开头，并遵循 Pascal 命名法；否则就遵循骆驼命名法，即第一个单词的首字母小写，其余单词的首字母大写。\r\n\r\n下面这一行调用了 `fmt` 包中的 `Println` 函数，可以将字符串输出到控制台，并在最后自动增加换行字符 `\\n`：\r\n\r\n```go\r\nfmt.Println（\"hello, world\"）\r\n```\r\n\r\n使用 `fmt.Print(\"hello, world\\n\")` 可以得到相同的结果。\r\n\r\n`Print` 和 `Println` 这两个函数也支持使用变量，如：`fmt.Println(arr)`。如果没有特别指定，它们会以默认的打印格式将变量 `arr` 输出到控制台。\r\n\r\n单纯地打印一个字符串或变量甚至可以使用预定义的方法来实现，如：`print`、`println：print(\"ABC\")`、`println(\"ABC\")`、`println(i)`（带一个变量 `i`）。\r\n\r\n这些函数只可以用于调试阶段，在部署程序的时候务必将它们替换成 `fmt` 中的相关函数。\r\n\r\n当被调用函数的代码执行到结束符 `}` 或返回语句时就会返回，然后程序继续执行调用该函数之后的代码。\r\n\r\n程序正常退出的代码为 `0` 即 `Program exited with code 0`；如果程序因为异常而被终止，则会返回非零值，如：`1`。这个数值可以用来测试是否成功执行一个程序。\r\n\r\n## 4.2.3 注释\r\n\r\n示例 4.2 [hello_world2.go](examples/chapter_4/hello_world2.go)\r\n\r\n```go\r\npackage main\r\n\r\nimport \"fmt\" // Package implementing formatted I/O.\r\n\r\nfunc main() {\r\n   fmt.Printf(\"Καλημέρα κόσμε; or こんにちは 世界\\n\")\r\n}\r\n```\r\n\r\n上面这个例子通过打印 `Καλημέρα κόσμε; or こんにちは 世界` 展示了如何在 Go 中使用国际化字符，以及如何使用注释。\r\n\r\n注释不会被编译，但可以通过 godoc 来使用（[第 3.6 节](03.6.md)）。\r\n\r\n单行注释是最常见的注释形式，你可以在任何地方使用以 `//` 开头的单行注释。多行注释也叫块注释，均已以 `/*` 开头，并以 `*/` 结尾，且不可以嵌套使用，多行注释一般用于包的文档描述或注释成块的代码片段。\r\n\r\n每一个包应该有相关注释，在 `package` 语句之前的块注释将被默认认为是这个包的文档说明，其中应该提供一些相关信息并对整体功能做简要的介绍。一个包可以分散在多个文件中，但是只需要在其中一个进行注释说明即可。当开发人员需要了解包的一些情况时，自然会用 godoc 来显示包的文档说明，在首行的简要注释之后可以用成段的注释来进行更详细的说明，而不必拥挤在一起。另外，在多段注释之间应以空行分隔加以区分。\r\n\r\n示例：\r\n\r\n```go\r\n// Package superman implements methods for saving the world.\r\n//\r\n// Experience has shown that a small number of procedures can prove\r\n// helpful when attempting to save the world.\r\npackage superman\r\n```\r\n\r\n几乎所有全局作用域的类型、常量、变量、函数和被导出的对象都应该有一个合理的注释。如果这种注释（称为文档注释）出现在函数前面，例如函数 Abcd，则要以 `\"Abcd...\"` 作为开头。\r\n\r\n示例：\r\n\r\n```go\r\n// enterOrbit causes Superman to fly into low Earth orbit, a position\r\n// that presents several possibilities for planet salvation.\r\nfunc enterOrbit() error {\r\n   ...\r\n}\r\n```\r\n\r\ngodoc 工具（[第 3.6 节](03.6.md)）会收集这些注释并产生一个技术文档。\r\n\r\n## 4.2.4 类型\r\n\r\n变量（或常量）包含数据，这些数据可以有不同的数据类型，简称类型。使用 `var` 声明的变量的值会自动初始化为该类型的零值。类型定义了某个变量的值的集合与可对其进行操作的集合。\r\n\r\n类型可以是基本类型，如：`int`、`float`、`bool`、`string`；结构化的（复合的），如：`struct`、`array`、切片 (slice)、`map`、通道 (channel)；只描述类型的行为的，如：`interface`。\r\n\r\n结构化的类型没有真正的值，它使用 `nil` 作为默认值（在 Objective-C 中是 nil，在 Java 中是 null，在 C 和 C++ 中是 NULL 或 0）。值得注意的是，Go 语言中不存在类型继承。\r\n\r\n函数也可以是一个确定的类型，就是以函数作为返回类型。这种类型的声明要写在函数名和可选的参数列表之后，例如：\r\n\r\n```go\r\nfunc FunctionName (a typea, b typeb) typeFunc\r\n```\r\n\r\n你可以在函数体中的某处返回使用类型为 `typeFunc` 的变量 `var`：\r\n\r\n```go\r\nreturn var\r\n```\r\n\r\n一个函数可以拥有多返回值，返回类型之间需要使用逗号分割，并使用小括号 `()` 将它们括起来，如：\r\n\r\n```go\r\nfunc FunctionName (a typea, b typeb) (t1 type1, t2 type2)\r\n```\r\n\r\n示例： 函数 `Atoi()`（[第 4.7 节](04.7.md)）：`func Atoi(s string) (i int, err error)`\r\n\r\n返回的形式：\r\n\r\n```go\r\nreturn var1, var2\r\n```\r\n\r\n这种多返回值一般用于判断某个函数是否执行成功 (true/false) 或与其它返回值一同返回错误消息（详见之后的并行赋值）。\r\n\r\n使用 `type` 关键字可以定义你自己的类型，你可能想要定义一个结构体（[第 10 章](10.0.md)），但是也可以定义一个已经存在的类型的别名，如：\r\n\r\n```go\r\ntype IZ int\r\n```\r\n\r\n**这里并不是真正意义上的别名，因为使用这种方法定义之后的类型可以拥有更多的特性，且在类型转换时必须显式转换。**\r\n\r\n然后我们可以使用下面的方式声明变量：\r\n\r\n```go\r\nvar a IZ = 5\r\n```\r\n\r\n这里我们可以看到 `int` 是变量 `a` 的底层类型，这也使得它们之间存在相互转换的可能（[第 4.2.6 节](04.2.md)）。\r\n\r\n如果你有多个类型需要定义，可以使用因式分解关键字的方式，例如：\r\n\r\n```go\r\ntype (\r\n   IZ int\r\n   FZ float64\r\n   STR string\r\n)\r\n```\r\n\r\n每个值都必须在经过编译后属于某个类型（编译器必须能够推断出所有值的类型），因为 Go 语言是一种静态类型语言。\r\n\r\n## 4.2.5 Go 程序的一般结构\r\n\r\n下面的程序可以被顺利编译但什么都做不了，不过这很好地展示了一个 Go 程序的首选结构。这种结构并没有被强制要求，编译器也不关心 `main()` 函数在前还是变量的声明在前，但使用统一的结构能够在从上至下阅读 Go 代码时有更好的体验。\r\n\r\n所有的结构将在这一章或接下来的章节中进一步地解释说明，但总体思路如下：\r\n\r\n- 在完成包的 `import` 之后，开始对常量、变量和类型的定义或声明。\r\n- 如果存在 `init()` 函数的话，则对该函数进行定义（这是一个特殊的函数，每个含有该函数的包都会首先执行这个函数）。\r\n- 如果当前包是 `main` 包，则定义 `main()` 函数。\r\n- 然后定义其余的函数，首先是类型的方法，接着是按照 `main()` 函数中先后调用的顺序来定义相关函数，如果有很多函数，则可以按照字母顺序来进行排序。\r\n\r\n示例 4.4 [gotemplate.go](examples/chapter_4/gotemplate.go)\r\n\r\n```go\r\npackage main\r\n\r\nimport (\r\n   \"fmt\"\r\n)\r\n\r\nconst c = \"C\"\r\n\r\nvar v int = 5\r\n\r\ntype T struct{}\r\n\r\nfunc init() { // initialization of package\r\n}\r\n\r\nfunc main() {\r\n   var a int\r\n   Func1()\r\n   // ...\r\n   fmt.Println(a)\r\n}\r\n\r\nfunc (t T) Method1() {\r\n   //...\r\n}\r\n\r\nfunc Func1() { // exported function Func1\r\n   //...\r\n}\r\n```\r\n\r\nGo 程序的执行（程序启动）顺序如下：\r\n\r\n1. 按顺序导入所有被 `main` 包引用的其它包，然后在每个包中执行如下流程：\r\n2. 如果该包又导入了其它的包，则从第一步开始递归执行，但是每个包只会被导入一次。\r\n3. 然后以相反的顺序在每个包中初始化常量和变量，如果该包含有 `init()` 函数的话，则调用该函数。\r\n4. 在完成这一切之后，`main` 也执行同样的过程，最后调用 `main()` 函数开始执行程序。\r\n\r\n## 4.2.6 类型转换\r\n\r\n在必要以及可行的情况下，一个类型的值可以被转换成另一种类型的值。由于 Go 语言不存在隐式类型转换，因此所有的转换都必须显式说明，就像调用一个函数一样（类型在这里的作用可以看作是一种函数）：\r\n\r\n```go\r\nvalueOfTypeB = typeB(valueOfTypeA)\r\n```\r\n\r\n**类型 B 的值 = 类型 B(类型 A 的值)**\r\n\r\n示例： \r\n\r\n```go\r\na := 5.0\r\nb := int(a)\r\n```\r\n\r\n但这只能在定义正确的情况下转换成功，例如从一个取值范围较小的类型转换到一个取值范围较大的类型（例如将 `int16` 转换为 `int32`）。当从一个取值范围较大的转换到取值范围较小的类型时（例如将 `int32` 转换为 `int16` 或将 `float32` 转换为 `int`），会发生精度丢失（截断）的情况。当编译器捕捉到非法的类型转换时会引发编译时错误，否则将引发运行时错误。\r\n\r\n具有相同底层类型的变量之间可以相互转换：\r\n\r\n```go\r\nvar a IZ = 5\r\nc := int(a)\r\nd := IZ(c)\r\n```\r\n\r\n## 4.2.7 Go 命名规范\r\n\r\n干净、可读的代码和简洁性是 Go 追求的主要目标。通过 gofmt 来强制实现统一的代码风格。Go 语言中对象的命名也应该是简洁且有意义的。像 Java 和 Python 中那样使用混合着大小写和下划线的冗长的名称会严重降低代码的可读性。名称不需要指出自己所属的包，因为在调用的时候会使用包名作为限定符。返回某个对象的函数或方法的名称一般都是使用名词，没有 `Get...` 之类的字符，如果是用于修改某个对象，则使用 `SetName()`。有必须要的话可以使用大小写混合的方式，如 `MixedCaps()` 或 `mixedCaps()`，而不是使用下划线来分割多个名称。\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[文件名、关键字与标识符](04.1.md)\r\n- 下一节：[常量](04.3.md)\r\n"
  },
  {
    "path": "eBook/04.3.md",
    "content": "# 4.3 常量\r\n\r\n常量使用关键字 `const` 定义，用于存储不会改变的数据。\r\n\r\n存储在常量中的数据类型只可以是布尔型、数字型（整数型、浮点型和复数）和字符串型。\r\n\r\n常量的定义格式：`const identifier [type] = value`，例如： \r\n\r\n```go\r\nconst Pi = 3.14159\r\n```\r\n\r\n在 Go 语言中，你可以省略类型说明符 `[type]`，因为编译器可以根据变量的值来推断其类型。\r\n\r\n- 显式类型定义： `const b string = \"abc\"`\r\n- 隐式类型定义： `const b = \"abc\"`\r\n\r\n一个没有指定类型的常量被使用时，会根据其使用环境而推断出它所需要具备的类型。换句话说，未定义类型的常量会在必要时刻根据上下文来获得相关类型。\r\n\r\n```go\r\nvar n int\r\nf(n + 5) // 无类型的数字型常量 “5” 它的类型在这里变成了 int\r\n```\r\n\r\n常量的值必须是能够在编译时就能够确定的；你可以在其赋值表达式中涉及计算过程，但是所有用于计算的值必须在编译期间就能获得。\r\n\r\n- 正确的做法：`const c1 = 2/3`  \r\n- 错误的做法：`const c2 = getNumber()` // 引发构建错误: `getNumber() used as value`\r\n\r\n**因为在编译期间自定义函数均属于未知，因此无法用于常量的赋值，但内置函数可以使用，如：`len()`。**\r\n\r\n数字型的常量是没有大小和符号的，并且可以使用任何精度而不会导致溢出：\r\n\r\n```go\r\nconst Ln2 = 0.693147180559945309417232121458\\\r\n\t\t\t176568075500134360255254120680009\r\nconst Log2E = 1/Ln2 // this is a precise reciprocal\r\nconst Billion = 1e9 // float constant\r\nconst hardEight = (1 << 100) >> 97\r\n```\r\n\r\n根据上面的例子我们可以看到，反斜杠 `\\` 可以在常量表达式中作为多行的连接符使用。\r\n\r\n与各种类型的数字型变量相比，你无需担心常量之间的类型转换问题，因为它们都是非常理想的数字。\r\n\r\n不过需要注意的是，当常量赋值给一个精度过小的数字型变量时，可能会因为无法正确表达常量所代表的数值而导致溢出，这会在编译期间就引发错误。另外，常量也允许使用并行赋值的形式：\r\n\r\n```go\r\nconst beef, two, c = \"eat\", 2, \"veg\"\r\nconst Monday, Tuesday, Wednesday, Thursday, Friday, Saturday = 1, 2, 3, 4, 5, 6\r\nconst (\r\n\tMonday, Tuesday, Wednesday = 1, 2, 3\r\n\tThursday, Friday, Saturday = 4, 5, 6\r\n)\r\n```\r\n\r\n常量还可以用作枚举：\r\n\r\n```go\r\nconst (\r\n\tUnknown = 0\r\n\tFemale = 1\r\n\tMale = 2\r\n)\r\n```\r\n\r\n现在，数字 `0`、`1` 和 `2` 分别代表未知性别、女性和男性。这些枚举值可以用于测试某个变量或常量的实际值，比如使用 switch/case 结构（[第 5.3 节](./05.3.md)）。\r\n\r\n在这个例子中，`iota` 可以被用作枚举值：\r\n\r\n```go\r\nconst (\r\n\ta = iota\r\n\tb = iota\r\n\tc = iota\r\n)\r\n```\r\n\r\n第一个 `iota` 等于 0，每当 `iota` 在新的一行被使用时，它的值都会自动加 1，并且没有赋值的常量默认会应用上一行的赋值表达式：\r\n\r\n```go\r\n// 赋值一个常量时，之后没赋值的常量都会应用上一行的赋值表达式\r\nconst (\r\n\ta = iota  // a = 0\r\n\tb         // b = 1\r\n\tc         // c = 2\r\n\td = 5     // d = 5   \r\n\te         // e = 5\r\n)\r\n\r\n// 赋值两个常量，iota 只会增长一次，而不会因为使用了两次就增长两次\r\nconst (\r\n\tApple, Banana = iota + 1, iota + 2 // Apple=1 Banana=2\r\n\tCherimoya, Durian                  // Cherimoya=2 Durian=3\r\n\tElderberry, Fig                    // Elderberry=3, Fig=4\r\n\r\n)\r\n\r\n// 使用 iota 结合 位运算 表示资源状态的使用案例\r\nconst (\r\n\tOpen = 1 << iota  // 0001\r\n\tClose             // 0010\r\n\tPending           // 0100\r\n)\r\n\r\nconst (\r\n\t_           = iota             // 使用 _ 忽略不需要的 iota\r\n\tKB = 1 << (10 * iota)          // 1 << (10*1)\r\n\tMB                             // 1 << (10*2)\r\n\tGB                             // 1 << (10*3)\r\n\tTB                             // 1 << (10*4)\r\n\tPB                             // 1 << (10*5)\r\n\tEB                             // 1 << (10*6)\r\n\tZB                             // 1 << (10*7)\r\n\tYB                             // 1 << (10*8)\r\n)\r\n```\r\n\r\n（ **译者注：关于 `iota` 的使用涉及到非常复杂多样的情况，这里作者解释的并不清晰，因为很难对 `iota` 的用法进行直观的文字描述。如希望进一步了解，请观看视频教程 [《Go编程基础》](https://github.com/Unknwon/go-fundamental-programming) [第四课：常量与运算符](https://github.com/Unknwon/go-fundamental-programming/blob/master/lectures/lecture4.md)** ）\r\n\r\n`iota` 也可以用在表达式中，如：`iota + 50`。在每遇到一个新的常量块或单个常量声明时， `iota` 都会重置为 0（ **简单地讲，每遇到一次 const 关键字，`iota` 就重置为 0** ）。\r\n\r\n当然，常量之所以为常量就是恒定不变的量，因此我们无法在程序运行过程中修改它的值；如果你在代码中试图修改常量的值则会引发编译错误。\r\n\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[Go 程序的基本结构和要素](04.2.md)\r\n- 下一节：[变量](04.4.md)\r\n"
  },
  {
    "path": "eBook/04.4.md",
    "content": "# 4.4 变量\n\n## 4.4.1 简介\n\n声明变量的一般形式是使用 `var` 关键字：`var identifier type`。\n\n需要注意的是，Go 和许多编程语言不同，它在声明变量时将变量的类型放在变量的名称之后。Go 为什么要选择这么做呢？\n\n首先，它是为了避免像 C 语言中那样含糊不清的声明形式，例如：`int* a, b;`。在这个例子中，只有 `a` 是指针而 `b` 不是。如果你想要这两个变量都是指针，则需要将它们分开书写（你可以在 [Go 语言的声明语法](http://blog.golang.org/2010/07/gos-declaration-syntax.html) 页面找到有关于这个话题的更多讨论）。\n\n而在 Go 中，则可以很轻松地将它们都声明为指针类型：\n\n```go\nvar a, b *int\n```\n\n其次，这种语法能够按照从左至右的顺序阅读，使得代码更加容易理解。\n\n示例：\n\n```go\nvar a int\nvar b bool\nvar str string\n```\n\n你也可以改写成这种形式：\n\n```go\nvar (\n\ta int\n\tb bool\n\tstr string\n)\n```\n\n这种因式分解关键字的写法一般用于声明全局变量。\n\n当一个变量被声明之后，系统自动赋予它该类型的零值：`int` 为 `0`，`float32(64)` 为 `0.0`，bool 为 `false`，`string` 为空字符串，指针为 `nil`。记住，所有的内存在 Go 中都是经过初始化的。\n\n变量的命名规则遵循骆驼命名法，即首个单词小写，每个新单词的首字母大写，例如：`numShips` 和 `startDate`。\n\n但如果你的全局变量希望能够被外部包所使用，则需要将首个单词的首字母也大写（第 4.2 节：可见性规则）。\n\n一个变量（常量、类型或函数）在程序中都有一定的作用范围，称之为作用域。如果一个变量在函数体外声明，则被认为是全局变量，可以在整个包甚至外部包（被导出后）使用，不管你声明在哪个源文件里或在哪个源文件里调用该变量。\n\n在函数体内声明的变量称之为局部变量，它们的作用域只在函数体内，参数和返回值变量也是局部变量。在 [第 5 章](05.0.md)，我们将会学习到像 `if` 和 `for` 这些控制结构，而在这些结构中声明的变量的作用域只在相应的代码块内。一般情况下，局部变量的作用域可以通过代码块（用大括号括起来的部分）判断。\n\n尽管变量的标识符必须是唯一的，但你可以在某个代码块的内层代码块中使用相同名称的变量，则此时外部的同名变量将会暂时隐藏（结束内部代码块的执行后隐藏的外部同名变量又会出现，而内部同名变量则被释放），你任何的操作都只会影响内部代码块的局部变量。\n\n变量可以编译期间就被赋值，赋值给变量使用运算符等号 `=`，当然你也可以在运行时对变量进行赋值操作。\n\n示例：\n\n```go\na = 15\nb = false\n```\n\n一般情况下，当变量a和变量b之间类型相同时，才能进行如 `a = b` 的赋值。\n\n声明与赋值（初始化）语句也可以组合起来。\n\n示例：\n\n```go\nvar identifier [type] = value\nvar a int = 15\nvar i = 5\nvar b bool = false\nvar str string = \"Go says hello to the world!\"\n```\n\n但是 Go 编译器的智商已经高到可以根据变量的值来自动推断其类型，这有点像 Ruby 和 Python 这类动态语言，只不过它们是在运行时进行推断，而 Go 是在编译时就已经完成推断过程。因此，你还可以使用下面的这些形式来声明及初始化变量：\n\n```go\nvar a = 15\nvar b = false\nvar str = \"Go says hello to the world!\"\n```\n\n或：\n\n```go\nvar (\n\ta = 15\n\tb = false\n\tstr = \"Go says hello to the world!\"\n\tnumShips = 50\n\tcity string\n)\n```\n\n不过自动推断类型并不是任何时候都适用的，当你想要给变量的类型并不是自动推断出的某种类型时，你还是需要显式指定变量的类型，例如：\n\n```go\nvar n int64 = 2\n```\n\n然而，`var a` 这种语法是不正确的，因为编译器没有任何可以用于自动推断类型的依据。变量的类型也可以在运行时实现自动推断，例如：\n\n```go\nvar (\n\tHOME = os.Getenv(\"HOME\")\n\tUSER = os.Getenv(\"USER\")\n\tGOROOT = os.Getenv(\"GOROOT\")\n)\n```\n\n这种写法主要用于声明包级别的全局变量，当你在函数体内声明局部变量时，应使用简短声明语法 `:=`，例如：\n\n```go\na := 1\n```\n\n下面这个例子展示了如何通过 `runtime` 包在运行时获取所在的操作系统类型，以及如何通过 `os` 包中的函数 `os.Getenv()` 来获取环境变量中的值，并保存到 `string` 类型的局部变量 `path` 中。\n\n示例 4.5 [goos.go](examples/chapter_4/goos.go)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n   \"runtime\"\n\t\"os\"\n)\n\nfunc main() {\n\tvar goos string = runtime.GOOS\n\tfmt.Printf(\"The operating system is: %s\\n\", goos)\n\tpath := os.Getenv(\"PATH\")\n\tfmt.Printf(\"Path is %s\\n\", path)\n}\n```\n\n如果你在 Windows 下运行这段代码，则会输出 `The operating system is: windows` 以及相应的环境变量的值；如果你在 Linux 下运行这段代码，则会输出 `The operating system is: linux` 以及相应的的环境变量的值。\n\n这里用到了 `Printf` 的格式化输出的功能（[第 4.4.3 节](./04.4.md)）。\n\n## 4.4.2 值类型和引用类型\n\n程序中所用到的内存在计算机中使用一堆箱子来表示（这也是人们在讲解它的时候的画法），这些箱子被称为“字”。根据不同的处理器以及操作系统类型，所有的字都具有 32 位（4 字节）或 64 位（8 字节）的相同长度；所有的字都使用相关的内存地址来进行表示（以十六进制数表示）。\n\n所有像 `int`、`float`、`bool` 和 `string` 这些基本类型都属于值类型，使用这些类型的变量直接指向存在内存中的值：\n\n<img src=\"images/4.4.2_fig4.1.jpg?raw=true\" style=\"zoom:67%;\" />\n\n另外，像数组（[第 7 章](./07.0.md)）和结构（[第 10 章](./10.0md)）这些复合类型也是值类型。\n\n当使用等号 `=` 将一个变量的值赋值给另一个变量时，如：`j = i`，实际上是在内存中将 `i` 的值进行了拷贝：\n\n<img src=\"images/4.4.2_fig4.2.jpg?raw=true\" style=\"zoom: 67%;\" />\n\n你可以通过 `&i` 来获取变量 `i` 的内存地址（[第 4.9 节](./04.9.md)），例如：`0xf840000040`（每次的地址都可能不一样）。值类型的变量的值存储在栈中。\n\n内存地址会根据机器的不同而有所不同，甚至相同的程序在不同的机器上执行后也会有不同的内存地址。因为每台机器可能有不同的存储器布局，并且位置分配也可能不同。\n\n更复杂的数据通常会需要使用多个字，这些数据一般使用引用类型保存。\n\n一个引用类型的变量 `r1` 存储的是 `r1` 的值所在的内存地址（数字），或内存地址中第一个字所在的位置。\n\n<img src=\"images/4.4.2_fig4.3.jpg?raw=true\" style=\"zoom:67%;\" />\n\n这个内存地址被称之为指针（你可以从上图中很清晰地看到，[第 4.9 节](./04.9.md) 将会详细说明），这个指针实际上也被存在另外的某一个字中。\n\n同一个引用类型的指针指向的多个字可以是在连续的内存地址中（内存布局是连续的），这也是计算效率最高的一种存储形式；也可以将这些字分散存放在内存中，每个字都指示了下一个字所在的内存地址。\n\n当使用赋值语句 `r2 = r1` 时，只有引用（地址）被复制。\n\n如果 `r1` 的值被改变了，那么这个值的所有引用都会指向被修改后的内容，在这个例子中，`r2` 也会受到影响。\n\n在 Go 语言中，指针（[第 4.9 节](./04.9.md)）属于引用类型，其它的引用类型还包括 slices（[第 7 章](07.0.md)），maps（[第 8 章](08.0.md)）和 channel（[第 13 章](13.0.md)）。被引用的变量会存储在堆中，以便进行垃圾回收，且比栈拥有更大的内存空间。\n\n## 4.4.3 打印\n\n函数 `Printf` 可以在 `fmt` 包外部使用，这是因为它以大写字母 P 开头，该函数主要用于打印输出到控制台。通常使用的格式化字符串作为第一个参数：\n\n```go\nfunc Printf(format string, list of variables to be printed)\n```\n\n在示例 4.5 中，格式化字符串为：`\"The operating system is: %s\\n\"`。\n\n这个格式化字符串可以含有一个或多个的格式化标识符，例如：`%..`，其中 `..` 可以被不同类型所对应的标识符替换，如 `%s` 代表字符串标识符、`%v` 代表使用类型的默认输出格式的标识符。这些标识符所对应的值从格式化字符串后的第一个逗号开始按照相同顺序添加，如果参数超过 1 个则同样需要使用逗号分隔。使用这些占位符可以很好地控制格式化输出的文本。\n\n函数 `fmt.Sprintf` 与 `Printf` 的作用是完全相同的，不过前者将格式化后的字符串以返回值的形式返回给调用者，因此你可以在程序中使用包含变量的字符串，具体例子可以参见示例 15.4 [simple_tcp_server.go](examples/chapter_15/simple_tcp_server.go)。\n\n函数 `fmt.Print` 和 `fmt.Println` 会自动使用格式化标识符 `%v` 对字符串进行格式化，两者都会在每个参数之间自动增加空格，而后者还会在字符串的最后加上一个换行符。例如：\n\n```go\nfmt.Print(\"Hello:\", 23)\n```\n\n将输出：`Hello: 23`。\n\n## 4.4.4 简短形式，使用 := 赋值操作符\n\n我们知道可以在变量的初始化时省略变量的类型而由系统自动推断，而这个时候再在 Example 4.4.1 的最后一个声明语句写上 `var` 关键字就显得有些多余了，因此我们可以将它们简写为 `a := 50` 或 `b := false`。\n\n`a` 和 `b` 的类型（`int` 和 `bool`）将由编译器自动推断。\n\n这是使用变量的首选形式，但是它只能被用在函数体内，而不可以用于全局变量的声明与赋值。使用操作符 `:=` 可以高效地创建一个新的变量，称之为初始化声明。\n\n**注意事项**\n\n如果在相同的代码块中，我们不可以再次对于相同名称的变量使用初始化声明，例如：`a := 20` 就是不被允许的，编译器会提示错误 `no new variables on left side of :=`，但是 `a = 20` 是可以的，因为这是给相同的变量赋予一个新的值。\n\n如果你在定义变量 `a` 之前使用它，则会得到编译错误 `undefined: a`。\n\n如果你声明了一个局部变量却没有在相同的代码块中使用它，同样会得到编译错误，例如下面这个例子当中的变量 `a`：\n\n```go\nfunc main() {\n   var a string = \"abc\"\n   fmt.Println(\"hello, world\")\n}\n```\n\n尝试编译这段代码将得到错误 `a declared and not used`。\n\n此外，单纯地给 `a` 赋值也是不够的，这个值必须被使用，所以使用 `fmt.Println(\"hello, world\", a)` 会移除错误。\n\n但是全局变量是允许声明但不使用。\n\n其他的简短形式为：\n\n同一类型的多个变量可以声明在同一行，如：\n\n```go\nvar a, b, c int\n```\n\n(这是将类型写在标识符后面的一个重要原因)\n\n多变量可以在同一行进行赋值，如：\n\n```go\na, b, c = 5, 7, \"abc\"\n```\n\n上面这行假设了变量 `a`，`b` 和 `c` 都已经被声明，否则的话应该这样使用：\n\n```go\na, b, c := 5, 7, \"abc\"\n```\n\n右边的这些值以相同的顺序赋值给左边的变量，所以 `a` 的值是 `5`， `b` 的值是 `7`，`c` 的值是 `\"abc\"`。\n\n这被称为 **并行** 或 **同时** 赋值。\n\n如果你想要交换两个变量的值，则可以简单地使用 `a, b = b, a`。\n\n(在 Go 语言中，这样省去了使用交换函数的必要)\n\n空白标识符 `_` 也被用于抛弃值，如值 `5` 在：`_, b = 5, 7` 中被抛弃。\n\n`_` 实际上是一个只写变量，你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量，但有时你并不需要使用从一个函数得到的所有返回值。\n\n并行赋值也被用于当一个函数返回多个返回值时，比如这里的 `val` 和错误 `err` 是通过调用 `Func1` 函数同时得到：`val, err = Func1(var1)`。\n\n## 4.4.5 init 函数\n\n变量除了可以在全局声明中初始化，也可以在 `init()` 函数中初始化。这是一类非常特殊的函数，它不能够被人为调用，而是在每个包完成初始化后自动执行，并且执行优先级比 `main()` 函数高。\n\n每个源文件可以包含多个 `init()` 函数，同一个源文件中的 `init()` 函数会按照从上到下的顺序执行，如果一个包有多个源文件包含 `init()` 函数的话，则官方鼓励但不保证以文件名的顺序调用。初始化总是以单线程并且按照包的依赖关系顺序执行。\n\n一个可能的用途是在开始执行程序之前对数据进行检验或修复，以保证程序状态的正确性。\n\n示例 4.6 [init.go](examples/chapter_4/init.go):\n\n```go\npackage trans\n\nimport \"math\"\n\nvar Pi float64\n\nfunc init() {\n   Pi = 4 * math.Atan(1) // init() function computes Pi\n}\n```\n\n在它的 `init()` 函数中计算变量 `Pi` 的初始值。\n\n示例 4.7 [user_init.go](examples/chapter_4/user_init.go) 中导入了包 `trans`（需要 `init.go` 目录为 `./trans/init.go` ）并且使用到了变量 `Pi`：\n\n```go\npackage main\n\nimport (\n   \"fmt\"\n   \"./trans\"\n)\n\nvar twoPi = 2 * trans.Pi\n\nfunc main() {\n   fmt.Printf(\"2*Pi = %g\\n\", twoPi) // 2*Pi = 6.283185307179586\n}\n```\n\n`init()` 函数也经常被用在当一个程序开始之前调用后台执行的 goroutine，如下面这个例子当中的 `backend()`：\n\n```go\nfunc init() {\n   // setup preparations\n   go backend()\n}\n```\n\n**练习** 推断以下程序的输出，并解释你的答案，然后编译并执行它们。\n\n练习 4.1 [local_scope.go](examples/chapter_4/local_scope.go):\n\n```go\npackage main\n\nvar a = \"G\"\n\nfunc main() {\n   n()\n   m()\n   n()\n}\n\nfunc n() { print(a) }\n\nfunc m() {\n   a := \"O\"\n   print(a)\n}\n```\n\n练习 4.2 [global_scope.go](examples/chapter_4/global_scope.go):\n\n```go\npackage main\n\nvar a = \"G\"\n\nfunc main() {\n   n()\n   m()\n   n()\n}\n\nfunc n() {\n   print(a)\n}\n\nfunc m() {\n   a = \"O\"\n   print(a)\n}\n```\n\n练习 4.3 [function_calls_function.go](examples/chapter_4/function_calls_function.go)\n\n```go\npackage main\n\nvar a string\n\nfunc main() {\n   a = \"G\"\n   print(a)\n   f1()\n}\n\nfunc f1() {\n   a := \"O\"\n   print(a)\n   f2()\n}\n\nfunc f2() {\n   print(a)\n}\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[常量](04.3.md)\n- 下一节：[基本类型和运算符](04.5.md)\n"
  },
  {
    "path": "eBook/04.5.md",
    "content": "# 4.5 基本类型和运算符\n\n我们将在这个部分讲解有关布尔型、数字型和字符型的相关知识。\n\n表达式是一种特定的类型的值，它可以由其它的值以及运算符组合而成。每个类型都定义了可以和自己结合的运算符集合，如果你使用了不在这个集合中的运算符，则会在编译时获得编译错误。\n\n一元运算符只可以用于一个值的操作（作为后缀），而二元运算符则可以和两个值或者操作数结合（作为中缀）。\n\n只有两个类型相同的值才可以和二元运算符结合，另外要注意的是，Go 是强类型语言，因此不会进行隐式转换，任何不同类型之间的转换都必须显式说明（第 4.2 节）。Go 不存在像 C 那样的运算符重载，表达式的解析顺序是从左至右。\n\n你可以在第 4.5.3 节找到有关运算符优先级的相关信息，优先级越高的运算符在条件相同的情况下将被优先执行。但是你可以通过使用括号将其中的表达式括起来，以人为地提升某个表达式的运算优先级。\n\n## 4.5.1 布尔类型 bool\n\n一个简单的例子：`var b bool = true`。\n\n布尔型的值只可以是常量 true 或者 false。\n\n两个类型相同的值可以使用相等 `==` 或者不等 `!=` 运算符来进行比较并获得一个布尔型的值。\n\n当相等运算符两边的值是完全相同的值的时候会返回 `true`，否则返回 `false`，并且只有在两个的值的类型相同的情况下才可以使用。\n\n示例：\n\n```go\nvar aVar = 10\naVar == 5 -> false\naVar == 10 -> true\n```\n\n当不等运算符两边的值是不同的时候会返回 `true`，否则返回 `false`。\n\n示例：\n\n```go\nvar aVar = 10\naVar != 5 -> true\naVar != 10 -> false\n```\n\nGo 对于值之间的比较有非常严格的限制，只有两个类型相同的值才可以进行比较，如果值的类型是接口（interface，[第 11 章](11.0.md)），它们也必须都实现了相同的接口。如果其中一个值是常量，那么另外一个值的类型必须和该常量类型相兼容的。如果以上条件都不满足，则其中一个值的类型必须在被转换为和另外一个值的类型相同之后才可以进行比较。\n\n布尔型的常量和变量也可以通过和逻辑运算符（非 `!`、与 `&&`、或 `||`）结合来产生另外一个布尔值，这样的逻辑语句就其本身而言，并不是一个完整的 Go 语句。\n\n逻辑值可以被用于条件结构中的条件语句（[第 5 章](05.0.md)），以便测试某个条件是否满足。另外，与 `&&`、或 `||` 与相等 `==` 或不等 `!=` 属于二元运算符，而非 `!` 属于一元运算符。在接下来的内容中，我们会使用 T 来代表条件符合的语句，用 F 来代表条件不符合的语句。\n\nGo 语言中包含以下逻辑运算符：\n\n非运算符：`!`\n\n```go\n!T -> false\n!F -> true\n```\n\n非运算符用于取得和布尔值相反的结果。\n\n与运算符：`&&`\n\n```go\nT && T -> true\nT && F -> false\nF && T -> false\nF && F -> false\n```\n\n只有当两边的值都为 `true` 的时候，和运算符的结果才是 `true`。\n\n或运算符：`||`\n\n```go\nT || T -> true\nT || F -> true\nF || T -> true\nF || F -> false\n```\n\n只有当两边的值都为 `false` 的时候，或运算符的结果才是 `false`，其中任意一边的值为 `true` 就能够使得该表达式的结果为 `true`。\n\n在 Go 语言中，`&&` 和 `||` 是具有快捷性质的运算符，当运算符左边表达式的值已经能够决定整个表达式的值的时候（`&&` 左边的值为 `false`，`||` 左边的值为 `true`），运算符右边的表达式将不会被执行。利用这个性质，如果你有多个条件判断，应当将计算过程较为复杂的表达式放在运算符的右侧以减少不必要的运算。\n\n利用括号同样可以升级某个表达式的运算优先级。\n\n在格式化输出时，你可以使用 `%t` 来表示你要输出的值为布尔型。\n\n布尔值（以及任何结果为布尔值的表达式）最常用在条件结构的条件语句中，例如：if、for 和 switch 结构（第 5 章）。\n\n对于布尔值的好的命名能够很好地提升代码的可读性，例如以 `is` 或者 `Is` 开头的 `isSorted`、`isFinished`、`isVisible`，使用这样的命名能够在阅读代码的获得阅读正常语句一样的良好体验，例如标准库中的 `unicode.IsDigit(ch)`（[第 4.5.5 节](04.5.md)）。\n\n## 4.5.2 数字类型\n\n### 4.5.2.1 整型 int 和浮点型 float\n\nGo 语言支持整型和浮点型数字，并且原生支持复数，其中位的运算采用补码（详情参见 [二的补码](http://en.wikipedia.org/wiki/Two's_complement) 页面）。\n\nGo 也有基于架构的类型，例如：`int`、`uint` 和 `uintptr`。\n\n这些类型的长度都是根据运行程序所在的操作系统类型所决定的：\n\n- `int` 和 `uint` 在 32 位操作系统上，它们均使用 32 位（4 个字节），在 64 位操作系统上，它们均使用 64 位（8 个字节）。\n- `uintptr` 的长度被设定为足够存放一个指针即可。\n\nGo 语言中没有 float 类型。（Go语言中只有 `float32` 和 `float64`）没有 double 类型。\n\n与操作系统架构无关的类型都有固定的大小，并在类型的名称中就可以看出来：\n\n整数：\n\n- `int8`（-128 -> 127）\n- `int16`（-32768 -> 32767）\n- `int32`（-2,147,483,648 -> 2,147,483,647）\n- `int64`（-9,223,372,036,854,775,808 -> 9,223,372,036,854,775,807）\n\n无符号整数：\n\n- `uint8`（0 -> 255）\n- `uint16`（0 -> 65,535）\n- `uint32`（0 -> 4,294,967,295）\n- `uint64`（0 -> 18,446,744,073,709,551,615）\n\n浮点型（IEEE-754 标准）：\n\n- `float32`（+- 1e-45 -> +- 3.4 * 1e38）\n- `float64`（+- 5 * 1e-324 -> 107 * 1e308）\n\n`int` 型是计算最快的一种类型。\n\n整型的零值为 `0`，浮点型的零值为 `0.0`。\n\n`float32` 精确到小数点后 7 位，`float64` 精确到小数点后 15 位。由于精确度的缘故，你在使用 `==` 或者 `!=` 来比较浮点数时应当非常小心。你最好在正式使用前测试对于精确度要求较高的运算。\n\n你应该尽可能地使用 `float64`，因为 `math` 包中所有有关数学运算的函数都会要求接收这个类型。\n\n你可以通过增加前缀 0 来表示 8 进制数（如：077），增加前缀 0x 来表示 16 进制数（如：`0xFF`），以及使用 `e` 来表示 10 的连乘（如： 1e3 = 1000，或者 6.022e23 = 6.022 x 1e23）。\n\n你可以使用 `a := uint64(0)` 来同时完成类型转换和赋值操作，这样 `a` 的类型就是 `uint64`。\n\nGo 中不允许不同类型之间的混合使用，但是对于常量的类型限制非常少，因此允许常量之间的混合使用，下面这个程序很好地解释了这个现象（该程序无法通过编译）：\n\n示例 4.8 [type_mixing.go](examples/chapter_4/type_mixing.go)\n\n```go\npackage main\n\nfunc main() {\n\tvar a int\n\tvar b int32\n\ta = 15\n\tb = a + a\t // 编译错误\n\tb = b + 5    // 因为 5 是常量，所以可以通过编译\n}\n```\n\n如果你尝试编译该程序，则将得到编译错误 `cannot use a + a (type int) as type int32 in assignment`。\n\n同样地，`int16`  也不能够被隐式转换为 `int32`。\n\n下面这个程序展示了通过显式转换来避免这个问题（[第 4.2 节](04.2.md)）。\n\n示例 4.9 [casting.go](examples/chapter_4/casting.go)\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar n int16 = 34\n\tvar m int32\n\t// compiler error: cannot use n (type int16) as type int32 in assignment\n\t//m = n\n\tm = int32(n)\n\n\tfmt.Printf(\"32 bit int is: %d\\n\", m)\n\tfmt.Printf(\"16 bit int is: %d\\n\", n)\n}\n```\n\n输出：\n\n```\n32 bit int is: 34\n16 bit int is: 34\n```\n\n**格式化说明符**\n\n在格式化字符串里，`%d` 用于格式化整数（`%x` 和 `%X` 用于格式化 16 进制表示的数字），`%g` 用于格式化浮点型（`%f` 输出浮点数，`%e` 输出科学计数表示法），`%0nd` 用于规定输出长度为 n 的整数，其中开头的数字 0 是必须的。\n\n`%n.mg` 用于表示数字 n 并精确到小数点后 m 位，除了使用 g 之外，还可以使用 e 或者 f，例如：使用格式化字符串 `%5.2e` 来输出 3.4 的结果为 `3.40e+00`。\n\n**数字值转换**\n\n当进行类似 `a32bitInt = int32(a32Float)` 的转换时，小数点后的数字将被丢弃。这种情况一般发生当从取值范围较大的类型转换为取值范围较小的类型时，或者你可以写一个专门用于处理类型转换的函数来确保没有发生精度的丢失。下面这个例子展示如何安全地从 `int` 型转换为 `int8`：\n\n```go\nfunc Uint8FromInt(n int) (uint8, error) {\n\tif 0 <= n && n <= math.MaxUint8 { // conversion is safe\n\t\treturn uint8(n), nil\n\t}\n\treturn 0, fmt.Errorf(\"%d is out of the uint8 range\", n)\n}\n```\n\n或者安全地从 `float64` 转换为 `int`：\n\n```go\nfunc IntFromFloat64(x float64) int {\n\tif math.MinInt32 <= x && x <= math.MaxInt32 { // x lies in the integer range\n\t\twhole, fraction := math.Modf(x)\n\t\tif fraction >= 0.5 {\n\t\t\twhole++\n\t\t}\n\t\treturn int(whole)\n\t}\n\tpanic(fmt.Sprintf(\"%g is out of the int32 range\", x))\n}\n```\n\n不过如果你实际存的数字超出你要转换到的类型的取值范围的话，则会引发 `panic`（[第 13.2 节](./13.2.md)）。\n\n**问题 4.1** `int` 和 `int64` 是相同的类型吗？\n\n### 4.5.2.2 复数\n\nGo 拥有以下复数类型：\n\n\tcomplex64 (32 位实数和虚数)\n\tcomplex128 (64 位实数和虚数)\n\n复数使用 `re+imI` 来表示，其中 `re` 代表实数部分，`im` 代表虚数部分，`I` 代表根号负 1。\n\n示例：\n\n```go\nvar c1 complex64 = 5 + 10i\nfmt.Printf(\"The value is: %v\", c1)\n// 输出： 5 + 10i\n```\n\n如果 `re` 和 `im` 的类型均为 `float32`，那么类型为 `complex64` 的复数 `c` 可以通过以下方式来获得：\n\n```go\nc = complex(re, im)\n```\n\n函数 `real(c)` 和 `imag(c)` 可以分别获得相应的实数和虚数部分。\n\n在使用格式化说明符时，可以使用 `%v` 来表示复数，但当你希望只表示其中的一个部分的时候需要使用 `%f`。\n\n复数支持和其它数字类型一样的运算。当你使用等号 `==` 或者不等号 `!=` 对复数进行比较运算时，注意对精确度的把握。`cmath` 包中包含了一些操作复数的公共方法。如果你对内存的要求不是特别高，最好使用 `complex128` 作为计算类型，因为相关函数都使用这个类型的参数。\n\n### 4.5.2.3 位运算\n\n位运算只能用于整数类型的变量，且需当它们拥有等长位模式时。\n\n`%b` 是用于表示位的格式化标识符。\n\n**二元运算符**\n\n- 按位与 `&`：\n\n\t对应位置上的值经过和运算结果，具体参见和运算符（第 4.5.1 节），并将 T (true) 替换为 `1`，将 F (false) 替换为 `0`\n\n\t\t1 & 1 -> 1\n\t\t1 & 0 -> 0\n\t\t0 & 1 -> 0\n\t\t0 & 0 -> 0\n\n- 按位或 `|`：\n\n\t对应位置上的值经过或运算结果，具体参见或运算符（第 4.5.1 节），并将 T (true) 替换为 `1`，将 F (false) 替换为 `0`\n\n\t\t1 | 1 -> 1\n\t\t1 | 0 -> 1\n\t\t0 | 1 -> 1\n\t\t0 | 0 -> 0\n\n- 按位异或 `^`：\n\n\t对应位置上的值根据以下规则组合：\n\n\t\t1 ^ 1 -> 0\n\t\t1 ^ 0 -> 1\n\t\t0 ^ 1 -> 1\n\t\t0 ^ 0 -> 0\n\n- 位清除 `&^`：将指定位置上的值设置为 `0`。\n\n\t```go\n\tpackage main\n\timport \"fmt\"\n\tfunc main() {\n\t\tvar x uint8 = 15\n\t\tvar y uint8 = 4\n\t\tfmt.Printf(\"%08b\\n\", x &^ y);  // 00001011\n\t}\n\t```\n\n**一元运算符**\n\n- 按位补足 `^`：\n\n\t该运算符与异或运算符一同使用，即 `m^x`，对于无符号 `x` 使用 “全部位设置为 1” 的规则，对于有符号 `x` 时使用 `m=-1`。例如：\n\n\t\t^10 = -01 ^ 10 = -11\n\n- 位左移 `<<`：\n\n\t- 用法：`bitP << n`。\n\t- `bitP` 的位向左移动 `n` 位，右侧空白部分使用 0 填充；如果 `n` 等于 2，则结果是 2 的相应倍数，即 2 的 `n` 次方。例如：\n\n\t\t\t1 << 10 // 等于 1 KB\n\t\t\t1 << 20 // 等于 1 MB\n\t\t\t1 << 30 // 等于 1 GB\n\n- 位右移 `>>`：\n\n\t- 用法：`bitP >> n`。\n\t- `bitP` 的位向右移动 `n` 位，左侧空白部分使用 0 填充；如果 `n` 等于 2，则结果是当前值除以 2 的 n 次方。\n\n当希望把结果赋值给第一个操作数时，可以简写为 `a <<= 2` 或者 `b ^= a & 0xffffffff`。\n\n**位左移常见实现存储单位的用例**\n\n使用位左移与 `iota` 计数配合可优雅地实现存储单位的常量枚举：\n\n```go\ntype ByteSize float64\nconst (\n\t_ = iota // 通过赋值给空白标识符来忽略值\n\tKB ByteSize = 1<<(10*iota)\n\tMB\n\tGB\n\tTB\n\tPB\n\tEB\n\tZB\n\tYB\n)\n```\n\n**在通讯中使用位左移表示标识的用例**\n\n```go\ntype BitFlag int\nconst (\n\tActive BitFlag = 1 << iota // 1 << 0 == 1\n\tSend // 1 << 1 == 2\n\tReceive // 1 << 2 == 4\n)\n\nflag := Active | Send // == 3\n```\n\n### 4.5.2.4 逻辑运算符\n\nGo 中拥有以下逻辑运算符：`==`、`!=`（第 4.5.1 节）、`<`、`<=`、`>`、`>=`。\n\n它们之所以被称为逻辑运算符是因为它们的运算结果总是为布尔值 `bool`。例如：\n\n```go\nb3 := 10 > 5 // b3 is true\n```\n\n### 4.5.2.5 算术运算符\n\n常见可用于整数和浮点数的二元运算符有 `+`、`-`、`*` 和 `/`。\n\n（相对于一般规则而言，Go 在进行字符串拼接时允许使用对运算符 `+` 的重载，但 Go 本身不允许开发者进行自定义的运算符重载）\n\n`/` 对于整数运算而言，结果依旧为整数，例如：`9 / 4 -> 2`。\n\n取余运算符只能作用于整数：`9 % 4 -> 1`。\n\n整数除以 0 可能导致程序崩溃，将会导致运行时的恐慌状态（如果除以 0 的行为在编译时就能被捕捉到，则会引发编译错误）；[第 13 章](13.0.md) 将会详细讲解如何正确地处理此类情况。\n\n浮点数除以 `0.0` 会返回一个无穷尽的结果，使用 `+Inf` 表示。\n\n**练习 4.4** 尝试编译 [divby0.go](exercises/chapter_4/divby0.go)。\n\n你可以将语句 `b = b + a` 简写为 `b += a`，同样的写法也可用于 `-=`、`*=`、`/=`、`%=`。\n\n对于整数和浮点数，你可以使用一元运算符 `++`（递增）和 `--`（递减），但只能用于后缀：\n\n\ti++ -> i += 1 -> i = i + 1\n\ti-- -> i -= 1 -> i = i - 1\n\n同时，带有 `++` 和 `--` 的只能作为语句，而非表达式，因此 `n = i++` 这种写法是无效的，其它像 `f(i++)` 或者 `a[i]=b[i++]` 这些可以用于 C、C++ 和 Java 中的写法在 Go 中也是不允许的。\n\n在运算时 **溢出** 不会产生错误，Go 会简单地将超出位数抛弃。如果你需要范围无限大的整数或者有理数（意味着只被限制于计算机内存），你可以使用标准库中的 `big` 包，该包提供了类似 `big.Int` 和 `big.Rat` 这样的类型（[第 9.4 节](09.4.md)）。\n\n### 4.5.2.6 随机数\n\n一些像游戏或者统计学类的应用需要用到随机数。`rand` 包实现了伪随机数的生成。\n\n示例 4.10 [random.go](examples/chapter_4/random.go) 演示了如何生成 10 个非负随机数：\n\n```go\npackage main\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"time\"\n)\n\nfunc main() {\n\tfor i := 0; i < 10; i++ {\n\t\ta := rand.Int()\n\t\tfmt.Printf(\"%d / \", a)\n\t}\n\tfor i := 0; i < 5; i++ {\n\t\tr := rand.Intn(8)\n\t\tfmt.Printf(\"%d / \", r)\n\t}\n\tfmt.Println()\n\ttimens := int64(time.Now().Nanosecond())\n\trand.Seed(timens)\n\tfor i := 0; i < 10; i++ {\n\t\tfmt.Printf(\"%2.2f / \", 100*rand.Float32())\n\t}\n}\n```\n\n可能的输出：\n\n\t816681689 / 1325201247 / 623951027 / 478285186 / 1654146165 /\n\t1951252986 / 2029250107 / 762911244 / 1372544545 / 591415086 / / 3 / 0 / 6 / 4 / 2 /22.10\n\t/ 65.77 / 65.89 / 16.85 / 75.56 / 46.90 / 55.24 / 55.95 / 25.58 / 70.61 /\n\n函数 `rand.Float32` 和 `rand.Float64` 返回介于 $[0.0, 1.0)$ 之间的伪随机数，其中包括 `0.0` 但不包括 `1.0`。函数 `rand.Intn` 返回介于 $[0, n)$ 之间的伪随机数。\n\n你可以使用 `rand.Seed(value)` 函数来提供伪随机数的生成种子，一般情况下都会使用当前时间的纳秒级数字（第 4.8 节）。\n\n## 4.5.3 运算符与优先级\n\n有些运算符拥有较高的优先级，二元运算符的运算方向均是从左至右。下表列出了所有运算符以及它们的优先级，由上至下代表优先级由高到低：\n\n\t优先级 \t运算符\n\t 7 \t\t^ !\n\t 6 \t\t* / % << >> & &^\n\t 5 \t\t+ - | ^\n\t 4 \t\t== != < <= >= >\n\t 3 \t\t<-\n\t 2 \t\t&&\n\t 1 \t\t||\n\n当然，你可以通过使用括号来临时提升某个表达式的整体运算优先级。\n\n## 4.5.4 类型别名\n\n当你在使用某个类型时，你可以给它起另一个名字，然后你就可以在你的代码中使用新的名字（用于简化名称或解决名称冲突）。\n\n在 `type TZ int` 中，`TZ` 就是 `int` 类型的新名称（用于表示程序中的时区），然后就可以使用 `TZ` 来操作 `int` 类型的数据。\n\n示例 4.11 [type.go](examples/chapter_4/type.go)\n\n```go\npackage main\nimport \"fmt\"\n\ntype TZ int\n\nfunc main() {\n\tvar a, b TZ = 3, 4\n\tc := a + b\n\tfmt.Printf(\"c has the value: %d\", c) // 输出：c has the value: 7\n}\n```\n\n实际上，类型别名得到的新类型并非和原类型完全相同，新类型不会拥有原类型所附带的方法（[第 10 章](./10.0.md)）；`TZ` 可以自定义一个方法用来输出更加人性化的时区信息。\n\n**练习 4.5** 定义一个 `string` 的类型别名 `Rope`，并声明一个该类型的变量。\n\n## 4.5.5 字符类型\n\n严格来说，这并不是 Go 语言的一个类型，字符只是整数的特殊用例。`byte` 类型是 `uint8` 的别名，对于只占用 1 个字节的传统 ASCII 编码的字符来说，完全没有问题。例如：`var ch byte = 'A'`；字符使用单引号括起来。\n\n在 ASCII 码表中，`'A'` 的值是 `65`，而使用 16 进制表示则为 `41`，所以下面的写法是等效的：\n\n```go\nvar ch byte = 65 或 var ch byte = '\\x41'\n```\n\n（`\\x` 总是紧跟着长度为 2 的 16 进制数）\n\n另外一种可能的写法是 `\\` 后面紧跟着长度为 3 的 8 进制数，例如：`\\377`。\n\n不过 Go 同样支持 Unicode（UTF-8），因此字符同样称为 Unicode 代码点或者 runes，并在内存中使用 `int` 来表示。在文档中，一般使用格式 `U+hhhh` 来表示，其中 `h` 表示一个 16 进制数。其实 `rune` 也是 Go 当中的一个类型，并且是 `int32` 的别名。\n\n在书写 Unicode 字符时，需要在 16 进制数之前加上前缀 `\\u` 或者 `\\U`。\n\n因为 Unicode 至少占用 2 个字节，所以我们使用 `int16` 或者 `int` 类型来表示。如果需要使用到 4 字节，则会加上 `\\U` 前缀；前缀 `\\u` 则总是紧跟着长度为 4 的 16 进制数，前缀 `\\U` 紧跟着长度为 8 的 16 进制数。\n\n示例 4.12 [char.go](examples/chapter_4/char.go)\n\n```go\nvar ch int = '\\u0041'\nvar ch2 int = '\\u03B2'\nvar ch3 int = '\\U00101234'\nfmt.Printf(\"%d - %d - %d\\n\", ch, ch2, ch3) // integer\nfmt.Printf(\"%c - %c - %c\\n\", ch, ch2, ch3) // character\nfmt.Printf(\"%X - %X - %X\\n\", ch, ch2, ch3) // UTF-8 bytes\nfmt.Printf(\"%U - %U - %U\", ch, ch2, ch3) // UTF-8 code point\n```\n\n输出：\n\n\t65 - 946 - 1053236\n\tA - β - r\n\t41 - 3B2 - 101234\n\tU+0041 - U+03B2 - U+101234\n\n格式化说明符 `%c` 用于表示字符；当和字符配合使用时，`%v` 或 `%d` 会输出用于表示该字符的整数；`%U` 输出格式为 `U+hhhh` 的字符串（另一个示例见[第 5.4.4 节](./05.4.md)）。\n\n包 `unicode` 包含了一些针对测试字符的非常有用的函数（其中 `ch` 代表字符）：\n\n- 判断是否为字母：`unicode.IsLetter(ch)`\n- 判断是否为数字：`unicode.IsDigit(ch)`\n- 判断是否为空白符号：`unicode.IsSpace(ch)`\n\n这些函数返回单个布尔值。包 `utf8` 拥有更多与 `rune` 类型相关的函数。\n\n（ **译者注：关于类型的相关讲解，可参考视频教程 《Go编程基础》 第 3 课：[类型与变量](https://github.com/Unknwon/go-fundamental-programming/blob/master/lectures/lecture3.md)** ）\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[变量](04.4.md)\n- 下一节：[字符串](04.6.md)\n"
  },
  {
    "path": "eBook/04.6.md",
    "content": "# 4.6 字符串\n\n字符串是 UTF-8 字符的一个序列（当字符为 ASCII 码时则占用 1 个字节，其它字符根据需要占用 2-4 个字节）。UTF-8 是被广泛使用的编码格式，是文本文件的标准编码，其它包括 XML 和 JSON 在内，也都使用该编码。由于该编码对占用字节长度的不定性，Go 中的字符串里面的字符也可能根据需要占用 1 至 4 个字节（示例见[第 4.6 节](04.6.md)），这与其它语言如 C++、Java 或者 Python 不同（Java 始终使用 2 个字节）。Go 这样做的好处是不仅减少了内存和硬盘空间占用，同时也不用像其它语言那样需要对使用 UTF-8 字符集的文本进行编码和解码。\n\n字符串是一种值类型，且值不可变，即创建某个文本后你无法再次修改这个文本的内容；更深入地讲，字符串是字节的定长数组。\n\nGo 支持以下 2 种形式的字面值：\n\n- 解释字符串：\n\n\t该类字符串使用双引号括起来，其中的相关的转义字符将被替换，这些转义字符包括：\n\n\t- `\\n`：换行符\n\t- `\\r`：回车符\n\t- `\\t`：tab 键\n\t- `\\u` 或 `\\U`：Unicode 字符\n\t- `\\\\`：反斜杠自身\n\n- 非解释字符串：\n\n\t该类字符串使用反引号括起来，支持换行，例如：\n\n\t\t`This is a raw string \\n` 中的 `\\n\\` 会被原样输出。\n\n和 C/C++不一样，Go 中的字符串是根据长度限定，而非特殊字符 `\\0`。\n\n`string` 类型的零值为长度为零的字符串，即空字符串 `\"\"`。\n\n一般的比较运算符（`==`、`!=`、`<`、`<=`、`>=`、`>`）通过在内存中按字节比较来实现字符串的对比。你可以通过函数 `len()` 来获取字符串所占的字节长度，例如：`len(str)`。\n\n字符串的内容（纯字节）可以通过标准索引法来获取，在中括号 `[]` 内写入索引，索引从 0 开始计数：\n\n- 字符串 `str` 的第 1 个字节：`str[0]`\n- 第 `i` 个字节：`str[i - 1]`\n- 最后 1 个字节：`str[len(str)-1]`\n\n需要注意的是，这种转换方案只对纯 ASCII 码的字符串有效。\n\n**注意事项** 获取字符串中某个字节的地址的行为是非法的，例如：`&str[i]`。\n\n**字符串拼接符 `+`**\n\n两个字符串 `s1` 和 `s2` 可以通过 `s := s1 + s2` 拼接在一起。\n\n`s2` 追加在 `s1` 尾部并生成一个新的字符串 `s`。\n\n你可以通过以下方式来对代码中多行的字符串进行拼接：\n\n```go\nstr := \"Beginning of the string \" +\n\t\"second part of the string\"\n```\n\n由于编译器行尾自动补全分号的缘故，加号 `+` 必须放在第一行。\n\n拼接的简写形式 `+=` 也可以用于字符串：\n\n```go\ns := \"hel\" + \"lo,\"\ns += \"world!\"\nfmt.Println(s) //输出 “hello, world!”\n```\n\n在循环中使用加号 `+` 拼接字符串并不是最高效的做法，更好的办法是使用函数 `strings.Join()`（[第 4.7.10 节](04.7.md)），有没有更好的办法了？有！使用字节缓冲（`bytes.Buffer`）拼接更加给力（[第 7.2.6 节](07.2.md)）！\n\n在[第 7 章](07.0.md)，我们会讲到通过将字符串看作是字节 (`byte`) 的切片 (slice) 来实现对其标准索引法的操作。会在[第 5.4.1 节](05.4.md) 中讲到的 `for` 循环只会根据索引返回字符串中的纯字节，而在[第 5.4.4 节](./05.4.md)（以及[第 7.6.1 节](07.6.md) 的示例）将会展示如何使用 for-range 循环来实现对 Unicode 字符串的迭代操作。在下一节，我们会学习到许多有关字符串操作的函数和方法，同时 `fmt` 包中的 `fmt.Sprint(x)` 也可以格式化生成并返回你所需要的字符串（[第 4.4.3 节](04.3.md)）。\n\n**练习 4.6** [count_characters.go](exercises/chapter_4/count_characters.go)\n\n创建一个用于统计字节和字符 (rune) 的程序，并对字符串 `asSASA ddd dsjkdsjs dk` 进行分析，然后再分析 `asSASA ddd dsjkdsjsこん dk`，最后解释两者不同的原因（提示：使用 `unicode/utf8` 包）。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[基本类型和运算符](04.5.md)\n- 下一节：[strings 和 strconv 包](04.7.md)\n"
  },
  {
    "path": "eBook/04.7.md",
    "content": "# 4.7 strings 和 strconv 包\n\n作为一种基本数据结构，每种语言都有一些对于字符串的预定义处理函数。Go 中使用 `strings` 包来完成对字符串的主要操作。\n\n## 4.7.1 前缀和后缀\n\n`HasPrefix()` 判断字符串 `s` 是否以 `prefix` 开头：\n\n```go\nstrings.HasPrefix(s, prefix string) bool\n```\n\n`HasSuffix()` 判断字符串 `s` 是否以 `suffix` 结尾：\n\n```go\nstrings.HasSuffix(s, suffix string) bool\n```\n\n示例 4.13 [presuffix.go](examples/chapter_4/presuffix.go)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc main() {\n\tvar str string = \"This is an example of a string\"\n\tfmt.Printf(\"T/F? Does the string \\\"%s\\\" have prefix %s? \", str, \"Th\")\n\tfmt.Printf(\"%t\\n\", strings.HasPrefix(str, \"Th\"))\n}\n```\n\n输出：\n\n\tT/F? Does the string \"This is an example of a string\" have prefix Th? true\n\n这个例子同样演示了转义字符 `\\` 和格式化字符串的使用。\n\n## 4.7.2 字符串包含关系\n\n`Contains()` 判断字符串 `s` 是否包含 `substr`：\n\n```go\nstrings.Contains(s, substr string) bool\n```\n\n## 4.7.3 判断子字符串或字符在父字符串中出现的位置（索引）\n\n`Index()` 返回字符串 `str` 在字符串 `s` 中的索引（`str` 的第一个字符的索引），`-1` 表示字符串 `s` 不包含字符串 `str`：\n\n```go\nstrings.Index(s, str string) int\n```\n\n`LastIndex()` 返回字符串 `str` 在字符串 `s` 中最后出现位置的索引（`str` 的第一个字符的索引），`-1` 表示字符串 `s` 不包含字符串 `str`：\n\n```go\nstrings.LastIndex(s, str string) int\n```\n\n如果需要查询非 ASCII 编码的字符在父字符串中的位置，建议使用以下函数来对字符进行定位：\n\n```go\nstrings.IndexRune(s string, r rune) int\n```\n\t注: 原文为 \"If ch is a non-ASCII character use strings.IndexRune(s string, ch int) int.\"\n\t该方法在最新版本的 Go 中定义为 func IndexRune(s string, r rune) int\n\t实际使用中的第二个参数 rune 可以是 rune 或 int, 例如 strings.IndexRune(\"chicken\", 99) 或 strings.IndexRune(\"chicken\", rune('k'))\n\n示例 4.14 [index_in_string.go](examples/chapter_4/index_in_string.go)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc main() {\n\tvar str string = \"Hi, I'm Marc, Hi.\"\n\n\tfmt.Printf(\"The position of \\\"Marc\\\" is: \")\n\tfmt.Printf(\"%d\\n\", strings.Index(str, \"Marc\"))\n\n\tfmt.Printf(\"The position of the first instance of \\\"Hi\\\" is: \")\n\tfmt.Printf(\"%d\\n\", strings.Index(str, \"Hi\"))\n\tfmt.Printf(\"The position of the last instance of \\\"Hi\\\" is: \")\n\tfmt.Printf(\"%d\\n\", strings.LastIndex(str, \"Hi\"))\n\n\tfmt.Printf(\"The position of \\\"Burger\\\" is: \")\n\tfmt.Printf(\"%d\\n\", strings.Index(str, \"Burger\"))\n}\n```\n\n输出：\n\n\tThe position of \"Marc\" is: 8\n\tThe position of the first instance of \"Hi\" is: 0\n\tThe position of the last instance of \"Hi\" is: 14\n\tThe position of \"Burger\" is: -1\n\n## 4.7.4 字符串替换\n\n`Replace()` 用于将字符串 `str` 中的前 `n` 个字符串 `old` 替换为字符串 `new`，并返回一个新的字符串，如果 `n = -1` 则替换所有字符串 `old` 为字符串 `new`：\n\n```go\nstrings.Replace(str, old, new string, n int) string\n```\n\n## 4.7.5 统计字符串出现次数\n\n`Count()` 用于计算字符串 `str` 在字符串 `s` 中出现的非重叠次数：\n\n```go\nstrings.Count(s, str string) int\n```\n\n示例 4.15 [count_substring.go](examples/chapter_4/count_substring.go)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc main() {\n\tvar str string = \"Hello, how is it going, Hugo?\"\n\tvar manyG = \"gggggggggg\"\n\n\tfmt.Printf(\"Number of H's in %s is: \", str)\n\tfmt.Printf(\"%d\\n\", strings.Count(str, \"H\"))\n\n\tfmt.Printf(\"Number of double g's in %s is: \", manyG)\n\tfmt.Printf(\"%d\\n\", strings.Count(manyG, \"gg\"))\n}\n```\n\n输出：\n\n\tNumber of H's in Hello, how is it going, Hugo? is: 2\n\tNumber of double g’s in gggggggggg is: 5\n\n## 4.7.6 重复字符串\n\n`Repeat()` 用于重复 `count` 次字符串 `s` 并返回一个新的字符串：\n\n```go\nstrings.Repeat(s, count int) string\n```\n\n示例 4.16 [repeat_string.go](examples/chapter_4/repeat_string.go)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc main() {\n\tvar origS string = \"Hi there! \"\n\tvar newS string\n\n\tnewS = strings.Repeat(origS, 3)\n\tfmt.Printf(\"The new repeated string is: %s\\n\", newS)\n}\n```\n\n输出：\n\n\tThe new repeated string is: Hi there! Hi there! Hi there!\n\n## 4.7.7 修改字符串大小写\n\n\n`ToLower()` 将字符串中的 Unicode 字符全部转换为相应的小写字符：\n\n```go\nstrings.ToLower(s) string\n```\n\n`ToUpper()` 将字符串中的 Unicode 字符全部转换为相应的大写字符：\n\n```go\nstrings.ToUpper(s) string\n```\n\n示例 4.17 [toupper_lower.go](examples/chapter_4/toupper_lower.go)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc main() {\n\tvar orig string = \"Hey, how are you George?\"\n\tvar lower string\n\tvar upper string\n\n\tfmt.Printf(\"The original string is: %s\\n\", orig)\n\tlower = strings.ToLower(orig)\n\tfmt.Printf(\"The lowercase string is: %s\\n\", lower)\n\tupper = strings.ToUpper(orig)\n\tfmt.Printf(\"The uppercase string is: %s\\n\", upper)\n}\n```\n\n输出：\n\n\tThe original string is: Hey, how are you George?\n\tThe lowercase string is: hey, how are you george?\n\tThe uppercase string is: HEY, HOW ARE YOU GEORGE?\n\n## 4.7.8 修剪字符串\n\n你可以使用 `strings.TrimSpace(s)` 来剔除字符串开头和结尾的空白符号；如果你想要剔除指定字符，则可以使用 `strings.Trim(s, \"cut\")` 来将开头和结尾的 `cut` 去除掉。该函数的第二个参数可以包含任何字符，如果你只想剔除开头或者结尾的字符串，则可以使用 `TrimLeft()` 或者 `TrimRight()` 来实现。\n\n## 4.7.9 分割字符串\n\n`strings.Fields(s)` 将会利用 1 个或多个空白符号来作为动态长度的分隔符将字符串分割成若干小块，并返回一个 slice，如果字符串只包含空白符号，则返回一个长度为 0 的 slice。\n\n`strings.Split(s, sep)` 用于自定义分割符号来对指定字符串进行分割，同样返回 slice。\n\n因为这 2 个函数都会返回 slice，所以习惯使用 for-range 循环来对其进行处理（第 7.3 节）。\n\n## 4.7.10 拼接 slice 到字符串\n\n`Join()` 用于将元素类型为 string 的 slice 使用分割符号来拼接组成一个字符串：\n\n```go\nstrings.Join(sl []string, sep string) string\n```\n\n示例 4.18 [strings_splitjoin.go](examples/chapter_4/strings_splitjoin.go)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc main() {\n\tstr := \"The quick brown fox jumps over the lazy dog\"\n\tsl := strings.Fields(str)\n\tfmt.Printf(\"Splitted in slice: %v\\n\", sl)\n\tfor _, val := range sl {\n\t\tfmt.Printf(\"%s - \", val)\n\t}\n\tfmt.Println()\n\tstr2 := \"GO1|The ABC of Go|25\"\n\tsl2 := strings.Split(str2, \"|\")\n\tfmt.Printf(\"Splitted in slice: %v\\n\", sl2)\n\tfor _, val := range sl2 {\n\t\tfmt.Printf(\"%s - \", val)\n\t}\n\tfmt.Println()\n\tstr3 := strings.Join(sl2,\";\")\n\tfmt.Printf(\"sl2 joined by ;: %s\\n\", str3)\n}\n```\n\n输出：\n\n\tSplitted in slice: [The quick brown fox jumps over the lazy dog]\n\tThe - quick - brown - fox - jumps - over - the - lazy - dog -\n\tSplitted in slice: [GO1 The ABC of Go 25]\n\tGO1 - The ABC of Go - 25 -\n\tsl2 joined by ;: GO1;The ABC of Go;25\n\n其它有关字符串操作的文档请参阅 [官方文档](http://golang.org/pkg/strings/)（ **译者注：国内用户可访问 [该页面](http://docs.studygolang.com/pkg/strings/)** ）。\n\n## 4.7.11 从字符串中读取内容\n\n函数 `strings.NewReader(str)` 用于生成一个 `Reader` 并读取字符串中的内容，然后返回指向该 `Reader` 的指针，从其它类型读取内容的函数还有：\n\n- `Read()` 从 `[]byte` 中读取内容。\n- `ReadByte()` 和 `ReadRune()` 从字符串中读取下一个 `byte` 或者 `rune`。\n\n## 4.7.12 字符串与其它类型的转换\n\n与字符串相关的类型转换都是通过 `strconv` 包实现的。\n\n该包包含了一些变量用于获取程序运行的操作系统平台下 `int` 类型所占的位数，如：`strconv.IntSize`。\n\n任何类型 **`T`** 转换为字符串总是成功的。\n\n针对从数字类型转换到字符串，Go 提供了以下函数：\n\n- `strconv.Itoa(i int) string` 返回数字 `i` 所表示的字符串类型的十进制数。\n- `strconv.FormatFloat(f float64, fmt byte, prec int, bitSize int) string` 将 64 位浮点型的数字转换为字符串，其中 `fmt` 表示格式（其值可以是 `'b'`、`'e'`、`'f'` 或 `'g'`），`prec` 表示精度，`bitSize` 则使用 32 表示 `float32`，用 64 表示 `float64`。\n\n将字符串转换为其它类型 **`tp`** 并不总是可能的，可能会在运行时抛出错误 `parsing \"…\": invalid argument`。\n\n针对从字符串类型转换为数字类型，Go 提供了以下函数：\n\n- `strconv.Atoi(s string) (i int, err error)` 将字符串转换为 `int` 型。\n- `strconv.ParseFloat(s string, bitSize int) (f float64, err error)` 将字符串转换为 `float64` 型。\n\n利用多返回值的特性，这些函数会返回 2 个值，第 1 个是转换后的结果（如果转换成功），第 2 个是可能出现的错误，因此，我们一般使用以下形式来进行从字符串到其它类型的转换：\n\n\tval, err = strconv.Atoi(s)\n\n在下面这个示例中，我们忽略可能出现的转换错误：\n\n示例 4.19 [string_conversion.go](examples/chapter_4/string_conversion.go)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n)\n\nfunc main() {\n\tvar orig string = \"666\"\n\tvar an int\n\tvar newS string\n\n\tfmt.Printf(\"The size of ints is: %d\\n\", strconv.IntSize)\t  \n\n\tan, _ = strconv.Atoi(orig)\n\tfmt.Printf(\"The integer is: %d\\n\", an) \n\tan = an + 5\n\tnewS = strconv.Itoa(an)\n\tfmt.Printf(\"The new string is: %s\\n\", newS)\n}\n```\n\n输出：\n\t\n\t64 位系统：\n\tThe size of ints is: 64\n\t32 位系统：\n\tThe size of ints is: 32\n\tThe integer is: 666\n\tThe new string is: 671\n\n在第 5.1 节，我们将会利用 `if` 语句来对可能出现的错误进行分类处理。\n\n更多有关该包的讨论，请参阅 [官方文档](http://golang.org/pkg/strconv/)（ **译者注：国内用户可访问 [该页面](http://docs.studygolang.com/pkg/strconv/)** ）。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[字符串](04.6.md)\n- 下一节：[时间和日期](04.8.md)\n"
  },
  {
    "path": "eBook/04.8.md",
    "content": "# 4.8 时间和日期\r\n\r\n`time` 包为我们提供了一个数据类型 `time.Time`（作为值使用）以及显示和测量时间和日期的功能函数。\r\n\r\n当前时间可以使用 `time.Now()` 获取，或者使用 `t.Day()`、`t.Minute()` 等等来获取时间的一部分；你甚至可以自定义时间格式化字符串，例如： `fmt.Printf(\"%02d.%02d.%4d\\n\", t.Day(), t.Month(), t.Year())` 将会输出 `21.07.2011`。\r\n\r\n`Duration` 类型表示两个连续时刻所相差的纳秒数，类型为 `int64`。`Location` 类型映射某个时区的时间，UTC 表示通用协调世界时间。\r\n\r\n包中的一个预定义函数 `func (t Time) Format(layout string) string` 可以根据一个格式化字符串来将一个时间 `t` 转换为相应格式的字符串，你可以使用一些预定义的格式，如：`time.ANSIC` 或 `time.RFC822`。 \r\n\r\n一般的格式化设计是通过对于一个标准时间的格式化描述来展现的，这听起来很奇怪（`02 Jan 2006 15:04` 是 Go 语言的诞生时间且自定义格式化时必须以此时间为基准），但看下面这个例子你就会一目了然：\r\n\r\n\r\n```go\r\nfmt.Println(t.Format(\"02 Jan 2006 15:04\")) \r\n```\r\n\r\n输出：\r\n\r\n\t21 Jul 2011 10:31\r\n\r\n其它有关时间操作的文档请参阅 [官方文档](http://golang.org/pkg/time/)（ **译者注：国内用户可访问 [该页面](http://docs.studygolang.com/pkg/time/)** ）。\r\n\r\n示例 4.20 [time.go](examples/chapter_4/time.go)\r\n\r\n```go\r\npackage main\r\nimport (\r\n\t\"fmt\"\r\n\t\"time\"\r\n)\r\n\r\nvar week time.Duration\r\nfunc main() {\r\n\tt := time.Now()\r\n\tfmt.Println(t) // e.g. Wed Dec 21 09:52:14 +0100 RST 2011\r\n\tfmt.Printf(\"%02d.%02d.%4d\\n\", t.Day(), t.Month(), t.Year())\r\n\t// 21.12.2011\r\n\tt = time.Now().UTC()\r\n\tfmt.Println(t) // Wed Dec 21 08:52:14 +0000 UTC 2011\r\n\tfmt.Println(time.Now()) // Wed Dec 21 09:52:14 +0100 RST 2011\r\n\t// calculating times:\r\n\tweek = 60 * 60 * 24 * 7 * 1e9 // must be in nanosec\r\n\tweek_from_now := t.Add(time.Duration(week))\r\n\tfmt.Println(week_from_now) // Wed Dec 28 08:52:14 +0000 UTC 2011\r\n\t// formatting times:\r\n\tfmt.Println(t.Format(time.RFC822)) // 21 Dec 11 0852 UTC\r\n\tfmt.Println(t.Format(time.ANSIC)) // Wed Dec 21 08:56:34 2011\r\n\t// The time must be 2006-01-02 15:04:05\r\n\tfmt.Println(t.Format(\"02 Jan 2006 15:04\")) // 21 Dec 2011 08:52\r\n\ts := t.Format(\"20060102\")\r\n\tfmt.Println(t, \"=>\", s)\r\n\t// Wed Dec 21 08:52:14 +0000 UTC 2011 => 20111221\r\n}\r\n```\r\n\r\n输出的结果已经写在每行 `//` 的后面。\r\n\r\n如果你需要在应用程序在经过一定时间或周期执行某项任务（事件处理的特例），则可以使用 `time.After()` 或者 `time.Ticker`：我们将会在 [第 14.5 节](14.5.md) 讨论这些有趣的事情。 另外，`time.Sleep(d Duration)` 可以实现对某个进程（实质上是 goroutine）时长为 `d` 的暂停。\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[strings 和 strconv 包](04.7.md)\r\n- 下一节：[指针](04.9.md)\r\n"
  },
  {
    "path": "eBook/04.9.md",
    "content": "# 4.9 指针\r\n\r\n不像 Java 和 .NET，Go 语言为程序员提供了控制数据结构的指针的能力；但是，你不能进行指针运算。通过给予程序员基本内存布局，Go 语言允许你控制特定集合的数据结构、分配的数量以及内存访问模式，这些对构建运行良好的系统是非常重要的：指针对于性能的影响是不言而喻的，而如果你想要做的是系统编程、操作系统或者网络应用，指针更是不可或缺的一部分。\r\n\r\n由于各种原因，指针对于使用面向对象编程的现代程序员来说可能显得有些陌生，不过我们将会在这一小节对此进行解释，并在未来的章节中展开深入讨论。\r\n\r\n程序在内存中存储它的值，每个内存块（或字）有一个地址，通常用十六进制数表示，如：`0x6b0820` 或 `0xf84001d7f0`。\r\n\r\nGo 语言的取地址符是 `&`，放到一个变量前使用就会返回相应变量的内存地址。\r\n\r\n下面的代码片段（示例 4.9 [pointer.go](examples/chapter_4/pointer.go)）可能输出 `An integer: 5, its location in memory: 0x6b0820`（这个值随着你每次运行程序而变化）。\r\n\r\n```go\r\nvar i1 = 5\r\nfmt.Printf(\"An integer: %d, it's location in memory: %p\\n\", i1, &i1)\r\n```\r\n\r\n这个地址可以存储在一个叫做指针的特殊数据类型中，在本例中这是一个指向 int 的指针，即 `i1`：此处使用 `*int` 表示。如果我们想调用指针 `intP`，我们可以这样声明它：\r\n\r\n```go\r\nvar intP *int\r\n```\r\n\r\n然后使用 `intP = &i1` 是合法的，此时 `intP` 指向 `i1`。\r\n\r\n（指针的格式化标识符为 `%p`）\r\n\r\n`intP` 存储了 `i1` 的内存地址；它指向了 `i1` 的位置，它引用了变量 `i1`。\r\n\r\n**一个指针变量可以指向任何一个值的内存地址** 它指向那个值的内存地址，在 32 位机器上占用 4 个字节，在 64 位机器上占用 8 个字节，并且与它所指向的值的大小无关。当然，可以声明指针指向任何类型的值来表明它的原始性或结构性；你可以在指针类型前面加上 `*` 号（前缀）来获取指针所指向的内容，这里的 `*` 号是一个类型更改器。使用一个指针引用一个值被称为间接引用。\r\n\r\n当一个指针被定义后没有分配到任何变量时，它的值为 `nil`。\r\n\r\n一个指针变量通常缩写为 `ptr`。\r\n\r\n**注意事项**\r\n\r\n在书写表达式类似 `var p *type` 时，切记在 * 号和指针名称间留有一个空格，因为 `- var p*type` 是语法正确的，但是在更复杂的表达式中，它容易被误认为是一个乘法表达式！\r\n\r\n符号 * 可以放在一个指针前，如 `*intP`，那么它将得到这个指针指向地址上所存储的值；这被称为反引用（或者内容或者间接引用）操作符；另一种说法是指针转移。\r\n\r\n对于任何一个变量 `var`， 如下表达式都是正确的：`var == *(&var)`。\r\n\r\n现在，我们应当能理解 pointer.go 的全部内容及其输出：\r\n\r\n示例 4.21 [pointer.go](examples/chapter_4/pointer.go):\r\n\r\n```go\r\npackage main\r\nimport \"fmt\"\r\nfunc main() {\r\n\tvar i1 = 5\r\n\tfmt.Printf(\"An integer: %d, its location in memory: %p\\n\", i1, &i1)\r\n\tvar intP *int\r\n\tintP = &i1\r\n\tfmt.Printf(\"The value at memory location %p is %d\\n\", intP, *intP)\r\n}\r\n```\r\n\r\n输出：\r\n\r\n\tAn integer: 5, its location in memory: 0x24f0820\r\n\tThe value at memory location 0x24f0820 is 5\r\n\r\n我们可以用下图来表示内存使用的情况：\r\n\r\n<img src=\"images/4.9_fig4.4.png?raw=true\" style=\"zoom:67%;\" />\r\n\r\n程序 string_pointer.go 为我们展示了指针对 `string` 的例子。\r\n\r\n它展示了分配一个新的值给 `*p` 并且更改这个变量自己的值（这里是一个字符串）。\r\n\r\n示例 4.22 [string_pointer.go](examples/chapter_4/string_pointer.go)\r\n\r\n```go\r\npackage main\r\nimport \"fmt\"\r\nfunc main() {\r\n\ts := \"good bye\"\r\n\tvar p *string = &s\r\n\t*p = \"ciao\"\r\n\tfmt.Printf(\"Here is the pointer p: %p\\n\", p) // prints address\r\n\tfmt.Printf(\"Here is the string *p: %s\\n\", *p) // prints string\r\n\tfmt.Printf(\"Here is the string s: %s\\n\", s) // prints same string\r\n}\r\n```\r\n\r\n输出：\r\n\r\n\tHere is the pointer p: 0x2540820\r\n\tHere is the string *p: ciao\r\n\tHere is the string s: ciao\r\n\r\n通过对 `*p` 赋另一个值来更改“对象”，这样 `s` 也会随之更改。\r\n\r\n内存示意图如下：\r\n\r\n<img src=\"images/4.9_fig4.5.png?raw=true\" style=\"zoom:67%;\" />\r\n\r\n**注意事项** \r\n\r\n你不能获取字面量或常量的地址，例如：\r\n\r\n```go\r\nconst i = 5\r\nptr := &i //error: cannot take the address of i\r\nptr2 := &10 //error: cannot take the address of 10\r\n```\r\n\r\n所以说，Go 语言和 C、C++ 以及 D 语言这些低级（系统）语言一样，都有指针的概念。但是对于经常导致 C 语言内存泄漏继而程序崩溃的指针运算（所谓的指针算法，如：`pointer+2`，移动指针指向字符串的字节数或数组的某个位置）是不被允许的。Go 语言中的指针保证了内存安全，更像是 Java、C# 和 VB.NET 中的引用。\r\n\r\n因此 `p++` 在 Go 语言的代码中是不合法的。\r\n\r\n指针的一个高级应用是你可以传递一个变量的引用（如函数的参数），这样不会传递变量的拷贝。指针传递是很廉价的，只占用 4 个或 8 个字节。当程序在工作中需要占用大量的内存，或很多变量，或者两者都有，使用指针会减少内存占用和提高效率。被指向的变量也保存在内存中，直到没有任何指针指向它们，所以从它们被创建开始就具有相互独立的生命周期。\r\n\r\n另一方面（虽然不太可能），由于一个指针导致的间接引用（一个进程执行了另一个地址），指针的过度频繁使用也会导致性能下降。\r\n\r\n指针也可以指向另一个指针，并且可以进行任意深度的嵌套，导致你可以有多级的间接引用，但在大多数情况这会使你的代码结构不清晰。\r\n\r\n如我们所见，在大多数情况下 Go 语言可以使程序员轻松创建指针，并且隐藏间接引用，如：自动反向引用。\r\n\r\n对一个空指针的反向引用是不合法的，并且会使程序崩溃：\r\n\r\n示例 4.23 [testcrash.go](examples/chapter_4/testcrash.go):\r\n\r\n```go\r\npackage main\r\nfunc main() {\r\n\tvar p *int = nil\r\n\t*p = 0\r\n}\r\n// in Windows: stops only with: <exit code=\"-1073741819\" msg=\"process crashed\"/>\r\n// runtime error: invalid memory address or nil pointer dereference\r\n```\r\n\r\n**问题 4.2** 列举 Go 语言中 `*` 号的所有用法。\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[时间和日期](04.8.md)\r\n- 下一节：[控制结构](05.0.md)\r\n"
  },
  {
    "path": "eBook/05.0.md",
    "content": "# 5.0 控制结构\n\n到目前为止，我们看到的 Go 程序都是从 `main()` 函数开始执行，然后按顺序执行该函数体中的代码。但我们经常会需要只有在满足一些特定情况时才执行某些代码，也就是说在代码里进行条件判断。针对这种需求，Go 提供了下面这些条件结构和分支结构：\n\n- `if`-`else` 结构\n- `switch` 结构\n- `select` 结构，用于 channel 的选择（[第 14.4 节](14.4.md)）\n\n可以使用迭代或循环结构来重复执行一次或多次某段代码（任务）：\n\n- `for` (`range`) 结构\n\n一些如 `break` 和 `continue` 这样的关键字可以用于中途改变循环的状态。\n\n此外，你还可以使用 `return` 来结束某个函数的执行，或使用 `goto` 和标签来调整程序的执行位置。\n\nGo 完全省略了 `if`、`switch` 和 `for` 结构中条件语句两侧的括号，相比 Java、C++ 和 C# 中减少了很多视觉混乱的因素，同时也使你的代码更加简洁。\n\n## 链接\n\n- [目录](directory.md)\n- 上一章：[指针](04.9.md)\n- 下一节：[if-else 结构](05.1.md)\n"
  },
  {
    "path": "eBook/05.1.md",
    "content": "# 5.1 if-else 结构\n\nif 是用于测试某个条件（布尔型或逻辑型）的语句，如果该条件成立，则会执行 if 后由大括号括起来的代码块，否则就忽略该代码块继续执行后续的代码。\n\n```go\nif condition {\n\t// do something\t\n}\n```\n\n如果存在第二个分支，则可以在上面代码的基础上添加 `else` 关键字以及另一代码块，这个代码块中的代码只有在条件不满足时才会执行。`if` 和 `else` 后的两个代码块是相互独立的分支，只可能执行其中一个。\n\n```go\nif condition {\n\t// do something\t\n} else {\n\t// do something\t\n}\n```\n\n如果存在第三个分支，则可以使用下面这种三个独立分支的形式：\n\n```go\nif condition1 {\n\t// do something\t\n} else if condition2 {\n\t// do something else\t\n} else {\n\t// catch-all or default\n}\n```\n\nelse-if 分支的数量是没有限制的，但是为了代码的可读性，还是不要在 `if` 后面加入太多的 else-if 结构。如果你必须使用这种形式，则把尽可能先满足的条件放在前面。\n\n即使当代码块之间只有一条语句时，大括号也不可被省略（尽管有些人并不赞成，但这还是符合了软件工程原则的主流做法）。\n\n关键字 `if` 和 `else` 之后的左大括号 `{` 必须和关键字在同一行，如果你使用了 else-if 结构，则前段代码块的右大括号 `}` 必须和 else-if 关键字在同一行。这两条规则都是被编译器强制规定的。\n\n非法的 Go 代码:\n\n```go\nif x{\n}\nelse {\t// 无效的\n}\n```\n\n要注意的是，在你使用 `gofmt` 格式化代码之后，每个分支内的代码都会缩进 4 个或 8 个空格，或者是 1 个 tab，并且右大括号与对应的 `if` 关键字垂直对齐。\n\n在有些情况下，条件语句两侧的括号是可以被省略的；当条件比较复杂时，则可以使用括号让代码更易读。条件允许是符合条件，需使用 `&&`、`||` 或 `!`，你可以使用括号来提升某个表达式的运算优先级，并提高代码的可读性。\n\n一种可能用到条件语句的场景是测试变量的值，在不同的情况执行不同的语句，不过将在第 5.3 节讲到的 switch 结构会更适合这种情况。\n\n示例 5.1 [booleans.go](examples/chapter_5/booleans.go)\n\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tbool1 := true\n\tif bool1 {\n\t\tfmt.Printf(\"The value is true\\n\")\n\t} else {\n\t\tfmt.Printf(\"The value is false\\n\")\n\t}\n}\n```\n\n输出：\n\t\n\tThe value is true\n\n**注意事项** 这里不需要使用 `if bool1 == true` 来判断，因为 `bool1` 本身已经是一个布尔类型的值。\n\n这种做法一般都用在测试 `true` 或者有利条件时，但你也可以使用取反 `!` 来判断值的相反结果，如：`if !bool1` 或者 `if !(condition)`。后者的括号大多数情况下是必须的，如这种情况：`if !(var1 == var2)`。\n\n当 if 结构内有 `break`、`continue`、`goto` 或者 `return` 语句时，Go 代码的常见写法是省略 `else` 部分（另见[第 5.2 节](05.2.md)）。无论满足哪个条件都会返回 `x` 或者 `y` 时，一般使用以下写法：\n\n```go\nif condition {\n\treturn x\n}\nreturn y\n```\n\n**注意事项** 不要同时在 if-else 结构的两个分支里都使用 `return` 语句，这将导致编译报错 `function ends without a return statement`（你可以认为这是一个编译器的 Bug 或者特性）。（ **译者注：该问题已经在 Go 1.1 中被修复或者说改进** ）\n\n这里举一些有用的例子：\n\n1. 判断一个字符串是否为空：\n\t- `if str == \"\" { ... }`\n\t- `if len(str) == 0 {...}`\t\n2. 判断运行 Go 程序的操作系统类型，这可以通过常量 `runtime.GOOS` 来判断（[第 2.2 节](02.2.md)）。\n\t\n\t```go\n\tif runtime.GOOS == \"windows\"\t {\n\t\t.\t..\n\t} else { // Unix-like\n\t\t.\t..\n\t}\n\t```\n\n\t这段代码一般被放在 `init()` 函数中执行。这儿还有一段示例来演示如何根据操作系统来决定输入结束的提示：\n\t\n\t```go\n\tvar prompt = \"Enter a digit, e.g. 3 \"+ \"or %s to quit.\"\n\t\n\tfunc init() {\n\t\tif runtime.GOOS == \"windows\" {\n\t\t\tprompt = fmt.Sprintf(prompt, \"Ctrl+Z, Enter\")\t\t\n\t\t} else { //Unix-like\n\t\t\tprompt = fmt.Sprintf(prompt, \"Ctrl+D\")\n\t\t}\n\t}\n\t```\n\t\n3. 函数 `Abs()` 用于返回一个整型数字的绝对值:\n\n\t```go\n\tfunc Abs(x int) int {\n\tif x < 0 {\n\t\treturn -x\n\t}\n\treturn x\t\n\t}\n\t```\n\n4. `isGreater` 用于比较两个整型数字的大小:\n\n\t```go\n\tfunc isGreater(x, y int) bool {\n\t\tif x > y {\n\t\t\treturn true\t\n\t\t}\n\t\treturn false\n\t}\n\t```\n\n在第四种情况中，`if` 可以包含一个初始化语句（如：给一个变量赋值）。这种写法具有固定的格式（在初始化语句后方必须加上分号）：\n\n```go\nif initialization; condition {\n\t// do something\n}\n```\n\n例如:\n\n```go\nval := 10\nif val > max {\n\t// do something\n}\n```\n\n你也可以这样写:\n\n```go\nif val := 10; val > max {\n\t// do something\n}\n```\n\n但要注意的是，使用简短方式 `:=` 声明的变量的作用域只存在于 `if` 结构中（在 `if` 结构的大括号之间，如果使用 if-else 结构则在 `else` 代码块中变量也会存在）。如果变量在 `if` 结构之前就已经存在，那么在 `if` 结构中，该变量原来的值会被隐藏。最简单的解决方案就是不要在初始化语句中声明变量（见[5.2 节的例 3](05.2.md) 了解更多)。\n\n示例 5.2 [ifelse.go](examples/chapter_5/ifelse.go)\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar first int = 10\n\tvar cond int\n\n\tif first <= 0 {\n\t\tfmt.Printf(\"first is less than or equal to 0\\n\")\n\t} else if first > 0 && first < 5 {\n\t\tfmt.Printf(\"first is between 0 and 5\\n\")\n\t} else {\n\t\tfmt.Printf(\"first is 5 or greater\\n\")\n\t}\n\tif cond = 5; cond > 10 {\n\t\tfmt.Printf(\"cond is greater than 10\\n\")\n\t} else {\n\t\tfmt.Printf(\"cond is not greater than 10\\n\")\n\t}\n}\n```\n\n输出：\n\n\tfirst is 5 or greater\n\tcond is not greater than 10\n\n下面的代码片段展示了如何通过在初始化语句中获取函数 `process()` 的返回值，并在条件语句中作为判定条件来决定是否执行 `if` 结构中的代码：\n\n```go\nif value := process(data); value > max {\n\t...\n}\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[控制结构](05.0.md)\n- 下一节：[测试多返回值函数的错误](05.2.md)\n"
  },
  {
    "path": "eBook/05.2.md",
    "content": "# 5.2 测试多返回值函数的错误\n\nGo 语言的函数经常使用两个返回值来表示执行是否成功：返回某个值以及 `true` 表示成功；返回零值（或 `nil`）和 `false` 表示失败（[第 4.4 节](04.4.md)）。当不使用 `true` 或 `false` 的时候，也可以使用一个 `error` 类型的变量来代替作为第二个返回值：成功执行的话，`error` 的值为 `nil`，否则就会包含相应的错误信息（Go 语言中的错误类型为 `error`: `var err error`，我们将会在[第 13 章](13.0.md) 进行更多地讨论）。这样一来，就很明显需要用一个 `if` 语句来测试执行结果；由于其符号的原因，这样的形式又称之为“逗号 ok 模式”(comma, ok pattern)。\n\n在[第 4.7 节](04.7.md) 的程序 [string_conversion.go](examples/chapter_4/string_conversion.go) 中，函数 `strconv.Atoi()` 的作用是将一个字符串转换为一个整数。之前我们忽略了相关的错误检查：\n\n```go\nanInt, _ = strconv.Atoi(origStr)\n```\n\n如果 `origStr` 不能被转换为整数，`anInt` 的值会变成 `0` 而 `_` 无视了错误，程序会继续运行。\n\n这样做是非常不好的：程序应该在最接近的位置检查所有相关的错误，至少需要暗示用户有错误发生并对函数进行返回，甚至中断程序。\n\n我们在第二个版本中对代码进行了改进：\n\n\n示例 1：\n\n示例 5.3 [string_conversion2.go](examples/chapter_5/string_conversion2.go)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n)\n\nfunc main() {\n\tvar orig string = \"ABC\"\n\t// var an int\n\tvar newS string\n\t// var err error\n\n\tfmt.Printf(\"The size of ints is: %d\\n\", strconv.IntSize)\t  \n\t// anInt, err = strconv.Atoi(origStr)\n\tan, err := strconv.Atoi(orig)\n\tif err != nil {\n\t\tfmt.Printf(\"orig %s is not an integer - exiting with error\\n\", orig)\n\t\treturn\n\t} \n\tfmt.Printf(\"The integer is %d\\n\", an)\n\tan = an + 5\n\tnewS = strconv.Itoa(an)\n\tfmt.Printf(\"The new string is: %s\\n\", newS)\n}\n```\n\n这是测试 `err` 变量是否包含一个真正的错误（`if err != nil`）的习惯用法。如果确实存在错误，则会打印相应的错误信息然后通过 `return` 提前结束函数的执行。我们还可以使用携带返回值的 `return` 形式，例如 `return err`。这样一来，函数的调用者就可以检查函数执行过程中是否存在错误了。\n\n**习惯用法**\n\n```go\nvalue, err := pack1.Function1(param1)\nif err != nil {\n\tfmt.Printf(\"An error occured in pack1.Function1 with parameter %v\", param1)\n\treturn err\n}\n// 未发生错误，继续执行：\n```\n\n由于本例的函数调用者属于 `main` 函数，所以程序会直接停止运行。\n\n如果我们想要在错误发生的同时终止程序的运行，我们可以使用 `os` 包的 `Exit` 函数：\n\n**习惯用法**\n\n```go\nif err != nil {\n\tfmt.Printf(\"Program stopping with error %v\", err)\n\tos.Exit(1)\n}\n```\n\n（此处的退出代码 `1` 可以使用外部脚本获取到）\n\n有时候，你会发现这种习惯用法被连续重复地使用在某段代码中。\n\n当没有错误发生时，代码继续运行就是唯一要做的事情，所以 `if` 语句块后面不需要使用 `else` 分支。\n\n示例 2：我们尝试通过 `os.Open` 方法打开一个名为 `name` 的只读文件：\n\n```go\nf, err := os.Open(name)\nif err != nil {\n\treturn err\n}\ndoSomething(f) // 当没有错误发生时，文件对象被传入到某个函数中\ndoSomething\n```\n\n**练习 5.1** 尝试改写 [string_conversion2.go](examples/chapter_5/string_conversion2.go) 中的代码，要求使用 `:=` 方法来对 `err` 进行赋值，哪些地方可以被修改？\n\n示例 3：可以将错误的获取放置在 `if` 语句的初始化部分：\n\n**习惯用法**\n\n```go\nif err := file.Chmod(0664); err != nil {\n\tfmt.Println(err)\n\treturn err\n}\n```\n\n示例 4：或者将 ok-pattern 的获取放置在 `if` 语句的初始化部分，然后进行判断：\n\n**习惯用法**\n\n```go\nif value, ok := readData(); ok {\n…\n}\n```\n\n**注意事项**\n\n如果您像下面一样，没有为多返回值的函数准备足够的变量来存放结果：\n```go\nfunc mySqrt(f float64) (v float64, ok bool) {\n\tif f < 0 { return } // error case\n\treturn math.Sqrt(f),true\n}\n\nfunc main() {\n\tt := mySqrt(25.0)\n\tfmt.Println(t)\n}\n```\n\n您会得到一个编译错误：`multiple-value mySqrt() in single-value context`。\n\n正确的做法是：\n\n```go\nt, ok := mySqrt(25.0)\nif ok { fmt.Println(t) }\n```\n\n**注意事项 2**\n\n当您将字符串转换为整数时，且确定转换一定能够成功时，可以将 `Atoi()` 函数进行一层忽略错误的封装：\n\n```go\nfunc atoi (s string) (n int) {\n\tn, _ = strconv.Atoi(s)\n\treturn\n}\n```\n\n实际上，`fmt` 包（[第 4.4.3 节](04.4.md)）最简单的打印函数也有 2 个返回值：\n\n```go\ncount, err := fmt.Println(x) // number of bytes printed, nil or 0, error\n```\n\n当打印到控制台时，可以将该函数返回的错误忽略；但当输出到文件流、网络流等具有不确定因素的输出对象时，应该始终检查是否有错误发生（另见[练习 6.1b](06.1.md)）。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[if-else 结构](05.1.md)\n- 下一节：[switch 结构](05.3.md)\n"
  },
  {
    "path": "eBook/05.3.md",
    "content": "# 5.3 switch 结构\n\n相比较 C 和 Java 等其它语言而言，Go 语言中的 `switch` 结构使用上更加灵活。它接受任意形式的表达式：\n\n```go\nswitch var1 {\n\tcase val1:\n\t\t...\n\tcase val2:\n\t\t...\n\tdefault:\n\t\t...\n}\n```\n\n变量 `var1` 可以是任何类型，而 `val1` 和 `val2` 则可以是同类型的任意值。类型不被局限于常量或整数，但必须是相同的类型；或者最终结果为相同类型的表达式。前花括号 `{` 必须和 `switch` 关键字在同一行。\n\n您可以同时测试多个可能符合条件的值，使用逗号分割它们，例如：`case val1, val2, val3`。\n\n每一个 `case` 分支都是唯一的，从上至下逐一测试，直到匹配为止。（ Go 语言使用快速的查找算法来测试 `switch` 条件与 `case` 分支的匹配情况，直到算法匹配到某个 `case` 或者进入 `default` 条件为止。）\n\n一旦成功地匹配到某个分支，在执行完相应代码后就会退出整个 `switch` 代码块，也就是说您不需要特别使用 `break` 语句来表示结束。\n\n因此，程序也不会自动地去执行下一个分支的代码。如果在执行完每个分支的代码后，还希望继续执行后续分支的代码，可以使用 `fallthrough` 关键字来达到目的。\n\n因此：\n\n```go\nswitch i {\n\tcase 0: // 空分支，只有当 i == 0 时才会进入分支\n\tcase 1:\n\t\tf() // 当 i == 0 时函数不会被调用\n}\n```\n\n并且：\n\n```go\nswitch i {\n\tcase 0: fallthrough\n\tcase 1:\n\t\tf() // 当 i == 0 时函数也会被调用\n}\n```\n\n在 `case ...:` 语句之后，您不需要使用花括号将多行语句括起来，但您可以在分支中进行任意形式的编码。当代码块只有一行时，可以直接放置在 `case` 语句之后。\n\n您同样可以使用 `return` 语句来提前结束代码块的执行。当您在 `switch` 语句块中使用 `return` 语句，并且您的函数是有返回值的，您还需要在 switch 之后添加相应的 `return` 语句以确保函数始终会返回。\n\n可选的 `default` 分支可以出现在任何顺序，但最好将它放在最后。它的作用类似与 if-else 语句中的 `else`，表示不符合任何已给出条件时，执行相关语句。\n\n示例 5.4 [switch1.go](examples/chapter_5/switch1.go)：\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar num1 int = 100\n\n\tswitch num1 {\n\tcase 98, 99:\n\t\tfmt.Println(\"It's equal to 98\")\n\tcase 100: \n\t\tfmt.Println(\"It's equal to 100\")\n\tdefault:\n\t\tfmt.Println(\"It's not equal to 98 or 100\")\n\t}\n}\n\n```\n\n输出：\n\n\tIt's equal to 100\n\n在第 12.1 节，我们会使用 `switch` 语句判断从键盘输入的字符（详见[第 12.2 节](12.2.md) 的 [switch.go](./examples/chapter_12/switch.go)）。`switch` 语句的第二种形式是不提供任何被判断的值（实际上默认为判断是否为 `true`），然后在每个 `case` 分支中进行测试不同的条件。当任一分支的测试结果为 `true` 时，该分支的代码会被执行。这看起来非常像链式的 if-else 语句，但是在测试条件非常多的情况下，提供了可读性更好的书写方式。\n\n```go\nswitch {\n\tcase condition1:\n\t\t...\n\tcase condition2:\n\t\t...\n\tdefault:\n\t\t...\n}\n```\n\n例如：\n\n```go\nswitch {\n\tcase i < 0:\n\t\tf1()\n\tcase i == 0:\n\t\tf2()\n\tcase i > 0:\n\t\tf3()\n}\n```\n\n任何支持进行相等判断的类型都可以作为测试表达式的条件，包括 `int`、`string`、指针等。\n\n示例 5.4 [switch2.go](examples/chapter_5/switch2.go)：\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar num1 int = 7\n\n\tswitch {\n\t    case num1 < 0:\n\t\t    fmt.Println(\"Number is negative\")\n\t    case num1 > 0 && num1 < 10:\n\t\t    fmt.Println(\"Number is between 0 and 10\")\n\t    default:\n\t\t    fmt.Println(\"Number is 10 or greater\")\n\t}\n}\n```\n\n输出：\n\n\tNumber is between 0 and 10\n\nswitch 语句的第三种形式是包含一个初始化语句：\n\n```go\nswitch initialization {\n\tcase val1:\n\t\t...\n\tcase val2:\n\t\t...\n\tdefault:\n\t\t...\n}\n```\n\n这种形式可以非常优雅地进行条件判断：\n\n```go\nswitch result := calculate(); {\n\tcase result < 0:\n\t\t...\n\tcase result > 0:\n\t\t...\n\tdefault:\n\t\t// 0\n}\n```\n\n在下面这个代码片段中，变量 `a` 和 `b` 被平行初始化，然后作为判断条件：\n\n```go\nswitch a, b := x[i], y[j]; {\n\tcase a < b: t = -1\n\tcase a == b: t = 0\n\tcase a > b: t = 1\n}\n```\n\n`switch` 语句还可以被用于 type-switch（详见[第 11.4 节](11.4.md)）来判断某个 `interface` 变量中实际存储的变量类型。\n\n**问题 5.1：**\n\n请说出下面代码片段输出的结果：\n\n```go\n\tk := 6\n\tswitch k {\n\tcase 4:\n\t\tfmt.Println(\"was <= 4\")\n\t\tfallthrough\n\tcase 5:\n\t\tfmt.Println(\"was <= 5\")\n\t\tfallthrough\n\tcase 6:\n\t\tfmt.Println(\"was <= 6\")\n\t\tfallthrough\n\tcase 7:\n\t\tfmt.Println(\"was <= 7\")\n\t\tfallthrough\n\tcase 8:\n\t\tfmt.Println(\"was <= 8\")\n\t\tfallthrough\n\tdefault:\n\t\tfmt.Println(\"default case\")\n\t}\n```\n\n**练习 5.2：** [season.go](exercises/chapter_5/season.go)：\n\n写一个 `Season()` 函数，要求接受一个代表月份的数字，然后返回所代表月份所在季节的名称（不用考虑月份的日期）。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[测试多返回值函数的错误](05.2.md)\n- 下一节：[for 结构](05.4.md)\n"
  },
  {
    "path": "eBook/05.4.md",
    "content": "# 5.4 for 结构\n\n如果想要重复执行某些语句，Go 语言中您只有 `for` 结构可以使用。不要小看它，这个 `for` 结构比其它语言中的更为灵活。\n\n**注意事项** 其它许多语言中也没有发现和 do-while 完全对等的 `for` 结构，可能是因为这种需求并不是那么强烈。\n\n## 5.4.1 基于计数器的迭代\n\n文件 for1.go 中演示了最简单的基于计数器的迭代，基本形式为：\n\n\tfor 初始化语句; 条件语句; 修饰语句 {}\n\n示例 5.6 [for1.go](examples/chapter_5/for1.go)：\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tfor i := 0; i < 5; i++ {\n\t\tfmt.Printf(\"This is the %d iteration\\n\", i)\n\t}\n}\n```\n\n输出：\n\n\tThis is the 0 iteration\n\tThis is the 1 iteration\n\tThis is the 2 iteration\n\tThis is the 3 iteration\n\tThis is the 4 iteration\n\n由花括号括起来的代码块会被重复执行已知次数，该次数是根据计数器（此例为 `i`）决定的。循环开始前，会执行且仅会执行一次初始化语句 `i := 0;`；这比在循环之前声明更为简短。紧接着的是条件语句 `i < 5;`，在每次循环开始前都会进行判断，一旦判断结果为 `false`，则退出循环体。最后一部分为修饰语句 `i++`，一般用于增加或减少计数器。\n\n这三部分组成的循环的头部，它们之间使用分号 `;` 相隔，但并不需要括号 `()` 将它们括起来。例如：`for (i = 0; i < 10; i++) { }`，这是无效的代码！\n\n同样的，左花括号 `{` 必须和 for 语句在同一行，计数器的生命周期在遇到右花括号 `}` 时便终止。一般习惯使用 i、j、z 或 ix 等较短的名称命名计数器。\n\n特别注意，永远不要在循环体内修改计数器，这在任何语言中都是非常差的实践！\n\n您还可以在循环中同时使用多个计数器：\n\n```go\nfor i, j := 0, N; i < j; i, j = i+1, j-1 {}\n```\n\n这得益于 Go 语言具有的平行赋值的特性（可以查看[第 7 章](07.0.md) [string_reverse.go](./examples/chapter_7/string_reverse.go) 中反转数组的示例）。\n\n您可以将两个 for 循环嵌套起来：\n\n```go\nfor i:=0; i<5; i++ {\n\tfor j:=0; j<10; j++ {\n\t\tprintln(j)\n\t}\n}\n```\n\n如果您使用 for 循环迭代一个 Unicode 编码的字符串，会发生什么？\n\n示例 5.7 [for_string.go](examples/chapter_5/for_string.go)：\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tstr := \"Go is a beautiful language!\"\n\tfmt.Printf(\"The length of str is: %d\\n\", len(str))\n\tfor ix :=0; ix < len(str); ix++ {\n\t\tfmt.Printf(\"Character on position %d is: %c \\n\", ix, str[ix])\n\t}\n\tstr2 := \"日本語\"\n\tfmt.Printf(\"The length of str2 is: %d\\n\", len(str2))\n\tfor ix :=0; ix < len(str2); ix++ {\n\t\tfmt.Printf(\"Character on position %d is: %c \\n\", ix, str2[ix])\n\t}\n}\n```\n\n输出：\n\n\tThe length of str is: 27\n\tCharacter on position 0 is: G \n\tCharacter on position 1 is: o \n\tCharacter on position 2 is:   \n\tCharacter on position 3 is: i \n\tCharacter on position 4 is: s \n\tCharacter on position 5 is:   \n\tCharacter on position 6 is: a \n\tCharacter on position 7 is:   \n\tCharacter on position 8 is: b \n\tCharacter on position 9 is: e \n\tCharacter on position 10 is: a \n\tCharacter on position 11 is: u \n\tCharacter on position 12 is: t \n\tCharacter on position 13 is: i \n\tCharacter on position 14 is: f \n\tCharacter on position 15 is: u \n\tCharacter on position 16 is: l \n\tCharacter on position 17 is:   \n\tCharacter on position 18 is: l \n\tCharacter on position 19 is: a \n\tCharacter on position 20 is: n \n\tCharacter on position 21 is: g \n\tCharacter on position 22 is: u \n\tCharacter on position 23 is: a \n\tCharacter on position 24 is: g \n\tCharacter on position 25 is: e \n\tCharacter on position 26 is: ! \n\tThe length of str2 is: 9\n\tCharacter on position 0 is: æ \n\tCharacter on position 1 is:  \n\tCharacter on position 2 is: ¥ \n\tCharacter on position 3 is: æ \n\tCharacter on position 4 is:  \n\tCharacter on position 5 is: ¬ \n\tCharacter on position 6 is: è \n\tCharacter on position 7 is: ª \n\tCharacter on position 8 is:  \n\n如果我们打印 `str` 和 `str2` 的长度，会分别得到 `27` 和 `9`。\n\n由此我们可以发现，ASCII 编码的字符占用 1 个字节，既每个索引都指向不同的字符，而非 ASCII 编码的字符（占有 2 到 4 个字节）不能单纯地使用索引来判断是否为同一个字符。我们会在[第 5.4.4 节](05.4.md) 解决这个问题。\n\n### 练习题\n\n**练习 5.4** [for_loop.go](exercises/chapter_5/for_loop.go)\n\n1. 使用 `for` 结构创建一个简单的循环。要求循环 15 次然后使用 `fmt` 包来打印计数器的值。\n2. 使用 `goto` 语句重写循环，要求不能使用 `for` 关键字。\n\n**练习 5.5** [for_character.go](exercises/chapter_5/for_character.go)\n\n创建一个程序，要求能够打印类似下面的结果（尾行达 25 个字符为止）：\n\n\tG\n\tGG\n\tGGG\n\tGGGG\n\tGGGGG\n\tGGGGGG\n\n1. 使用 2 层嵌套 for 循环。\n2. 仅用 1 层 for 循环以及字符串连接。\n\n**练习 5.6** [bitwise_complement.go](exercises/chapter_5/bitwise_complement.go)\n\n使用按位补码从 0 到 10，使用位表达式 `%b` 来格式化输出。\n\n**练习 5.7** Fizz-Buzz 问题：[fizzbuzz.go](exercises/chapter_5/fizzbuzz.go)\n\n写一个从 1 打印到 100 的程序，但是每当遇到 3 的倍数时，不打印相应的数字，但打印一次 \"Fizz\"。遇到 5 的倍数时，打印 `Buzz` 而不是相应的数字。对于同时为 3 和 5 的倍数的数，打印 `FizzBuzz`（提示：使用 switch 语句）。\n\n**练习 5.8** [rectangle_stars.go](exercises/chapter_5/rectangle_stars.go)\n\n使用 `*` 符号打印宽为 20，高为 10 的矩形。\n\n## 5.4.2 基于条件判断的迭代\n\nfor 结构的第二种形式是没有头部的条件判断迭代（类似其它语言中的 while 循环），基本形式为：`for 条件语句 {}`。\n\n您也可以认为这是没有初始化语句和修饰语句的 for 结构，因此 `;;` 便是多余的了。\n\nListing 5.8 [for2.go](examples/chapter_5/for2.go)：\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar i int = 5\n\n\tfor i >= 0 {\n\t\ti = i - 1\n\t\tfmt.Printf(\"The variable i is now: %d\\n\", i)\n\t}\n}\n```\n\n输出：\n\n\tThe variable i is now: 4\n\tThe variable i is now: 3\n\tThe variable i is now: 2\n\tThe variable i is now: 1\n\tThe variable i is now: 0\n\tThe variable i is now: -1\n\n## 5.4.3 无限循环\n\n条件语句是可以被省略的，如 `i:=0; ; i++` 或 `for { }` 或 `for ;; { }`（`;;` 会在使用 gofmt 时被移除）：这些循环的本质就是无限循环。最后一个形式也可以被改写为 `for true { }`，但一般情况下都会直接写 `for { }`。\n\n如果 for 循环的头部没有条件语句，那么就会认为条件永远为 true，因此循环体内必须有相关的条件判断以确保会在某个时刻退出循环。\n\n想要直接退出循环体，可以使用 break 语句（第 5.5 节）或 return 语句直接返回（第 6.1 节）。\n\n但这两者之间有所区别，break 只是退出当前的循环体，而 return 语句提前对函数进行返回，不会执行后续的代码。\n\n无限循环的经典应用是服务器，用于不断等待和接受新的请求。\n\n```go\nfor t, err = p.Token(); err == nil; t, err = p.Token() {\n\t...\n}\n```\n\n## 5.4.4 for-range 结构\n\n这是 Go 特有的一种的迭代结构，您会发现它在许多情况下都非常有用。它可以迭代任何一个集合（包括数组和 `map`，详见第 [7](07.0.md) 和 [8](08.0.md) 章）。语法上很类似其它语言中的 foreach 语句，但您依旧可以获得每次迭代所对应的索引。一般形式为：`for ix, val := range coll { }`。\n\n要注意的是，`val` 始终为集合中对应索引的值拷贝，因此它一般只具有只读性质，对它所做的任何修改都不会影响到集合中原有的值（**译者注：如果 `val` 为指针，则会产生指针的拷贝，依旧可以修改集合中的原值**）。一个字符串是 Unicode 编码的字符（或称之为 `rune`）集合，因此您也可以用它迭代字符串：\n\n```go\nfor pos, char := range str {\n...\n}\n```\n\n每个 `rune` 字符和索引在 for-range 循环中是一一对应的。它能够自动根据 UTF-8 规则识别 Unicode 编码的字符。\n\n示例 5.9 [range_string.go](examples/chapter_5/range_string.go)：\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tstr := \"Go is a beautiful language!\"\n\tfmt.Printf(\"The length of str is: %d\\n\", len(str))\n\tfor pos, char := range str {\n\t\tfmt.Printf(\"Character on position %d is: %c \\n\", pos, char)\n\t}\n\tfmt.Println()\n\tstr2 := \"Chinese: 日本語\"\n\tfmt.Printf(\"The length of str2 is: %d\\n\", len(str2))\n\tfor pos, char := range str2 {\n    \tfmt.Printf(\"character %c starts at byte position %d\\n\", char, pos)\n\t}\n\tfmt.Println()\n\tfmt.Println(\"index int(rune) rune    char bytes\")\n\tfor index, rune := range str2 {\n    \tfmt.Printf(\"%-2d      %d      %U '%c' % X\\n\", index, rune, rune, rune, []byte(string(rune)))\n\t}\n}\n```\n\n输出：\n\n```\nThe length of str is: 27\nCharacter on position 0 is: G \nCharacter on position 1 is: o \nCharacter on position 2 is:   \nCharacter on position 3 is: i \nCharacter on position 4 is: s \nCharacter on position 5 is:   \nCharacter on position 6 is: a \nCharacter on position 7 is:   \nCharacter on position 8 is: b \nCharacter on position 9 is: e \nCharacter on position 10 is: a \nCharacter on position 11 is: u \nCharacter on position 12 is: t \nCharacter on position 13 is: i \nCharacter on position 14 is: f \nCharacter on position 15 is: u \nCharacter on position 16 is: l \nCharacter on position 17 is:   \nCharacter on position 18 is: l \nCharacter on position 19 is: a \nCharacter on position 20 is: n \nCharacter on position 21 is: g \nCharacter on position 22 is: u \nCharacter on position 23 is: a \nCharacter on position 24 is: g \nCharacter on position 25 is: e \nCharacter on position 26 is: ! \n\nThe length of str2 is: 18\ncharacter C starts at byte position 0\ncharacter h starts at byte position 1\ncharacter i starts at byte position 2\ncharacter n starts at byte position 3\ncharacter e starts at byte position 4\ncharacter s starts at byte position 5\ncharacter e starts at byte position 6\ncharacter : starts at byte position 7\ncharacter   starts at byte position 8\ncharacter 日 starts at byte position 9\ncharacter 本 starts at byte position 12\ncharacter 語 starts at byte position 15\n\nindex int(rune) rune    char bytes\n0       67      U+0043 'C' 43\n1       104      U+0068 'h' 68\n2       105      U+0069 'i' 69\n3       110      U+006E 'n' 6E\n4       101      U+0065 'e' 65\n5       115      U+0073 's' 73\n6       101      U+0065 'e' 65\n7       58      U+003A ':' 3A\n8       32      U+0020 ' ' 20\n9       26085      U+65E5 '日' E6 97 A5\n12      26412      U+672C '本' E6 9C AC\n15      35486      U+8A9E '語' E8 AA 9E\n```\n\n请将输出结果和 Listing 5.7（[for_string.go](examples/chapter_5/for_string.go)）进行对比。\n\n我们可以看到，常用英文字符使用 1 个字节表示，而汉字（**译者注：严格来说，“Chinese: 日本語”的 Chinese 应该是 Japanese**）使用 3 个字符表示。\n\n**练习 5.9** 以下程序的输出结果是什么？\n\n```go\nfor i := 0; i < 5; i++ {\n\tvar v int\n\tfmt.Printf(\"%d \", v)\n\tv = 5\n}\n```\n\n**问题 5.2：** 请描述以下 for 循环的输出结果：\n\n1.\n\n```go\nfor i := 0; ; i++ {\n\tfmt.Println(\"Value of i is now:\", i)\n}\n```\n\n2.\n\n```go\nfor i := 0; i < 3; {\n\tfmt.Println(\"Value of i:\", i)\n}\n```\n\n3.\n\n```go\ns := \"\"\nfor ; s != \"aaaaa\"; {\n\tfmt.Println(\"Value of s:\", s)\n\ts = s + \"a\"\n}\n```\n\n4.\n\n```go\nfor i, j, s := 0, 5, \"a\"; i < 3 && j < 100 && s != \"aaaaa\"; i, j,\n\ts = i+1, j+1, s + \"a\" {\n\tfmt.Println(\"Value of i, j, s:\", i, j, s)\n}\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[switch 结构](05.3.md)\n- 下一节：[Break 与 continue](05.5.md)\n"
  },
  {
    "path": "eBook/05.5.md",
    "content": "# 5.5 break 与 continue\n\n您可以使用 `break` 语句重写 [for2.go](examples/chapter_5/for2.go) 的代码：\n\n示例 5.10 [for3.go](examples/chapter_5/for3.go)：\n\n```go\nfor {\n\ti = i - 1\n\tfmt.Printf(\"The variable i is now: %d\\n\", i)\n\tif i < 0 {\n\t\tbreak\n\t}\n}\n```\n\n因此每次迭代都会对条件进行检查（`i < 0`），以此判断是否需要停止循环。如果退出条件满足，则使用 `break` 语句退出循环。\n\n一个 `break` 的作用范围为该语句出现后的最内部的结构，它可以被用于任何形式的 `for` 循环（计数器、条件判断等）。但在 `switch` 或 `select` 语句中（详见[第 13 章](13.0.md)），`break` 语句的作用结果是跳过整个代码块，执行后续的代码。\n\n下面的示例中包含了嵌套的循环体（for4.go），`break` 只会退出最内层的循环：\n\n示例 5.11 [for4.go](examples/chapter_5/for4.go)：\n\n```go\npackage main\n\nfunc main() {\n\tfor i:=0; i<3; i++ {\n\t\tfor j:=0; j<10; j++ {\n\t\t\tif j>5 {\n\t\t\t    break   \n\t\t\t}\n\t\t\tprint(j)\n\t\t}\n\t\tprint(\"  \")\n\t}\n}\n```\n\n输出：\n\n\t012345 012345 012345\n\n关键字 `continue` 忽略剩余的循环体而直接进入下一次循环的过程，但不是无条件执行下一次循环，执行之前依旧需要满足循环的判断条件。\n\n示例 5.12 [for5.go](examples/chapter_5/for5.go)：\n\n```go\npackage main\n\nfunc main() {\n\tfor i := 0; i < 10; i++ {\n\t\tif i == 5 {\n\t\t\tcontinue\n\t\t}\n\t\tprint(i)\n\t\tprint(\" \")\n\t}\n}\n```\n\n输出：\n\n```\n0 1 2 3 4 6 7 8 9\n```\n\n显然，`5` 被跳过了。\n\n另外，关键字 `continue` 只能被用于 `for` 循环中。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[for 结构](05.4.md)\n- 下一节：[标签与 goto](05.6.md)\n"
  },
  {
    "path": "eBook/05.6.md",
    "content": "# 5.6 标签与 goto\n\n`for`、`switch` 或 `select` 语句都可以配合标签 (label) 形式的标识符使用，即某一行第一个以冒号 (`:`) 结尾的单词（gofmt 会将后续代码自动移至下一行）。\n\n示例 5.13 [for6.go](examples/chapter_5/for6.go)：\n\n（标签的名称是大小写敏感的，为了提升可读性，一般建议使用全部大写字母）\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\nLABEL1:\n\tfor i := 0; i <= 5; i++ {\n\t\tfor j := 0; j <= 5; j++ {\n\t\t\tif j == 4 {\n\t\t\t\tcontinue LABEL1\n\t\t\t}\n\t\t\tfmt.Printf(\"i is: %d, and j is: %d\\n\", i, j)\n\t\t}\n\t}\n\n}\n```\n\n本例中，`continue` 语句指向 `LABEL1`，当执行到该语句的时候，就会跳转到 `LABEL1` 标签的位置。\n\n您可以看到当 `j==4` 和 `j==5` 的时候，没有任何输出：标签的作用对象为外部循环，因此 `i` 会直接变成下一个循环的值，而此时 `j` 的值就被重设为 `0`，即它的初始值。如果将 `continue` 改为 `break`，则不会只退出内层循环，而是直接退出外层循环了。另外，还可以使用 `goto` 语句和标签配合使用来模拟循环。\n\n示例 5.14 [goto.go](examples/chapter_5/goto.go)：\n\n```go\npackage main\n\nfunc main() {\n\ti:=0\n\tHERE:\n\t\tprint(i)\n\t\ti++\n\t\tif i==5 {\n\t\t\treturn\n\t\t}\n\t\tgoto HERE\n}\n```\n\n上面的代码会输出 `01234`。\n\n使用逆向的 `goto` 会很快导致意大利面条式的代码，所以不应当使用而选择更好的替代方案。\n\n**特别注意** 使用标签和 `goto` 语句是不被鼓励的：它们会很快导致非常糟糕的程序设计，而且总有更加可读的替代方案来实现相同的需求。\n\n一个建议使用 `goto` 语句的示例会在[第 15.1 章](15.1.md) 的 [simple_tcp_server.go](./examples/chapter_15/simple_tcp_server.go) 中出现：示例中在发生读取错误时，使用 goto 来跳出无限读取循环并关闭相应的客户端链接。\n\n定义但未使用标签会导致编译错误：`label … defined and not used`。\n\n如果您必须使用 `goto`，应当只使用正序的标签（标签位于 `goto` 语句之后），但注意标签和 `goto` 语句之间不能出现定义新变量的语句，否则会导致编译失败。\n\n示例 5.15 [goto2.go](examples/chapter_5/got2o.go)：\n\n```go\n// compile error goto2.go:8: goto TARGET jumps over declaration of b at goto2.go:8\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\t\ta := 1\n\t\tgoto TARGET // compile error\n\t\tb := 9\n\tTARGET:  \n\t\tb += a\n\t\tfmt.Printf(\"a is %v *** b is %v\", a, b)\n}\n```\n\n**问题 5.3** 请描述下面 `for` 循环的输出：\n\n1.\n\n```go\ni := 0\nfor { //since there are no checks, this is an infinite loop\n\tif i >= 3 { break }\n\t//break out of this for loop when this condition is met\n\tfmt.Println(\"Value of i is:\", i)\n\ti++\n}\nfmt.Println(\"A statement just after for loop.\")\n```\n\n2.\n\n```go\nfor i := 0; i<7 ; i++ {\n\tif i%2 == 0 { continue }\n\tfmt.Println(\"Odd:\", i)\n}\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[Break 与 continue](05.5.md)\n- 下一章：[函数](06.0.md)\n"
  },
  {
    "path": "eBook/06.0.md",
    "content": "# 6.0 函数 (function)\n\n函数是 Go 里面的基本代码块：Go 函数的功能非常强大，以至于被认为拥有函数式编程语言的多种特性。在这一章，我们将对 [第 4.2.2 节](04.2.md) 所简要描述的函数进行详细的讲解。\n\n## 链接\n\n- [目录](directory.md)\n- 上一章：[标签与 goto](05.6.md)\n- 下一节：[介绍](06.1.md)\n"
  },
  {
    "path": "eBook/06.1.md",
    "content": "# 6.1 介绍\n\n每一个程序都包含很多的函数：函数是基本的代码块。\n\nGo是编译型语言，所以函数编写的顺序是无关紧要的；鉴于可读性的需求，最好把 `main()` 函数写在文件的前面，其他函数按照一定逻辑顺序进行编写（例如函数被调用的顺序）。\n\n编写多个函数的主要目的是将一个需要很多行代码的复杂问题分解为一系列简单的任务（那就是函数）来解决。而且，同一个任务（函数）可以被调用多次，有助于代码重用。\n\n（事实上，好的程序是非常注意 DRY 原则的，即不要重复你自己 (Don't Repeat Yourself)，意思是执行特定任务的代码只能在程序里面出现一次。）\n\n当函数执行到代码块最后一行（`}` 之前）或者 `return` 语句的时候会退出，其中 `return` 语句可以带有零个或多个参数；这些参数将作为返回值（参考 [第 6.2 节](06.2.md)）供调用者使用。简单的 `return` 语句也可以用来结束 `for` 死循环，或者结束一个协程 (goroutine)。\n\nGo 里面有三种类型的函数：  \n\n- 普通的带有名字的函数\n- 匿名函数或者lambda函数（参考 [第 6.8 节](06.8.md)）\n- 方法（Methods，参考 [第 10.6 节](10.6.md)）\n\n除了 `main()`、`init()` 函数外，其它所有类型的函数都可以有参数与返回值。函数参数、返回值以及它们的类型被统称为函数签名。\n\n作为提醒，提前介绍一个语法：\n\n这样是不正确的 Go 代码：\n\n```go\nfunc g()\n{\n}\n```\n\n它必须是这样的：\n\n```go\nfunc g() {\n}\n```\n\n函数被调用的基本格式如下：\n\n```go\npack1.Function(arg1, arg2, …, argn)\n```\n\n`Function` 是 `pack1` 包里面的一个函数，括号里的是被调用函数的实参 (argument)：这些值被传递给被调用函数的*形参*（parameter，参考[第 6.2 节](06.2.md)）。函数被调用的时候，这些实参将被复制（简单而言）然后传递给被调用函数。函数一般是在其他函数里面被调用的，这个其他函数被称为调用函数 (calling function)。函数能多次调用其他函数，这些被调用函数按顺序（简单而言）执行，理论上，函数调用其他函数的次数是无穷的（直到函数调用栈被耗尽）。\n\n一个简单的函数调用其他函数的例子：\n\n示例 6.1 [greeting.go](examples/chapter_6/greeting.go)\n\n```go\npackage main\n\nfunc main() {\n    println(\"In main before calling greeting\")\n    greeting()\n    println(\"In main after calling greeting\")\n}\n\nfunc greeting() {\n    println(\"In greeting: Hi!!!!!\")\n}\n```\n\n代码输出：\n\n    In main before calling greeting\n    In greeting: Hi!!!!!\n    In main after calling greeting\n\n函数可以将其他函数调用作为它的参数，只要这个被调用函数的返回值个数、返回值类型和返回值的顺序与调用函数所需求的实参是一致的，例如：\n\n假设 `f1` 需要 3 个参数 `f1(a, b, c int)`，同时 `f2` 返回 3 个参数 `f2(a, b int) (int, int, int)`，就可以这样调用 `f1`：`f1(f2(a, b))`。\n\n函数重载 (function overloading) 指的是可以编写多个同名函数，只要它们拥有不同的形参/或者不同的返回值，在 Go 里面函数重载是不被允许的。这将导致一个编译错误：\n\n    funcName redeclared in this book, previous declaration at lineno\n\nGo 语言不支持这项特性的主要原因是函数重载需要进行多余的类型匹配影响性能；没有重载意味着只是一个简单的函数调度。所以你需要给不同的函数使用不同的名字，我们通常会根据函数的特征对函数进行命名（参考 [第 11.12.5 节](11.12.md)）。\n\n如果需要申明一个在外部定义的函数，你只需要给出函数名与函数签名，不需要给出函数体：\n\n```go\nfunc flushICache(begin, end uintptr) // implemented externally\n```\n\n**函数也可以以申明的方式被使用，作为一个函数类型**，就像：\n\n```go\ntype binOp func(int, int) int\n```\n\n在这里，不需要函数体 `{}`。\n\n函数是一等值 (first-class value)：它们可以赋值给变量，就像 `add := binOp` 一样。\n\n这个变量知道自己指向的函数的签名，所以给它赋一个具有不同签名的函数值是不可能的。\n\n函数值 (functions value) 之间可以相互比较：如果它们引用的是相同的函数或者都是 `nil` 的话，则认为它们是相同的函数。函数不能在其它函数里面声明（不能嵌套），不过我们可以通过使用匿名函数（参考 [第 6.8 节](06.8.md)）来破除这个限制。\n\n目前 Go 没有泛型 (generic) 的概念，也就是说它不支持那种支持多种类型的函数。不过在大部分情况下可以通过接口 (interface)，特别是空接口与类型选择（type switch，参考 [第 11.12 节](11.12.md)）与/或者通过使用反射（reflection，参考 [第 6.8 节](06.8.md)）来实现相似的功能。使用这些技术将导致代码更为复杂、性能更为低下，所以在非常注意性能的的场合，最好是为每一个类型单独创建一个函数，而且代码可读性更强。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[函数 (function)](06.0.md)\n- 下一节：[函数参数与返回值](06.2.md)\n"
  },
  {
    "path": "eBook/06.10.md",
    "content": "# 6.10 使用闭包调试\n\n当您在分析和调试复杂的程序时，无数个函数在不同的代码文件中相互调用，如果这时候能够准确地知道哪个文件中的具体哪个函数正在执行，对于调试是十分有帮助的。您可以使用 `runtime` 或 `log` 包中的特殊函数来实现这样的功能。包 `runtime` 中的函数 `Caller()` 提供了相应的信息，因此可以在需要的时候实现一个 `where()` 闭包函数来打印函数执行的位置：\n\n```go\nwhere := func() {\n\t_, file, line, _ := runtime.Caller(1)\n\tlog.Printf(\"%s:%d\", file, line)\n}\nwhere()\n// some code\nwhere()\n// some more code\nwhere()\n```\n\n您也可以设置 `log` 包中的 `flag` 参数来实现：\n\n```go\nlog.SetFlags(log.Llongfile)\nlog.Print(\"\")\n```\n\n或使用一个更加简短版本的 `where()` 函数：\n\n```go\nvar where = log.Print\nfunc func1() {\nwhere()\n... some code\nwhere()\n... some code\nwhere()\n}\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[应用闭包：将函数作为返回值](06.9.md)\n- 下一节：[计算函数执行时间](06.11.md)\n\n"
  },
  {
    "path": "eBook/06.11.md",
    "content": "# 6.11 计算函数执行时间\n\n有时候，能够知道一个计算执行消耗的时间是非常有意义的，尤其是在对比和基准测试中。最简单的一个办法就是在计算开始之前设置一个起始时间，再记录计算结束时的结束时间，最后计算它们的差值，就是这个计算所消耗的时间。想要实现这样的做法，可以使用 `time` 包中的 `Now()` 和 `Sub()` 函数：\n\n```go\nstart := time.Now()\nlongCalculation()\nend := time.Now()\ndelta := end.Sub(start)\nfmt.Printf(\"longCalculation took this amount of time: %s\\n\", delta)\n```\n\n您可以查看示例 6.20 [fibonacci.go](examples/chapter_6/fibonacci.go) 作为实例学习。\n\n如果您对一段代码进行了所谓的优化，请务必对它们之间的效率进行对比再做出最后的判断。在接下来的章节中，我们会学习如何进行有价值的优化操作。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[使用闭包调试](06.10.md)\n- 下一节：[通过内存缓存来提升性能](06.12.md)\n"
  },
  {
    "path": "eBook/06.12.md",
    "content": "# 6.12 通过内存缓存来提升性能\n\n当在进行大量的计算时，提升性能最直接有效的一种方式就是避免重复计算。通过在内存中缓存和重复利用相同计算的结果，称之为内存缓存。最明显的例子就是生成斐波那契数列的程序（详见第 [6.6](06.6.md) 和 [6.11](06.11.md) 节）：\n\n要计算数列中第 n 个数字，需要先得到之前两个数的值，但很明显绝大多数情况下前两个数的值都是已经计算过的。即每个更后面的数都是基于之前计算结果的重复计算，正如示例 6.11 [fibonnaci.go](examples/chapter_6/fibonacci.go) 所展示的那样。\n\n而我们要做就是将第 n 个数的值存在数组中索引为 n 的位置（详见[第 7 章](07.0.md)），然后在数组中查找是否已经计算过，如果没有找到，则再进行计算。\n\n程序 Listing 6.17 - [fibonacci_memoization.go](examples/chapter_6/fibonacci_memoization.go) 就是依照这个原则实现的，下面是计算到第 40 位数字的性能对比：\n\n- 普通写法：4.730270 秒\n- 内存缓存：0.001000 秒\n\n内存缓存的优势显而易见，而且您还可以将它应用到其它类型的计算中，例如使用 `map`（详见[第 7 章](07.0.md)）而不是数组或切片（Listing 6.21 - [fibonacci_memoization.go](examples/chapter_6/fibonacci_memoization.go)）：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nconst LIM = 41\n\nvar fibs [LIM]uint64\n\nfunc main() {\n\tvar result uint64 = 0\n\tstart := time.Now()\n\tfor i := 0; i < LIM; i++ {\n\t\tresult = fibonacci(i)\n\t\tfmt.Printf(\"fibonacci(%d) is: %d\\n\", i, result)\n\t}\n\tend := time.Now()\n\tdelta := end.Sub(start)\n\tfmt.Printf(\"longCalculation took this amount of time: %s\\n\", delta)\n}\nfunc fibonacci(n int) (res uint64) {\n\t// memoization: check if fibonacci(n) is already known in array:\n\tif fibs[n] != 0 {\n\t\tres = fibs[n]\n\t\treturn\n\t}\n\tif n <= 1 {\n\t\tres = 1\n\t} else {\n\t\tres = fibonacci(n-1) + fibonacci(n-2)\n\t}\n\tfibs[n] = res\n\treturn\n}\n```\n\n内存缓存的技术在使用计算成本相对昂贵的函数时非常有用（不仅限于例子中的递归），譬如大量进行相同参数的运算。这种技术还可以应用于纯函数中，即相同输入必定获得相同输出的函数。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[计算函数执行时间](06.11.md)\n- 下一章：[数组与切片](07.0.md)\n"
  },
  {
    "path": "eBook/06.2.md",
    "content": "# 6.2 函数参数与返回值\n\n函数能够接收参数供自己使用，也可以返回零个或多个值（我们通常把返回多个值称为返回一组值）。相比与 C、C++、Java 和 C#，多值返回是 Go 的一大特性，为我们判断一个函数是否正常执行（参考 [第 5.2 节](05.2.md)）提供了方便。\n\n我们通过 `return` 关键字返回一组值。事实上，任何一个有返回值（单个或多个）的函数都必须以 `return` 或 `panic`（参考 [第 13 章](13.0.md)）结尾。\n\n在函数块里面，`return` 之后的语句都不会执行。如果一个函数需要返回值，那么这个函数里面的每一个代码分支 (code-path) 都要有 `return` 语句。\n\n问题 6.1：下面的函数将不会被编译，为什么呢？大家可以试着纠正过来。\n\n```go\nfunc (st *Stack) Pop() int {\n    v := 0\n    for ix := len(st) - 1; ix >= 0; ix-- {\n        if v = st[ix]; v != 0 {\n            st[ix] = 0\n            return v\n        }\n    }\n}    \n```\n\n函数定义时，它的形参一般是有名字的，不过我们也可以定义没有形参名的函数，只有相应的形参类型，就像这样：`func f(int, int, float64)`。\n\n没有参数的函数通常被称为 **niladic** 函数 (niladic function)，就像 `main.main()`。\n\n## 6.2.1 按值传递 (call by value) 按引用传递 (call by reference)\n\nGo 默认使用按值传递来传递参数，也就是传递参数的副本。函数接收参数副本之后，在使用变量的过程中可能对副本的值进行更改，但不会影响到原来的变量，比如 `Function(arg1)`。\n\n如果你希望函数可以直接修改参数的值，而不是对参数的副本进行操作，你需要将参数的地址（变量名前面添加 `&` 符号，比如 `&variable`）传递给函数，这就是按引用传递，比如 `Function(&arg1)`，此时传递给函数的是一个指针。如果传递给函数的是一个指针，指针的值（一个地址）会被复制，但指针的值所指向的地址上的值不会被复制；我们可以通过这个指针的值来修改这个值所指向的地址上的值。（**译者注：指针也是变量类型，有自己的地址和值，通常指针的值指向一个变量的地址。所以，按引用传递也是按值传递。**）\n\n几乎在任何情况下，传递指针（一个32位或者64位的值）的消耗都比传递副本来得少。\n\n在函数调用时，像切片 (slice)、字典 (map)、接口 (interface)、通道 (channel) 这样的引用类型都是默认使用引用传递（即使没有显式的指出指针）。\n\n有些函数只是完成一个任务，并没有返回值。我们仅仅是利用了这种函数的副作用 (side-effect)，就像输出文本到终端，发送一个邮件或者是记录一个错误等。\n\n但是绝大部分的函数还是带有返回值的。\n\n如下，simple_function.go 里的 `MultiPly3Nums` 函数带有三个形参，分别是 `a`、`b`、`c`，还有一个 `int` 类型的返回值（被注释的代码具有和未注释部分同样的功能，只是多引入了一个本地变量）：\n\n示例 6.2 [simple_function.go](examples/chapter_6/simple_function.go)\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n    fmt.Printf(\"Multiply 2 * 5 * 6 = %d\\n\", MultiPly3Nums(2, 5, 6))\n    // var i1 int = MultiPly3Nums(2, 5, 6)\n    // fmt.Printf(\"MultiPly 2 * 5 * 6 = %d\\n\", i1)\n}\n\nfunc MultiPly3Nums(a int, b int, c int) int {\n    // var product int = a * b * c\n    // return product\n    return a * b * c\n}\n```\n\n输出显示：\n\n    Multiply 2 * 5 * 6 = 60\n\n如果一个函数需要返回四到五个值，我们可以传递一个切片给函数（如果返回值具有相同类型）或者是传递一个结构体（如果返回值具有不同的类型）。因为传递一个指针允许直接修改变量的值，消耗也更少。\n\n问题 6.2：\n\n如下的两个函数调用有什么不同：\n\n    (A) func DoSomething(a *A) {\n            b = a\n        }\n    \n    (B) func DoSomething(a A) {\n            b = &a\n        }\n\n## 6.2.2 命名的返回值 (named return variables)\n\n如下 multiple_return.go 里的函数带有一个 `int` 参数，返回两个 `int` 值；其中一个函数的返回值在函数调用时就已经被赋予了一个初始零值。\n\n`getX2AndX3` 与 `getX2AndX3_2` 两个函数演示了如何使用非命名返回值与命名返回值的特性。当需要返回多个非命名返回值时，需要使用 `()` 把它们括起来，比如 `(int, int)`。\n\n命名返回值作为结果形参 (result parameters) 被初始化为相应类型的零值，当需要返回的时候，我们只需要一条简单的不带参数的 `return` 语句。需要注意的是，即使只有一个命名返回值，也需要使用 `()` 括起来（参考[第 6.6 节](06.6.md) 的 [fibonacci.go](./examples/chapter_6/fibonacci.go) 函数）。\n\n示例 6.3 [multiple_return.go](examples/chapter_6/multiple_return.go)\n\n```go\npackage main\n\nimport \"fmt\"\n\nvar num int = 10\nvar numx2, numx3 int\n\nfunc main() {\n    numx2, numx3 = getX2AndX3(num)\n    PrintValues()\n    numx2, numx3 = getX2AndX3_2(num)\n    PrintValues()\n}\n\nfunc PrintValues() {\n    fmt.Printf(\"num = %d, 2x num = %d, 3x num = %d\\n\", num, numx2, numx3)\n}\n\nfunc getX2AndX3(input int) (int, int) {\n    return 2 * input, 3 * input\n}\n\nfunc getX2AndX3_2(input int) (x2 int, x3 int) {\n    x2 = 2 * input\n    x3 = 3 * input\n    // return x2, x3\n    return\n}\n```\n\n输出结果：\n    \n    num = 10, 2x num = 20, 3x num = 30    \n    num = 10, 2x num = 20, 3x num = 30 \n\n提示：\n\n虽然 `return` 或 `return var` 都是可以的，但是 `return var = expression`（表达式） 会引发一个编译错误：\n\n`syntax error: unexpected =, expecting semicolon or newline or }`。\n\n即使函数使用了命名返回值，你依旧可以无视它而返回明确的值。        \n            \n任何一个非命名返回值（使用非命名返回值是很糟的编程习惯）在 `return` 语句里面都要明确指出包含返回值的变量或是一个可计算的值（就像上面警告所指出的那样）。\n\n**尽量使用命名返回值：会使代码更清晰、更简短，同时更加容易读懂。**\n\n练习 6.1 [mult_returnval.go](exercises/chapter_6/mult_returnval.go)\n\n编写一个函数，接收两个整数，然后返回它们的和、积与差。编写两个版本，一个是非命名返回值，一个是命名返回值。\n\n练习 6.2 [error_returnval.go](exercises/chapter_6/error_returnval.go)\n\n编写一个名字为 `MySqrt()` 的函数，计算一个 `float64` 类型浮点数的平方根，如果参数是一个负数的话将返回一个错误。编写两个版本，一个是非命名返回值，一个是命名返回值。\n\n## 6.2.3 空白符 (blank identifier)\n\n空白符用来匹配一些不需要的值，然后丢弃掉，下面的 blank_identifier.go 就是很好的例子。\n\n`ThreeValues` 是拥有三个返回值的不需要任何参数的函数，在下面的例子中，我们将第一个与第三个返回值赋给了 `i1` 与 `f1`。第二个返回值赋给了空白符 `_`，然后自动丢弃掉。\n\n示例 6.4 [blank_identifier.go](examples/chapter_6/blank_identifier.go)\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n    var i1 int\n    var f1 float32\n    i1, _, f1 = ThreeValues()\n    fmt.Printf(\"The int: %d, the float: %f \\n\", i1, f1)\n}\n\nfunc ThreeValues() (int, int, float32) {\n    return 5, 6, 7.5\n}\n```\n\n输出结果：\n\n    The int: 5, the float: 7.500000\n\n另外一个示例，函数接收两个参数，比较它们的大小，然后按小-大的顺序返回这两个数，示例代码为 minmax.go。\n\n示例 6.5 [minmax.go](examples/chapter_6/minmax.go)\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n    var min, max int\n    min, max = MinMax(78, 65)\n    fmt.Printf(\"Minmium is: %d, Maximum is: %d\\n\", min, max)\n}\n\nfunc MinMax(a int, b int) (min int, max int) {\n    if a < b {\n        min = a\n        max = b\n    } else { // a = b or a < b\n        min = b\n        max = a\n    }\n    return\n}\n```\n\n输出结果：\n\n    Minimum is: 65, Maximum is 78\n\n## 6.2.4 改变外部变量 (outside variable)\n\n传递指针给函数不但可以节省内存（因为没有复制变量的值），而且赋予了函数直接修改外部变量的能力，所以被修改的变量不再需要使用 `return` 返回。如下的例子，`reply` 是一个指向 `int` 变量的指针，通过这个指针，我们在函数内修改了这个 `int` 变量的数值。\n\n示例 6.6 [side_effect.go](examples/chapter_6/side_effect.go)\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n)\n\n// this function changes reply:\nfunc Multiply(a, b int, reply *int) {\n    *reply = a * b\n}\n\nfunc main() {\n    n := 0\n    reply := &n\n    Multiply(10, 5, reply)\n    fmt.Println(\"Multiply:\", *reply) // Multiply: 50\n}\n```\n\n这仅仅是个指导性的例子，当需要在函数内改变一个占用内存比较大的变量时，性能优势就更加明显了。然而，如果不小心使用的话，传递一个指针很容易引发一些不确定的事，所以，我们要十分小心那些可以改变外部变量的函数，在必要时，需要添加注释以便其他人能够更加清楚的知道函数里面到底发生了什么。\n            \n## 链接\n\n- [目录](directory.md)\n- 上一节：[函数介绍](06.1.md)\n- 下一节：[传递变长参数](06.3.md)\n"
  },
  {
    "path": "eBook/06.3.md",
    "content": "# 6.3 传递变长参数\n\n如果函数的最后一个参数是采用 `...type` 的形式，那么这个函数就可以处理一个变长的参数，这个长度可以为 0，这样的函数称为变参函数。\n\n```go\nfunc myFunc(a, b, arg ...int) {}\n```\n\n这个函数接受一个类似于切片 (slice) 的参数（详见[第 7 章](07.0.md)），该参数可以通过[第 5.4.4 节](05.4.md) 中提到的 `for` 循环结构迭代。\n\n示例函数和调用：\n\n```go\nfunc Greeting(prefix string, who ...string)\nGreeting(\"hello:\", \"Joe\", \"Anna\", \"Eileen\")\n```\n\n在 `Greeting()` 函数中，变量 `who` 的值为 `[]string{\"Joe\", \"Anna\", \"Eileen\"}`。\n\n如果参数被存储在一个 slice 类型的变量 `slice` 中，则可以通过 `slice...` 的形式来传递参数，调用变参函数。\n\n示例 6.7 [varnumpar.go](examples/chapter_6/varnumpar.go)\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tx := min(1, 3, 2, 0)\n\tfmt.Printf(\"The minimum is: %d\\n\", x)\n\tslice := []int{7,9,3,5,1}\n\tx = min(slice...)\n\tfmt.Printf(\"The minimum in the slice is: %d\", x)\n}\n\nfunc min(s ...int) int {\n\tif len(s)==0 {\n\t\treturn 0\n\t}\n\tmin := s[0]\n\tfor _, v := range s {\n\t\tif v < min {\n\t\t\tmin = v\n\t\t}\n\t}\n\treturn min\n}\n```\n\n输出：\n\n\tThe minimum is: 0\n\tThe minimum in the slice is: 1\n\n**练习 6.3** [varargs.go](exercises/chapter_6/varargs.go)\n\n写一个函数，该函数接受一个变长参数并对每个元素进行换行打印。\n\n一个接受变长参数的函数可以将这个参数作为其它函数的参数进行传递：\n\n```go\nfunc F1(s ...string) {\n\tF2(s...)\n\tF3(s)\n}\n\nfunc F2(s ...string) { }\nfunc F3(s []string) { }\n```\n\n变长参数可以作为对应类型的 slice 进行二次传递。\n\n但是如果变长参数的类型并不是都相同的呢？使用 5 个参数来进行传递并不是很明智的选择，有 2 种方案可以解决这个问题：\n\n1. 使用结构（详见[第 10 章](10.0.md)）：\n\n\t定义一个结构类型，假设它叫 `Options`，用以存储所有可能的参数：\n\n\t```go\n\ttype Options struct {\n\t\tpar1 type1,\n\t\tpar2 type2,\n\t\t...\n\t}\n\t```\n\n\t函数 `F1()` 可以使用正常的参数 `a` 和 `b`，以及一个没有任何初始化的 `Options` 结构： `F1(a, b, Options {})`。如果需要对选项进行初始化，则可以使用 `F1(a, b, Options {par1:val1, par2:val2})`。\n\n2. 使用空接口：\n\n\t如果一个变长参数的类型没有被指定，则可以使用默认的空接口 `interface{}`，这样就可以接受任何类型的参数（详见[第 11.9 节](11.9.md) ）。该方案不仅可以用于长度未知的参数，还可以用于任何不确定类型的参数。一般而言我们会使用一个 for-range 循环以及 `switch` 结构对每个参数的类型进行判断：\n\n\t```go\n\tfunc typecheck(..,..,values … interface{}) {\n\t\tfor _, value := range values {\n\t\t\tswitch v := value.(type) {\n\t\t\t\tcase int: …\n\t\t\t\tcase float: …\n\t\t\t\tcase string: …\n\t\t\t\tcase bool: …\n\t\t\t\tdefault: …\n\t\t\t}\n\t\t}\n\t}\n\t```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[函数参数与返回值](06.2.md)\n- 下一节：[defer 和追踪](06.4.md)\n"
  },
  {
    "path": "eBook/06.4.md",
    "content": "# 6.4 defer 和追踪\n\n关键字 `defer` 允许我们推迟到函数返回之前（或任意位置执行 `return` 语句之后）一刻才执行某个语句或函数（为什么要在返回之后才执行这些语句？因为 `return` 语句同样可以包含一些操作，而不是单纯地返回某个值）。\n\n关键字 `defer` 的用法类似于面向对象编程语言 Java 和 C# 的 finally 语句块，它一般用于释放某些已分配的资源。\n\n示例 6.8 [defer.go](examples/chapter_6/defer.go)：\n\n```go\npackage main\nimport \"fmt\"\n\nfunc main() {\n\tfunction1()\n}\n\nfunc function1() {\n\tfmt.Printf(\"In function1 at the top\\n\")\n\tdefer function2()\n\tfmt.Printf(\"In function1 at the bottom!\\n\")\n}\n\nfunc function2() {\n\tfmt.Printf(\"Function2: Deferred until the end of the calling function!\")\n}\n```\n\n输出：\n\n```\nIn Function1 at the top\nIn Function1 at the bottom!\nFunction2: Deferred until the end of the calling function!\n```\n\n请将 `defer` 关键字去掉并对比输出结果。\n\n使用 `defer` 的语句同样可以接受参数，下面这个例子就会在执行 `defer` 语句时打印 `0`：\n\n```go\nfunc a() {\n\ti := 0\n\tdefer fmt.Println(i)\n\ti++\n\treturn\n}\n```\n\n当有多个 `defer` 行为被注册时，它们会以逆序执行（类似栈，即后进先出）：\n\n```go\nfunc f() {\n\tfor i := 0; i < 5; i++ {\n\t\tdefer fmt.Printf(\"%d \", i)\n\t}\n}\n```\n\n上面的代码将会输出：`4 3 2 1 0`。\n\n关键字 `defer` 允许我们进行一些函数执行完成后的收尾工作，例如：\n\n1. 关闭文件流 （详见 [第 12.2 节](12.2.md)）\n\n```go\n// open a file  \ndefer file.Close()\n```\n\n2. 解锁一个加锁的资源 （详见 [第 9.3 节](09.3.md)）\n\n```go\nmu.Lock()  \ndefer mu.Unlock() \n```\n\n3. 打印最终报告\n\n```go\nprintHeader()  \ndefer printFooter()\n```\n\n4. 关闭数据库链接\n\n```go\n// open a database connection  \ndefer disconnectFromDB()\n```\n\n合理使用 `defer` 语句能够使得代码更加简洁。\n\n以下代码模拟了上面描述的第 4 种情况：\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tdoDBOperations()\n}\n\nfunc connectToDB() {\n\tfmt.Println(\"ok, connected to db\")\n}\n\nfunc disconnectFromDB() {\n\tfmt.Println(\"ok, disconnected from db\")\n}\n\nfunc doDBOperations() {\n\tconnectToDB()\n\tfmt.Println(\"Defering the database disconnect.\")\n\tdefer disconnectFromDB() //function called here with defer\n\tfmt.Println(\"Doing some DB operations ...\")\n\tfmt.Println(\"Oops! some crash or network error ...\")\n\tfmt.Println(\"Returning from function here!\")\n\treturn //terminate the program\n\t// deferred function executed here just before actually returning, even if\n\t// there is a return or abnormal termination before\n}\n```\n\n输出：\n\n```\nok, connected to db\nDefering the database disconnect.\nDoing some DB operations ...\nOops! some crash or network error ...\nReturning from function here!\nok, disconnected from db\n```\n\n**使用 `defer` 语句实现代码追踪**\n\n一个基础但十分实用的实现代码执行追踪的方案就是在进入和离开某个函数打印相关的消息，即可以提炼为下面两个函数：\n\n```go\nfunc trace(s string) { fmt.Println(\"entering:\", s) }\nfunc untrace(s string) { fmt.Println(\"leaving:\", s) }\n```\n\n以下代码展示了何时调用这两个函数：\n\n示例 6.10 [defer_tracing.go](examples/chapter_6/defer_tracing.go):\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc trace(s string)   { fmt.Println(\"entering:\", s) }\nfunc untrace(s string) { fmt.Println(\"leaving:\", s) }\n\nfunc a() {\n\ttrace(\"a\")\n\tdefer untrace(\"a\")\n\tfmt.Println(\"in a\")\n}\n\nfunc b() {\n\ttrace(\"b\")\n\tdefer untrace(\"b\")\n\tfmt.Println(\"in b\")\n\ta()\n}\n\nfunc main() {\n\tb()\n}\n```\n\n输出：\n\n```\nentering: b\nin b\nentering: a\nin a\nleaving: a\nleaving: b\n```\n\n上面的代码还可以修改为更加简便的版本（示例 6.11 [defer_tracing2.go](examples/chapter_6/defer_tracing2.go)）：\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc trace(s string) string {\n\tfmt.Println(\"entering:\", s)\n\treturn s\n}\n\nfunc un(s string) {\n\tfmt.Println(\"leaving:\", s)\n}\n\nfunc a() {\n\tdefer un(trace(\"a\"))\n\tfmt.Println(\"in a\")\n}\n\nfunc b() {\n\tdefer un(trace(\"b\"))\n\tfmt.Println(\"in b\")\n\ta()\n}\n\nfunc main() {\n\tb()\n}\n```\n\n**使用 `defer` 语句来记录函数的参数与返回值**\n\n下面的代码展示了另一种在调试时使用 `defer` 语句的手法（示例 6.12 [defer_logvalues.go](examples/chapter_6/defer_logvalues.go)）：\n\n```go\npackage main\n\nimport (\n\t\"io\"\n\t\"log\"\n)\n\nfunc func1(s string) (n int, err error) {\n\tdefer func() {\n\t\tlog.Printf(\"func1(%q) = %d, %v\", s, n, err)\n\t}()\n\treturn 7, io.EOF\n}\n\nfunc main() {\n\tfunc1(\"Go\")\n}\n\n```\n\n输出：\n\n\tOutput: 2011/10/04 10:46:11 func1(\"Go\") = 7, EOF\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[传递变长参数](06.3.md)\n- 下一节：[内置函数](06.5.md)\n"
  },
  {
    "path": "eBook/06.5.md",
    "content": "# 6.5 内置函数\n\nGo 语言拥有一些不需要进行导入操作就可以使用的内置函数。它们有时可以针对不同的类型进行操作，例如：`len()`、`cap()` 和 `append()`，或必须用于系统级的操作，例如：`panic()`。因此，它们需要直接获得编译器的支持。\n\n以下是一个简单的列表，我们会在后面的章节中对它们进行逐个深入的讲解。\n\n|名称|说明|\n|---|---|\n|`close()`|用于管道通信|\n|`len()`、`cap()`|`len()` 用于返回某个类型的长度或数量（字符串、数组、切片、`map` 和管道）；`cap()` 是容量的意思，用于返回某个类型的最大容量（只能用于数组、切片和管道，不能用于 `map`）|\n|`new()`、`make()`|`new()` 和 `make()` 均是用于分配内存：`new()` 用于值类型和用户定义的类型，如自定义结构，`make` 用于内置引用类型（切片、`map` 和管道）。它们的用法就像是函数，但是将类型作为参数：`new(type)`、`make(type)`。`new(T)` 分配类型 `T` 的零值并返回其地址，也就是指向类型 `T` 的指针（详见[第 10.1 节](10.1.md)）。它也可以被用于基本类型：`v := new(int)`。`make(T)` 返回类型 `T` 的初始化之后的值，因此它比 `new()` 进行更多的工作（详见[第 7.2.3/4 节](07.2.md)、[第 8.1.1 节](08.1.md)和[第 14.2.1 节](14.2.md)）。**`new()` 是一个函数，不要忘记它的括号**。|\n|`copy()`、`append()`|用于复制和连接切片|\n|`panic()`、`recover()`|两者均用于错误处理机制|\n|`print()`、`println()`|底层打印函数（详见[第 4.2 节](04.2.md)），在部署环境中建议使用 `fmt` 包|\n|`complex()`、`real ()`、`imag()`|用于创建和操作复数（详见[第 4.5.2.2 节](04.5.md)）|\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[defer 和追踪](06.4.md)\n- 下一节：[递归函数](06.6.md)\n"
  },
  {
    "path": "eBook/06.6.md",
    "content": "# 6.6 递归函数\n\n当一个函数在其函数体内调用自身，则称之为递归。最经典的例子便是计算斐波那契数列，即前两个数为 1，从第三个数开始每个数均为前两个数之和。\n\n数列如下所示：\n\n\t1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, …\n\n下面的程序可用于生成该数列（示例 6.13 [fibonacci.go](examples/chapter_6/fibonacci.go)）：\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tresult := 0\n\tfor i := 0; i <= 10; i++ {\n\t\tresult = fibonacci(i)\n\t\tfmt.Printf(\"fibonacci(%d) is: %d\\n\", i, result)\n\t}\n}\n\nfunc fibonacci(n int) (res int) {\n\tif n <= 1 {\n\t\tres = 1\n\t} else {\n\t\tres = fibonacci(n-1) + fibonacci(n-2)\n\t}\n\treturn\n}\n```\n\n输出：\n\n```\nfibonacci(0) is: 1\nfibonacci(1) is: 1\nfibonacci(2) is: 2\nfibonacci(3) is: 3\nfibonacci(4) is: 5\nfibonacci(5) is: 8\nfibonacci(6) is: 13\nfibonacci(7) is: 21\nfibonacci(8) is: 34\nfibonacci(9) is: 55\nfibonacci(10) is: 89\n```\n\n许多问题都可以使用优雅的递归来解决，比如说著名的快速排序算法。\n\n在使用递归函数时经常会遇到的一个重要问题就是栈溢出：一般出现在大量的递归调用导致的程序栈内存分配耗尽。这个问题可以通过一个名为 [懒惰求值](https://zh.wikipedia.org/wiki/惰性求值) 的技术解决，在 Go 语言中，我们可以使用管道 (channel) 和 goroutine（详见[第 14.8 节](14.8.md)）来实现。[练习 14.12](14.8.md) 也会通过这个方案来优化斐波那契数列的生成问题。\n\nGo 语言中也可以使用相互调用的递归函数：多个函数之间相互调用形成闭环。因为 Go 语言编译器的特殊性，这些函数的声明顺序可以是任意的。下面这个简单的例子展示了函数 `odd()` 和 `even()` 之间的相互调用（示例 6.14 [mut_recurs.go](examples/chapter_6/mut_recurs.go)）：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tfmt.Printf(\"%d is even: is %t\\n\", 16, even(16)) // 16 is even: is true\n\tfmt.Printf(\"%d is odd: is %t\\n\", 17, odd(17))\n\t// 17 is odd: is true\n\tfmt.Printf(\"%d is odd: is %t\\n\", 18, odd(18))\n\t// 18 is odd: is false\n}\n\nfunc even(nr int) bool {\n\tif nr == 0 {\n\t\treturn true\n\t}\n\treturn odd(RevSign(nr) - 1)\n}\n\nfunc odd(nr int) bool {\n\tif nr == 0 {\n\t\treturn false\n\t}\n\treturn even(RevSign(nr) - 1)\n}\n\nfunc RevSign(nr int) int {\n\tif nr < 0 {\n\t\treturn -nr\n\t}\n\treturn nr\n}\n```\n\n### 练习题\n\n**练习 6.4** [fibonacci2.go](exercises/chapter_6/fibonacci2.go)\n\n重写本节中生成斐波那契数列的程序并返回两个命名返回值（详见[第 6.2 节](06.2.md)），即数列中的位置和对应的值，例如 5 与 4，89 与 10。\n\n**练习 6.5** [10to1_recursive.go](exercises/chapter_6/10to1_recursive.go)\n\n使用递归函数从 10 打印到 1。\n\n**练习 6.6** [factorial.go](exercises/chapter_6/factorial.go)\n\n实现一个输出前 30 个整数的阶乘的程序。\n\nn 的阶乘定义为：`n! = n * (n-1)!, 0! = 1`，因此它非常适合使用递归函数来实现。\n\n然后，使用命名返回值来实现这个程序的第二个版本。\n\n特别注意的是，使用 `int` 类型最多只能计算到 12 的阶乘，因为一般情况下 `int` 类型的大小为 32 位，继续计算会导致溢出错误。那么，如何才能解决这个问题呢？\n\n最好的解决方案就是使用 `big` 包（详见[第 9.4 节](09.4.md)）。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[内置函数](06.5.md)\n- 下一节：[将函数作为参数](06.7.md)\n"
  },
  {
    "path": "eBook/06.7.md",
    "content": "# 6.7 将函数作为参数\n\n函数可以作为其它函数的参数进行传递，然后在其它函数内调用执行，一般称之为回调。下面是一个将函数作为参数的简单例子（[function_parameter.go](examples/chapter_6/function_parameter.go)）：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tcallback(1, Add)\n}\n\nfunc Add(a, b int) {\n\tfmt.Printf(\"The sum of %d and %d is: %d\\n\", a, b, a+b)\n}\n\nfunc callback(y int, f func(int, int)) {\n\tf(y, 2) // this becomes Add(1, 2)\n}\n```\n\n输出：\n\n    The sum of 1 and 2 is: 3\n\n将函数作为参数的最好的例子是函数 `strings.IndexFunc()`：\n\n该函数的签名是 `func IndexFunc(s string, f func(c rune) bool) int`，它的返回值是字符串 s 中第一个使函数 `f(c)` 返回 `true` 的 Unicode 字符的索引值。如果找不到，则返回 -1。\n\n例如 `strings.IndexFunc(line, unicode.IsSpace)` 就会返回 `line` 中第一个空白字符的索引值。当然，您也可以书写自己的函数：\n\n```go\nfunc IsAscii(c int) bool {\n\tif c > 255 {\n\t\treturn false\n\t}\n\treturn true\n}\n```\n\n在[第 14.10.1 节](14.10.md) 中，我们将会根据一个客户端/服务端程序作为示例对这个用法进行深入讨论。\n\n```go\ntype binOp func(a, b int) int\nfunc run(op binOp, req *Request) { … }\n```\n\n**练习 6.7** [strings_map.go](exercises/chapter_6/strings_map.go)\n\n包 `strings` 中的 `Map()` 函数和 `strings.IndexFunc()` 一样都是非常好的使用例子。请学习它的源代码并基于该函数书写一个程序，要求将指定文本内的所有非 ASCII 字符替换成问号 `'?'` 或空格 `' '`。您需要怎么做才能删除这些字符呢？\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[递归函数](06.6.md)\n- 下一节：[闭包](06.8.md)\n"
  },
  {
    "path": "eBook/06.8.md",
    "content": "# 6.8 闭包\n\n当我们不希望给函数起名字的时候，可以使用匿名函数，例如：`func(x, y int) int { return x + y }`。\n\n这样的一个函数不能够独立存在（编译器会返回错误：`non-declaration statement\noutside function body`），但可以被赋值于某个变量，即保存函数的地址到变量中：`fplus := func(x, y int) int { return x + y }`，然后通过变量名对函数进行调用：`fplus(3,4)`。\n\n当然，您也可以直接对匿名函数进行调用：`func(x, y int) int { return x + y } (3, 4)`。\n\n下面是一个计算从 1 到 100 万整数的总和的匿名函数：\n\n```go\nfunc() {\n\tsum := 0\n\tfor i := 1; i <= 1e6; i++ {\n\t\tsum += i\n\t}\n}()\n```\n\n表示参数列表的第一对括号必须紧挨着关键字 `func`，因为匿名函数没有名称。花括号 `{}` 涵盖着函数体，最后的一对括号表示对该匿名函数的调用。\n\n下面的例子展示了如何将匿名函数赋值给变量并对其进行调用（[function_literal.go](examples/chapter_6/function_literal.go)）：\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tf()\n}\nfunc f() {\n\tfor i := 0; i < 4; i++ {\n\t\tg := func(i int) { fmt.Printf(\"%d \", i) }\n\t\tg(i)\n\t\tfmt.Printf(\" - g is of type %T and has value %v\\n\", g, g)\n\t}\n}\n```\n\n输出：\n\n```\n0 - g is of type func(int) and has value 0x681a80\n1 - g is of type func(int) and has value 0x681b00\n2 - g is of type func(int) and has value 0x681ac0\n3 - g is of type func(int) and has value 0x681400\n```\n\n我们可以看到变量 `g` 代表的是 `func(int)`，变量的值是一个内存地址。\n\n所以我们实际上拥有的是一个函数值：匿名函数可以被赋值给变量并作为值使用。\n\n**练习 6.8** 在 `main()` 函数中写一个用于打印 `Hello World` 字符串的匿名函数并赋值给变量 `fv`，然后调用该函数并打印变量 `fv` 的类型。\n\n匿名函数像所有函数一样可以接受或不接受参数。下面的例子展示了如何传递参数到匿名函数中：\n\n```go\nfunc (u string) {\n\tfmt.Println(u)\n\t…\n}(v)\n```\n\n请学习以下示例并思考（[return_defer.go](examples/chapter_6/return_defer.go)）：函数 `f` 返回时，变量 `ret` 的值是什么？\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc f() (ret int) {\n\tdefer func() {\n\t\tret++\n\t}()\n\treturn 1\n}\nfunc main() {\n\tfmt.Println(f())\n}\n```\n\n变量 `ret` 的值为 `2`，因为 `ret++` 是在执行 `return 1` 语句后发生的。\n\n这可用于在返回语句之后修改返回的 `error` 时使用。\n\n**defer 语句和匿名函数**\n\n关键字 `defer` （详见[第 6.4 节](06.4.md)）经常配合匿名函数使用，它可以用于改变函数的命名返回值。\n\n匿名函数还可以配合 `go` 关键字来作为 goroutine 使用（详见[第 14 章](14.0.md)和[第 16.9 节](16.9.md)）。\n\n匿名函数同样被称之为闭包（函数式语言的术语）：它们被允许调用定义在其它环境下的变量。闭包可使得某个函数捕捉到一些外部状态，例如：函数被创建时的状态。另一种表示方式为：一个闭包继承了函数所声明时的作用域。这种状态（作用域内的变量）都被共享到闭包的环境中，因此这些变量可以在闭包中被操作，直到被销毁，详见[第 6.9 节](06.9.md) 中的示例。闭包经常被用作包装函数：它们会预先定义好 1 个或多个参数以用于包装，详见下一节中的示例。另一个不错的应用就是使用闭包来完成更加简洁的错误检查（详见[第 16.10.2 节](16.10.md)）。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[将函数作为参数](06.7.md)\n- 下一节：[应用闭包：将函数作为返回值](06.9.md)\n"
  },
  {
    "path": "eBook/06.9.md",
    "content": "# 6.9 应用闭包：将函数作为返回值\n\n在程序 [function_return.go](examples/chapter_6/function_return.go) 中我们将会看到函数 `Add2()` 和 `Adder()` 均会返回签名为 `func(b int) int` 的函数：\n\n```go\nfunc Add2() (func(b int) int)\nfunc Adder(a int) (func(b int) int)\n```\n\n函数 `Add2()` 不接受任何参数，但函数 `Adder()` 接受一个 `int` 类型的整数作为参数。\n\n我们也可以将 `Adder()` 返回的函数存到变量中 ([function_return.go](examples/chapter_6/function_return.go))。\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\t// make an Add2 function, give it a name p2, and call it:\n\tp2 := Add2()\n\tfmt.Printf(\"Call Add2 for 3 gives: %v\\n\", p2(3))\n\t// make a special Adder function, a gets value 2:\n\tTwoAdder := Adder(2)\n\tfmt.Printf(\"The result is: %v\\n\", TwoAdder(3))\n}\n\nfunc Add2() func(b int) int {\n\treturn func(b int) int {\n\t\treturn b + 2\n\t}\n}\n\nfunc Adder(a int) func(b int) int {\n\treturn func(b int) int {\n\t\treturn a + b\n\t}\n}\n```\n\n输出：\n\n```\nCall Add2 for 3 gives: 5\nThe result is: 5\n```\n\n下例为一个略微不同的实现 ([function_closure.go](examples/chapter_6/function_closure.go))：\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar f = Adder()\n\tfmt.Print(f(1), \" - \")\n\tfmt.Print(f(20), \" - \")\n\tfmt.Print(f(300))\n}\n\nfunc Adder() func(int) int {\n\tvar x int\n\treturn func(delta int) int {\n\t\tx += delta\n\t\treturn x\n\t}\n}\n```\n\n函数 `Adder()` 现在被赋值到变量 `f` 中（类型为 `func(int) int`）。\n\n输出：\n\n\t1 - 21 - 321\n\n三次调用函数 `f` 的过程中函数 `Adder()` 中变量 `delta` 的值分别为：1、20 和 300。\n\n我们可以看到，在多次调用中，变量 `x` 的值是被保留的，即 `0 + 1 = 1`，然后 `1 + 20 = 21`，最后 `21 + 300 = 321`：闭包函数保存并积累其中的变量的值，不管外部函数退出与否，它都能够继续操作外部函数中的局部变量。\n\n这些局部变量同样可以是参数，例如之前例子中的 `Adder(as int)`。\n\n这些例子清楚地展示了如何在 Go 语言中使用闭包。\n\n在闭包中使用到的变量可以是在闭包函数体内声明的，也可以是在外部函数声明的：\n\n```go\nvar g int\ngo func(i int) {\n\ts := 0\n\tfor j := 0; j < i; j++ { s += j }\n\tg = s\n}(1000) // Passes argument 1000 to the function literal.\n```\n\n这样闭包函数就能够被应用到整个集合的元素上，并修改它们的值。然后这些变量就可以用于表示或计算全局或平均值。\n\n**练习 6.9** [fibonacci_closure](exercises/chapter_6/fibonacci_closure.go)\n\n不使用递归但使用闭包改写第 6.6 节中的斐波那契数列程序。\n\n**练习 6.10** \n\n学习并理解以下程序的工作原理：\n\n一个返回值为另一个函数的函数可以被称之为工厂函数，这在您需要创建一系列相似的函数的时候非常有用：书写一个工厂函数而不是针对每种情况都书写一个函数。下面的函数演示了如何动态返回追加后缀的函数：\n\n```go\nfunc MakeAddSuffix(suffix string) func(string) string {\n\treturn func(name string) string {\n\t\tif !strings.HasSuffix(name, suffix) {\n\t\t\treturn name + suffix\n\t\t}\n\t\treturn name\n\t}\n}\n```\n\n现在，我们可以生成如下函数：\n\n```go\naddBmp := MakeAddSuffix(\".bmp\")\naddJpeg := MakeAddSuffix(\".jpeg\")\n```\n\n然后调用它们：\n\n```go\naddBmp(\"file\") // returns: file.bmp\naddJpeg(\"file\") // returns: file.jpeg\n```\n\n可以返回其它函数的函数和接受其它函数作为参数的函数均被称之为高阶函数，是函数式语言的特点。我们已经在[第 6.7 节](06.7.md)中得知函数也是一种值，因此很显然 Go 语言具有一些函数式语言的特性。闭包在 Go 语言中非常常见，常用于 goroutine 和管道操作（详见第 [14.8](14.8.md)-[14.9](14.9.md) 节）。在[第 11.14 节](11.14.md)的程序中，我们将会看到 Go 语言中的函数在处理混合对象时的强大能力。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[闭包](06.8.md)\n- 下一节：[使用闭包调试](06.10.md)\n"
  },
  {
    "path": "eBook/07.0.md",
    "content": "# 7.0 数组与切片\n\n这章我们开始剖析 **集合**，它是可以包含大量条目 (item) 的数据结构，例如数组、切片和 `map`。从这看到 Go 明显受到 Python 的影响。\n\n以 `[]` 符号标识的数组类型几乎在所有的编程语言中都是一个基本主力。Go 语言中的数组也是类似的，只是有一些特点。Go 没有 C 那么灵活，但是拥有切片 (slice) 类型。这是一种建立在 Go 语言数组类型之上的抽象，要想理解切片我们必须先理解数组。数组有特定的用处，但是却有一些呆板，所以在 Go 语言的代码里并不是特别常见。相对的，切片确实随处可见的。它们构建在数组之上并且提供更强大的能力和便捷。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[通过内存缓存来提升性能](06.12.md)\n- 下一节：[声明和初始化](07.1.md)\n"
  },
  {
    "path": "eBook/07.1.md",
    "content": "# 7.1 声明和初始化\n\n## 7.1.1 概念\n数组是具有相同 **唯一类型** 的一组已编号且长度固定的数据项序列（这是一种同构的数据结构）；这种类型可以是任意的原始类型例如整型、字符串或者自定义类型。数组长度必须是一个常量表达式，并且必须是一个非负整数。数组长度也是数组类型的一部分，所以 `[5]int` 和 `[10]int` 是属于不同类型的。数组的编译时值初始化是按照数组顺序完成的（如下）。\n\n**注意事项** 如果我们想让数组元素类型为任意类型的话可以使用空接口作为类型（参考 [第 11 章](11.9.md)）。当使用值时我们必须先做一个类型判断（参考 [第 11 章](11.3.md)）。\n\n数组元素可以通过 **索引**（位置）来读取（或者修改），索引从 `0` 开始，第一个元素索引为 `0`，第二个索引为 `1`，以此类推（数组以 0 开始在所有类 C 语言中是相似的）。元素的数目（也称为长度或者数组大小）必须是固定的并且在声明该数组时就给出（编译时需要知道数组长度以便分配内存）；数组长度最大为 2GB。\n\n声明的格式是： \n\n```go\nvar identifier [len]type\n```\n\n例如： \n\n```go\nvar arr1 [5]int\n```\n\n在内存中的结构是：![](images/7.1_fig7.1.png?raw=true)\n\n每个元素是一个整型值，当声明数组时所有的元素都会被自动初始化为默认值 0。\n\n`arr1` 的长度是 5，索引范围从 `0` 到 `len(arr1)-1`。\n\n第一个元素是 `arr1[0]`，第三个元素是 `arr1[2]`；总体来说索引 `i` 代表的元素是 `arr1[i]`，最后一个元素是 `arr1[len(arr1)-1]`。\n\n对索引项为 `i` 的数组元素赋值可以这么操作：`arr[i] = value`，所以数组是 **可变的**。\n\n只有有效的索引可以被使用，当使用等于或者大于 `len(arr1)` 的索引时：如果编译器可以检测到，会给出索引超限的提示信息；如果检测不到的话编译会通过而运行时会 `panic()`:（参考[第 13 章](13.0.md)）\n\n\truntime error: index out of range\n\n由于索引的存在，遍历数组的方法自然就是使用 `for` 结构：\n\n- 通过 `for` 初始化数组项\n- 通过 `for` 打印数组元素\n- 通过 `for` 依次处理元素\n\n示例 7.1 [for_arrays.go](examples/chapter_7/for_arrays.go)\n\n```go\npackage main\nimport \"fmt\"\n\nfunc main() {\n\tvar arr1 [5]int\n\n\tfor i:=0; i < len(arr1); i++ {\n\t\tarr1[i] = i * 2\n\t}\n\n\tfor i:=0; i < len(arr1); i++ {\n\t\tfmt.Printf(\"Array at index %d is %d\\n\", i, arr1[i])\n\t}\n}\n```\n\n输出结果：\n\n\tArray at index 0 is 0\n\tArray at index 1 is 2\n\tArray at index 2 is 4\n\tArray at index 3 is 6\n\tArray at index 4 is 8\n\n`for` 循环中的条件非常重要：`i < len(arr1)`，如果写成 `i <= len(arr1)` 的话会产生越界错误。\n\nIDIOM:\n\n```go\nfor i:=0; i < len(arr1); i++｛\n\tarr1[i] = ...\n}\n```\n\n也可以使用 for-range 的生成方式：\n\nIDIOM:\n\n```go\nfor i,_:= range arr1 {\n...\n}\n```\n\n在这里 `i` 也是数组的索引。当然这两种 `for` 结构对于切片（`slices`）（参考 [第 7 章](07.2.md)）来说也同样适用。\n\n**问题 7.1** 下面代码段的输出是什么？\n\n```go\na := [...]string{\"a\", \"b\", \"c\", \"d\"}\nfor i := range a {\n\tfmt.Println(\"Array item\", i, \"is\", a[i])\n}\n```\n\nGo 语言中的数组是一种 **值类型**（不像 C/C++ 中是指向首元素的指针），所以可以通过 `new()` 来创建： `var arr1 = new([5]int)`。\n\n那么这种方式和 `var arr2 [5]int` 的区别是什么呢？`arr1` 的类型是 `*[5]int`，而 `arr2` 的类型是 `[5]int`。\n\n这样的结果就是当把一个数组赋值给另一个时，需要再做一次数组内存的拷贝操作。例如：\n\n```go\narr2 := *arr1\narr2[2] = 100\n```\n\n这样两个数组就有了不同的值，在赋值后修改 `arr2` 不会对 `arr1` 生效。\n\n所以在函数中数组作为参数传入时，如 `func1(arr2)`，会产生一次数组拷贝，`func1()` 方法不会修改原始的数组 `arr2`。\n\n如果你想修改原数组，那么 `arr2` 必须通过 `&` 操作符以引用方式传过来，例如 `func1(&arr2)`，下面是一个例子：\n\n示例 7.2 [pointer_array.go](examples/chapter_7/pointer_array.go):\n\n```go\npackage main\nimport \"fmt\"\nfunc f(a [3]int) { fmt.Println(a) }\nfunc fp(a *[3]int) { fmt.Println(a) }\n\nfunc main() {\n\tvar ar [3]int\n\tf(ar) \t// passes a copy of ar\n\tfp(&ar) // passes a pointer to ar\n}\n```\n\n输出结果：\n\n\t[0 0 0]\n\t&[0 0 0]\n\n另一种方法就是生成数组切片并将其传递给函数（详见[第 7.1.4 节](07.1.md)）。\n\n**练习**\n\n练习7.1：[array_value.go](examples/chapter_7/array_value.go):\n\n证明当数组赋值时，发生了数组内存拷贝。\n\n练习7.2：[for_array.go](examples/chapter_7/for_array.go): \n\n写一个循环并用下标给数组赋值（从 0 到 15）并且将数组打印在屏幕上。\n\n练习7.3：[fibonacci_array.go](examples/chapter_7/fibonacci_array.go): \n\n在[第 6.6 节](06.6.md) 我们看到了一个递归计算 Fibonacci 数值的方法。但是通过数组我们可以更快的计算出 Fibonacci 数。完成该方法并打印出前 50 个 Fibonacci 数字。\n\n## 7.1.2 数组常量\n\n如果数组值已经提前知道了，那么可以通过 **数组常量** 的方法来初始化数组，而不用依次使用 `[]=` 方法（所有的组成元素都有相同的常量语法）。\n\n示例 7.3 [array_literals.go](examples/chapter_7/array_literals.go)\n\n```go\npackage main\nimport \"fmt\"\n\nfunc main() {\n\t// var arrAge = [5]int{18, 20, 15, 22, 16}\n\t// var arrLazy = [...]int{5, 6, 7, 8, 22}\n\t// var arrLazy = []int{5, 6, 7, 8, 22}\t//注：初始化得到的实际上是切片slice\n\tvar arrKeyValue = [5]string{3: \"Chris\", 4: \"Ron\"}\n\t// var arrKeyValue = []string{3: \"Chris\", 4: \"Ron\"}\t//注：初始化得到的实际上是切片slice\n\n\tfor i:=0; i < len(arrKeyValue); i++ {\n\t\tfmt.Printf(\"Person at %d is %s\\n\", i, arrKeyValue[i])\n\t}\n}\n```\n\n第一种变化：\n\n```go\nvar arrAge = [5]int{18, 20, 15, 22, 16}\n```\n\n注意 `[5]int` 可以从左边起开始忽略：`[10]int {1, 2, 3}` :这是一个有 10 个元素的数组，除了前三个元素外其他元素都为 `0`。\n\n第二种变化：\n\n```go\nvar arrLazy = [...]int{5, 6, 7, 8, 22}\n```\n\n`...` 同样可以忽略，从技术上说它们其实变成了切片。\n\n第三种变化：`key: value 语法`\n\n```go\nvar arrKeyValue = [5]string{3: \"Chris\", 4: \"Ron\"}\n```\n\n只有索引 3 和 4 被赋予实际的值，其他元素都被设置为空的字符串，所以输出结果为：\n\n\tPerson at 0 is\n\tPerson at 1 is\n\tPerson at 2 is\n\tPerson at 3 is Chris\n\tPerson at 4 is Ron\n\n在这里数组长度同样可以写成 `...`。\n\n你可以取任意数组常量的地址来作为指向新实例的指针。\n\n示例 7.4 [pointer_array2.go](examples/chapter_7/pointer_array2.go)\n\n```go\npackage main\nimport \"fmt\"\n\nfunc fp(a *[3]int) { fmt.Println(a) }\n\nfunc main() {\n\tfor i := 0; i < 3; i++ {\n\t\tfp(&[3]int{i, i * i, i * i * i})\n\t}\n}\n```\n\n输出结果：\n\n```\n&[0 0 0]\n&[1 1 1]\n&[2 4 8]\n```\n\n几何点（或者数学向量）是一个使用数组的经典例子。为了简化代码通常使用一个别名：\n\n```go\ntype Vector3D [3]float32\nvar vec Vector3D\n```\n\n## 7.1.3 多维数组\n\n数组通常是一维的，但是可以用来组装成多维数组，例如：`[3][5]int`，`[2][2][2]float64`。\n\n内部数组总是长度相同的。Go 语言的多维数组是矩形式的（唯一的例外是切片的数组，参见[第 7.2.5 节](07.2.md)。\n\n示例 7.5 [multidim_array.go](examples/chapter_7/multidim_array.go)\n    \n```go\npackage main\nconst (\n\tWIDTH  = 1920\n\tHEIGHT = 1080\n)\n\ntype pixel int\nvar screen [WIDTH][HEIGHT]pixel\n\nfunc main() {\n\tfor y := 0; y < HEIGHT; y++ {\n\t\tfor x := 0; x < WIDTH; x++ {\n\t\t\tscreen[x][y] = 0\n\t\t}\n\t}\n}\n```\n\n## 7.1.4 将数组传递给函数\n\n把一个大数组传递给函数会消耗很多内存。有两种方法可以避免这种情况：\n\n- 传递数组的指针\n- 使用数组的切片\n\n接下来的例子阐明了第一种方法：\n\n示例 7.6 [array_sum.go](examples/chapter_7/array_sum.go)\n    \n```go\npackage main\nimport \"fmt\"\n\nfunc main() {\n\tarray := [3]float64{7.0, 8.5, 9.1}\n\tx := Sum(&array) // Note the explicit address-of operator\n\t// to pass a pointer to the array\n\tfmt.Printf(\"The sum of the array is: %f\", x)\n}\n\nfunc Sum(a *[3]float64) (sum float64) {\n\tfor _, v := range a { // derefencing *a to get back to the array is not necessary!\n\t\tsum += v\n\t}\n\treturn\n}\n```\n\n输出结果：\n\n\tThe sum of the array is: 24.600000\n\n但这在 Go 中并不常用，通常使用切片（参考 [第 7.2 节](07.2.md)）。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[数组与切片](07.0.md)\n- 下一节：[切片](07.2.md)\n"
  },
  {
    "path": "eBook/07.2.md",
    "content": "# 7.2 切片\n\n## 7.2.1 概念\n\n切片 (slice) 是对数组一个连续片段的引用（该数组我们称之为相关数组，通常是匿名的），所以切片是一个引用类型（因此更类似于 C/C++ 中的数组类型，或者 Python 中的 list 类型）。这个片段可以是整个数组，或者是由起始和终止索引标识的一些项的子集。需要注意的是，终止索引标识的项不包括在切片内。切片提供了一个相关数组的动态窗口。\n\n切片是可索引的，并且可以由 `len()` 函数获取长度。\n\n给定项的切片索引可能比相关数组的相同元素的索引小。和数组不同的是，切片的长度可以在运行时修改，最小为 0， 最大为相关数组的长度：切片是一个 **长度可变的数组**。\n\n切片提供了计算容量的函数 `cap()` 可以测量切片最长可以达到多少：它等于切片的长度 + 数组除切片之外的长度。如果 `s` 是一个切片，`cap(s)` 就是从 `s[0]` 到数组末尾的数组长度。切片的长度永远不会超过它的容量，所以对于切片 `s` 来说该不等式永远成立：`0 <= len(s) <= cap(s)`。\n\n多个切片如果表示同一个数组的片段，它们可以共享数据；因此一个切片和相关数组的其他切片是共享存储的，相反，不同的数组总是代表不同的存储。数组实际上是切片的构建块。\n\n**优点** 因为切片是引用，所以它们不需要使用额外的内存并且比使用数组更有效率，所以在 Go 代码中切片比数组更常用。\n\n声明切片的格式是： `var identifier []type`（不需要说明长度）。\n\n一个切片在未初始化之前默认为 `nil`，长度为 0。\n\n切片的初始化格式是：`var slice1 []type = arr1[start:end]`。\n\n这表示 `slice1` 是由数组 `arr1` 从 `start` 索引到 `end-1` 索引之间的元素构成的子集（切分数组，`start:end` 被称为切片表达式）。所以 `slice1[0]` 就等于 `arr1[start]`。这可以在 `arr1` 被填充前就定义好。\n\n如果某个人写：`var slice1 []type = arr1[:]` 那么 `slice1` 就等于完整的 `arr1` 数组（所以这种表示方式是 `arr1[0:len(arr1)]` 的一种缩写）。另外一种表述方式是：`slice1 = &arr1`。\n\n`arr1[2:]` 和 `arr1[2:len(arr1)]` 相同，都包含了数组从第三个到最后的所有元素。\n\n`arr1[:3]` 和 `arr1[0:3]` 相同，包含了从第一个到第三个元素（不包括第四个）。\n\n如果你想去掉 `slice1` 的最后一个元素，只要 `slice1 = slice1[:len(slice1)-1]`。\n\n一个由数字 1、2、3 组成的切片可以这么生成：`s := [3]int{1,2,3}[:]`（注：应先用 `s := [3]int{1, 2, 3}` 生成数组, 再使用 `s[:]` 转成切片）甚至更简单的 `s := []int{1,2,3}`。\n\n`s2 := s[:]` 是用切片组成的切片，拥有相同的元素，但是仍然指向相同的相关数组。\n\n一个切片 `s` 可以这样扩展到它的大小上限：`s = s[:cap(s)]`，如果再扩大的话就会导致运行时错误（参见第 7.7 节）。\n\n对于每一个切片（包括 `string`），以下状态总是成立的：\n\n\ts == s[:i] + s[i:] // i是一个整数且: 0 <= i <= len(s)\n\tlen(s) <= cap(s)\n\n切片也可以用类似数组的方式初始化：`var x = []int{2, 3, 5, 7, 11}`。这样就创建了一个长度为 5 的数组并且创建了一个相关切片。\n\n切片在内存中的组织方式实际上是一个有 3 个域的结构体：指向相关数组的指针，切片长度以及切片容量。下图给出了一个长度为 2，容量为 4 的切片 `y`。\n\n- `y[0] = 3` 且 `y[1] = 5`。\n- 切片 `y[0:4]` 由 元素 `3`，`5`，`7` 和 `11` 组成。\n\n<img src=\"images/7.2_fig7.2.png?raw=true\" style=\"zoom: 50%;\" />\n\n示例 7.7 [array_slices.go](examples/chapter_7/array_slices.go)\n\n```go\npackage main\nimport \"fmt\"\n\nfunc main() {\n\tvar arr1 [6]int\n\tvar slice1 []int = arr1[2:5] // item at index 5 not included!\n\n\t// load the array with integers: 0,1,2,3,4,5\n\tfor i := 0; i < len(arr1); i++ {\n\t\tarr1[i] = i\n\t}\n\n\t// print the slice\n\tfor i := 0; i < len(slice1); i++ {\n\t\tfmt.Printf(\"Slice at %d is %d\\n\", i, slice1[i])\n\t}\n\n\tfmt.Printf(\"The length of arr1 is %d\\n\", len(arr1))\n\tfmt.Printf(\"The length of slice1 is %d\\n\", len(slice1))\n\tfmt.Printf(\"The capacity of slice1 is %d\\n\", cap(slice1))\n\n\t// grow the slice\n\tslice1 = slice1[0:4]\n\tfor i := 0; i < len(slice1); i++ {\n\t\tfmt.Printf(\"Slice at %d is %d\\n\", i, slice1[i])\n\t}\n\tfmt.Printf(\"The length of slice1 is %d\\n\", len(slice1))\n\tfmt.Printf(\"The capacity of slice1 is %d\\n\", cap(slice1))\n\n\t// grow the slice beyond capacity\n\t//slice1 = slice1[0:7 ] // panic: runtime error: slice bound out of range\n}\n```\n\n输出：  \n\n\tSlice at 0 is 2  \n\tSlice at 1 is 3  \n\tSlice at 2 is 4  \n\tThe length of arr1 is 6  \n\tThe length of slice1 is 3  \n\tThe capacity of slice1 is 4  \n\tSlice at 0 is 2  \n\tSlice at 1 is 3  \n\tSlice at 2 is 4  \n\tSlice at 3 is 5  \n\tThe length of slice1 is 4  \n\tThe capacity of slice1 is 4  \n\n如果 `s2` 是一个切片，你可以将 `s2` 向后移动一位 `s2 = s2[1:]`，但是末尾没有移动。切片只能向后移动，`s2 = s2[-1:]` 会导致编译错误。切片不能被重新分片以获取数组的前一个元素。\n\n**注意** 绝对不要用指针指向切片。切片本身已经是一个引用类型，所以它本身就是一个指针！！\n\n问题 7.2： 给定切片 `b:= []byte{'g', 'o', 'l', 'a', 'n', 'g'}`，那么 `b[1:4]`、`b[:2]`、`b[2:]` 和 `b[:]` 分别是什么？\n\n## 7.2.2 将切片传递给函数\n\n如果你有一个函数需要对数组做操作，你可能总是需要把参数声明为切片。当你调用该函数时，把数组分片，创建为一个切片引用并传递给该函数。这里有一个计算数组元素和的方法:\n\n```go\nfunc sum(a []int) int {\n\ts := 0\n\tfor i := 0; i < len(a); i++ {\n\t\ts += a[i]\n\t}\n\treturn s\n}\n\nfunc main() {\n\tvar arr = [5]int{0, 1, 2, 3, 4}\n\tsum(arr[:])\n}\n```\n\n## 7.2.3 用 make() 创建一个切片\n\n当相关数组还没有定义时，我们可以使用 `make()` 函数来创建一个切片，同时创建好相关数组：`var slice1 []type = make([]type, len)`。\n\n也可以简写为 `slice1 := make([]type, len)`，这里 `len` 是数组的长度并且也是 `slice` 的初始长度。\n\n所以定义 `s2 := make([]int, 10)`，那么 `cap(s2) == len(s2) == 10`。\n\n`make()` 接受 2 个参数：元素的类型以及切片的元素个数。\n\n如果你想创建一个 `slice1`，它不占用整个数组，而只是占用以 `len` 为个数个项，那么只要：`slice1 := make([]type, len, cap)`。\n\n`make()` 的使用方式是：`func make([]T, len, cap)`，其中 `cap` 是可选参数。\n\n所以下面两种方法可以生成相同的切片:\n\n```go\nmake([]int, 50, 100)\nnew([100]int)[0:50]\n```\n\n下图描述了使用 `make()` 方法生成的切片的内存结构：\n\n<img src=\"images/7.2_fig7.2.1.png?raw=true\" style=\"zoom:50%;\" />\n\n示例 7.8 [make_slice.go](examples/chapter_7/make_slice.go)\n\n```go\npackage main\nimport \"fmt\"\n\nfunc main() {\n\tvar slice1 []int = make([]int, 10)\n\t// load the array/slice:\n\tfor i := 0; i < len(slice1); i++ {\n\t\tslice1[i] = 5 * i\n\t}\n\n\t// print the slice:\n\tfor i := 0; i < len(slice1); i++ {\n\t\tfmt.Printf(\"Slice at %d is %d\\n\", i, slice1[i])\n\t}\n\tfmt.Printf(\"\\nThe length of slice1 is %d\\n\", len(slice1))\n\tfmt.Printf(\"The capacity of slice1 is %d\\n\", cap(slice1))\n}\n```\n\n输出：  \n\n\tSlice at 0 is 0  \n\tSlice at 1 is 5  \n\tSlice at 2 is 10  \n\tSlice at 3 is 15  \n\tSlice at 4 is 20  \n\tSlice at 5 is 25  \n\tSlice at 6 is 30  \n\tSlice at 7 is 35  \n\tSlice at 8 is 40  \n\tSlice at 9 is 45  \n\t\n\tThe length of slice1 is 10  \n\tThe capacity of slice1 is 10  \n\n因为字符串是纯粹不可变的字节数组，它们也可以被切分成切片。\n\n练习 7.4： [fibonacci_funcarray.go](examples/chapter_7/fibonacci_funcarray.go): 为练习 7.3 写一个新的版本，主函数调用一个使用序列个数作为参数的函数，该函数返回一个大小为序列个数的 Fibonacci 切片。\n\n## 7.2.4 new() 和 make() 的区别\n\n看起来二者没有什么区别，都在堆上分配内存，但是它们的行为不同，适用于不同的类型。\n\n- `new(T)` 为每个新的类型 `T` 分配一片内存，初始化为 `0` 并且返回类型为 `*T` 的内存地址：这种方法 **返回一个指向类型为 `T`，值为 `0` 的地址的指针**，它适用于值类型如数组和结构体（参见[第 10 章](10.0.md)）；它相当于 `&T{}`。\n- `make(T)` **返回一个类型为 T 的初始值**，它只适用于 3 种内建的引用类型：切片、`map` 和 `channel`（参见[第 8 章](08.0.md)和[第 13 章](13.0.md)）。\n\n换言之，`new()` 函数分配内存，`make()` 函数初始化；下图给出了区别：\n\n<img src=\"images/7.2_fig7.3.png?raw=true\" style=\"zoom:50%;\" />\n\n在图 7.3 的第一幅图中：\n\n```go\nvar p *[]int = new([]int) // *p == nil; with len and cap 0\np := new([]int)\n```\n\n在第二幅图中， `p := make([]int, 0)` ，切片 已经被初始化，但是指向一个空的数组。\n\n以上两种方式实用性都不高。下面的方法：\n\n```go\nvar v []int = make([]int, 10, 50)\n```\n\n或者\n\t\n```go\nv := make([]int, 10, 50)\n```\n\n这样分配一个有 50 个 `int` 值的数组，并且创建了一个长度为 10，容量为 50 的切片 `v`，该切片指向数组的前 10 个元素。\n\n**问题 7.3** 给定 `s := make([]byte, 5)`，`len(s)` 和 `cap(s)` 分别是多少？`s = s[2:4]`，`len(s)` 和 `cap(s)` 又分别是多少？\n\n**问题 7.4** 假设 `s1 := []byte{'p', 'o', 'e', 'm'}` 且 `s2 := s1[2:]`，`s2` 的值是多少？如果我们执行 `s2[1] = 't'`，`s1` 和 `s2` 现在的值又分别是多少？\n\n*译者注：如何理解 new、make、slice、map、channel 的关系*\n\n*1.slice、map 以及 channel 都是 golang 内建的一种引用类型，三者在内存中存在多个组成部分，\n需要对内存组成部分初始化后才能使用，而 make 就是对三者进行初始化的一种操作方式*\n\n*2. new 获取的是存储指定变量内存地址的一个变量，对于变量内部结构并不会执行相应的初始化操作，\n所以 slice、map、channel 需要 make 进行初始化并获取对应的内存地址，而非 new 简单的获取内存地址*\n\n## 7.2.5 多维切片\n\n和数组一样，切片通常也是一维的，但是也可以由一维组合成高维。通过分片的分片（或者切片的数组），长度可以任意动态变化，所以 Go 语言的多维切片可以任意切分。而且，内层的切片必须单独分配（通过 `make()` 函数）。\n\n## 7.2.6 bytes 包\n\n类型 `[]byte` 的切片十分常见，Go 语言有一个 `bytes` 包专门用来提供这种类型的操作方法。\n\n`bytes` 包和字符串包十分类似（参见[第 4.7 节](04.7.md)）。而且它还包含一个十分有用的类型 `Buffer`:\n\n```go\nimport \"bytes\"\n\ntype Buffer struct {\n\t...\n}\n```\n\n这是一个长度可变的 `bytes` 的 buffer，提供 `Read()` 和 `Write()` 方法，因为读写长度未知的 `bytes` 最好使用 `buffer`。\n\n`Buffer` 可以这样定义：`var buffer bytes.Buffer`。\n\n或者使用 `new()` 获得一个指针：`var r *bytes.Buffer = new(bytes.Buffer)`。\n\n或者通过函数：`func NewBuffer(buf []byte) *Buffer`，创建一个 `Buffer` 对象并且用 `buf` 初始化好；`NewBuffer` 最好用在从 `buf` 读取的时候使用。\n\n**通过 buffer 串联字符串**\n\n类似于 Java 的 StringBuilder 类。\n\n在下面的代码段中，我们创建一个 `buffer`，通过 `buffer.WriteString(s)` 方法将字符串 `s` 追加到后面，最后再通过 `buffer.String()` 方法转换为 `string`：\n\n```go\nvar buffer bytes.Buffer\nfor {\n\tif s, ok := getNextString(); ok { //method getNextString() not shown here\n\t\tbuffer.WriteString(s)\n\t} else {\n\t\tbreak\n\t}\n}\nfmt.Print(buffer.String(), \"\\n\")\n```\n\n这种实现方式比使用 `+=` 要更节省内存和 CPU，尤其是要串联的字符串数目特别多的时候。\n\n**练习 7.5** \n\n给定切片 `sl`，将一个 `[]byte` 数组追加到 `sl` 后面。写一个函数 `Append(slice, data []byte) []byte`，该函数在 `sl` 不能存储更多数据的时候自动扩容。\n\n**练习 7.6** \n\n把一个缓存 `buf` 分片成两个切片：第一个是前 `n` 个 bytes，后一个是剩余的，用一行代码实现。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[声明和初始化](07.1.md)\n- 下一节：[For-range 结构](07.3.md)\n"
  },
  {
    "path": "eBook/07.3.md",
    "content": "# 7.3 For-range 结构\n\n这种构建方法可以应用于数组和切片:\n\n```go\nfor ix, value := range slice1 {\n\t...\n}\n```\n\n第一个返回值 `ix` 是数组或者切片的索引，第二个是在该索引位置的值；他们都是仅在 `for` 循环内部可见的局部变量。`value` 只是 `slice1` 某个索引位置的值的一个拷贝，不能用来修改 `slice1` 该索引位置的值。\n\n示例 7.9 [slices_forrange.go](examples/chapter_7/slices_forrange.go)\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar slice1 []int = make([]int, 4)\n\n\tslice1[0] = 1\n\tslice1[1] = 2\n\tslice1[2] = 3\n\tslice1[3] = 4\n\n\tfor ix, value := range slice1 {\n\t\tfmt.Printf(\"Slice at %d is: %d\\n\", ix, value)\n\t}\n}\n```\n\n示例 7.10 [slices_forrange2.go](examples/chapter_7/slices_forrange2.go)\n\n```go\npackage main\nimport \"fmt\"\n\nfunc main() {\n\tseasons := []string{\"Spring\", \"Summer\", \"Autumn\", \"Winter\"}\n\tfor ix, season := range seasons {\n\t\tfmt.Printf(\"Season %d is: %s\\n\", ix, season)\n\t}\n\n\tvar season string\n\tfor _, season = range seasons {\n\t\tfmt.Printf(\"%s\\n\", season)\n\t}\n}\n```\n\nslices_forrange2.go 给出了一个关于字符串的例子， `_` 可以用于忽略索引。\n\n如果你只需要索引，你可以忽略第二个变量，例如：\n\n```go\nfor ix := range seasons {\n\tfmt.Printf(\"%d\", ix)\n}\n// Output: 0 1 2 3\n```\n\n如果你需要修改 `seasons[ix]` 的值可以使用这个版本。\n\n**多维切片下的 for-range：**\n\n通过计算行数和矩阵值可以很方便的写出如（参考[第 7.1.3 节](07.1.md)）的 `for` 循环来，例如（参考[第 7.5 节](07.5.md)的例子 [multidim_array.go](exercises/chapter_7/multidim_array.go)）：\n\n```go\nfor row := range screen {\n\tfor column := range screen[row] {\n\t\tscreen[row][column] = 1\n\t}\n}\n```\n\n**问题 7.5** 假设我们有如下数组：`items := [...]int{10, 20, 30, 40, 50}`\n\na) 如果我们写了如下的 `for` 循环，那么执行完 `for` 循环后的 `items` 的值是多少？如果你不确定的话可以测试一下:)\n\n```go\nfor _, item := range items {\n\titem *= 2\n}\n```\n\nb) 如果 a) 无法正常工作，写一个 `for` 循环让值可以变成自身的两倍。\n\n**问题 7.6** 通过使用省略号操作符 `...` 来实现累加方法。\n\n**练习 7.7** [sum_array.go](exercises/chapter_7/sum_array.go)\n\na) 写一个 `Sum()` 函数，传入参数为一个 `float32` 数组成的数组 `arrF`，返回该数组的所有数字和。\n\n如果把数组修改为切片的话代码要做怎样的修改？如果用切片形式方法实现不同长度数组的的和呢？\n\nb) 写一个 `SumAndAverage()` 方法，返回两个 int 和 `float32` 类型的未命名变量的和与平均值。\n\n**练习 7.8** [min_max.go](exercises/chapter_7/min_max.go)\n\n写一个 `minSlice()` 方法，传入一个 `int` 的切片并且返回最小值，再写一个 `maxSlice()` 方法返回最大值。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[切片](07.2.md)\n- 下一节：[切片重组 (reslice)](07.4.md)\n\n"
  },
  {
    "path": "eBook/07.4.md",
    "content": "# 7.4 切片重组 (reslice)\n\n我们已经知道切片创建的时候通常比相关数组小，例如：\n\n```go\nslice1 := make([]type, start_length, capacity)\n```\n\n其中 `start_length` 作为切片初始长度而 `capacity` 作为相关数组的长度。\n\n这么做的好处是我们的切片在达到容量上限后可以扩容。改变切片长度的过程称之为切片重组 **reslicing**，做法如下：`slice1 = slice1[0:end]`，其中 `end` 是新的末尾索引（即长度）。\n\n将切片扩展 1 位可以这么做：\n\n```go\nsl = sl[0:len(sl)+1]\n```\n\n切片可以反复扩展直到占据整个相关数组。\n\n示例 7.11 [reslicing.go](examples/chapter_7/reslicing.go)\n\n```go\npackage main\nimport \"fmt\"\n\nfunc main() {\n\tslice1 := make([]int, 0, 10)\n\t// load the slice, cap(slice1) is 10:\n\tfor i := 0; i < cap(slice1); i++ {\n\t\tslice1 = slice1[0:i+1]\n\t\tslice1[i] = i\n\t\tfmt.Printf(\"The length of slice is %d\\n\", len(slice1))\n\t}\n\n\t// print the slice:\n\tfor i := 0; i < len(slice1); i++ {\n\t\tfmt.Printf(\"Slice at %d is %d\\n\", i, slice1[i])\n\t}\n}\n```\n\n输出结果：\n\n\tThe length of slice is 1\n\tThe length of slice is 2\n\tThe length of slice is 3\n\tThe length of slice is 4\n\tThe length of slice is 5\n\tThe length of slice is 6\n\tThe length of slice is 7\n\tThe length of slice is 8\n\tThe length of slice is 9\n\tThe length of slice is 10\n\tSlice at 0 is 0\n\tSlice at 1 is 1\n\tSlice at 2 is 2\n\tSlice at 3 is 3\n\tSlice at 4 is 4\n\tSlice at 5 is 5\n\tSlice at 6 is 6\n\tSlice at 7 is 7\n\tSlice at 8 is 8\n\tSlice at 9 is 9\n\n另一个例子：\n\n```go\nvar ar = [10]int{0,1,2,3,4,5,6,7,8,9}\nvar a = ar[5:7] // reference to subarray {5,6} - len(a) is 2 and cap(a) is 5\n```\n\n将 `a` 重新分片：\n\n```go\na = a[0:4] // ref of subarray {5,6,7,8} - len(a) is now 4 but cap(a) is still 5\n```\n\n**问题 7.7**\n\n1) 如果 `a` 是一个切片，那么 `a[n:n]` 的长度是多少？\n\n2) `a[n:n+1]` 的长度又是多少？          \n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[For-range 结构](07.3.md)\n- 下一节：[切片的复制与追加](07.5.md)\n"
  },
  {
    "path": "eBook/07.5.md",
    "content": "# 7.5 切片的复制与追加\n\n如果想增加切片的容量，我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。下面的代码描述了从拷贝切片的 `copy` 函数和向切片追加新元素的 `append()` 函数。\n\n示例 7.12 [copy_append_slice.go](examples/chapter_7/copy_append_slice.go)\n\n```go\npackage main\nimport \"fmt\"\n\nfunc main() {\n\tslFrom := []int{1, 2, 3}\n\tslTo := make([]int, 10)\n\n\tn := copy(slTo, slFrom)\n\tfmt.Println(slTo)\n\tfmt.Printf(\"Copied %d elements\\n\", n) // n == 3\n\n\tsl3 := []int{1, 2, 3}\n\tsl3 = append(sl3, 4, 5, 6)\n\tfmt.Println(sl3)\n}\n```\n\n`func append(s[]T, x ...T) []T` 其中 `append()` 方法将 0 个或多个具有相同类型 `s` 的元素追加到切片后面并且返回新的切片；追加的元素必须和原切片的元素是同类型。如果 `s` 的容量不足以存储新增元素，`append()` 会分配新的切片来保证已有切片元素和新增元素的存储。因此，返回的切片可能已经指向一个不同的相关数组了。`append()` 方法总是返回成功，除非系统内存耗尽了。\n\n如果你想将切片 `y` 追加到切片 `x` 后面，只要将第二个参数扩展成一个列表即可：`x = append(x, y...)`。\n\n**注意**： `append()` 在大多数情况下很好用，但是如果你想完全掌控整个追加过程，你可以实现一个这样的 `AppendByte()` 方法：\n\n```go\nfunc AppendByte(slice []byte, data ...byte) []byte {\n\tm := len(slice)\n\tn := m + len(data)\n\tif n > cap(slice) { // if necessary, reallocate\n\t\t// allocate double what's needed, for future growth.\n\t\tnewSlice := make([]byte, (n+1)*2)\n\t\tcopy(newSlice, slice)\n\t\tslice = newSlice\n\t}\n\tslice = slice[0:n]\n\tcopy(slice[m:n], data)\n\treturn slice\n}\n```\n\n`func copy(dst, src []T) int` 方法将类型为 `T` 的切片从源地址 `src` 拷贝到目标地址 `dst`，覆盖 `dst` 的相关元素，并且返回拷贝的元素个数。源地址和目标地址可能会有重叠。拷贝个数是 `src` 和 `dst` 的长度最小值。如果 `src` 是字符串那么元素类型就是 `byte`。如果你还想继续使用 `src`，在拷贝结束后执行 `src = dst`。\n\n**练习 7.9** [magnify_slice.go](exercises/chapter_7/magnify_slice.go)\n\n给定一个切片 `s []int` 和一个 `int` 类型的因子 `factor`，扩展 `s` 使其长度为 `len(s) * factor`。\n\n**练习 7.10 ** [filter_slice.go](exercises/chapter_7/filter_slice.go)\n\n用顺序函数过滤容器：`s` 是前 10 个整型的切片。构造一个函数 `Filter`，第一个参数是 `s`，第二个参数是一个 `fn func(int) bool`，返回满足函数 `fn` 的元素切片。通过 `fn` 测试方法测试当整型值是偶数时的情况。\n\n**练习 7.11**  [insert_slice.go](exercises/chapter_7/insert_slice.go)\n\n写一个函数 `InsertStringSlice()` 将切片插入到另一个切片的指定位置。\n\n**练习 7.12** [remove_slice.go](exercises/chapter_7/remove_slice.go)\n\n写一个函数 `RemoveStringSlice()` 将从 `start` 到 `end` 索引的元素从切片中移除。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[切片重组 (reslice)](07.4.md)\n- 下一节：[字符串、数组和切片的应用](07.6.md)\n"
  },
  {
    "path": "eBook/07.6.md",
    "content": "# 7.6 字符串、数组和切片的应用\n\n## 7.6.1 从字符串生成字节切片\n\n假设 `s` 是一个字符串（本质上是一个字节数组），那么就可以直接通过 `c := []byte(s)` 来获取一个字节的切片 `c` 。另外，您还可以通过 `copy()` 函数来达到相同的目的：`copy(dst []byte, src string)`。\n\n同样的，还可以使用 for-range 来获得每个元素（Listing 7.13 — [for_string.go](examples/chapter_7/for_string.go)）：\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n    s := \"\\u00ff\\u754c\"\n    for i, c := range s {\n        fmt.Printf(\"%d:%c \", i, c)\n    }\n}\n```\n\n输出：\n\n    0:ÿ 2:界\n\n我们知道，Unicode 字符会占用 2 个字节，有些甚至需要 3 个或者 4 个字节来进行表示。如果发现错误的 UTF8 字符，则该字符会被设置为 `U+FFFD` 并且索引向前移动一个字节。和字符串转换一样，您同样可以使用 `c := []int32(s)` 语法，这样切片中的每个 `int` 都会包含对应的 Unicode 代码，因为字符串中的每次字符都会对应一个整数。类似的，您也可以将字符串转换为元素类型为 `rune` 的切片：`r := []rune(s)`。\n\n可以通过代码 `len([]int32(s))` 来获得字符串中字符的数量，但使用 `utf8.RuneCountInString(s)` 效率会更高一点。(参考 [count_characters.go](exercises/chapter_4/count_characters.go))\n\n您还可以将一个字符串追加到某一个字节切片的尾部：\n\n```go\nvar b []byte\nvar s string\nb = append(b, s...)\n```\n\n## 7.6.2 获取字符串的某一部分\n\n使用 `substr := str[start:end]` 可以从字符串 str 获取到从索引 `start` 开始到 `end-1` 位置的子字符串。同样的，`str[start:]` 则表示获取从 `start` 开始到 `len(str)-1` 位置的子字符串。而 `str[:end]` 表示获取从 0 开始到 `end-1` 的子字符串。\n\n## 7.6.3 字符串和切片的内存结构\n\n在内存中，一个字符串实际上是一个双字结构，即一个指向实际数据的指针和记录字符串长度的整数（见图 7.4）。因为指针对用户来说是完全不可见，因此我们可以依旧把字符串看做是一个值类型，也就是一个字符数组。\n\n字符串 `string s = \"hello\"` 和子字符串 `t = s[2:3]` 在内存中的结构可以用下图表示：\n\n![](images/7.6_fig7.4.png)\n\n## 7.6.4 修改字符串中的某个字符\n\nGo 语言中的字符串是不可变的，也就是说 `str[index]` 这样的表达式是不可以被放在等号左侧的。如果尝试运行 `str[i] = 'D'` 会得到错误：`cannot assign to str[i]`。\n\n因此，您必须先将字符串转换成字节数组，然后再通过修改数组中的元素值来达到修改字符串的目的，最后将字节数组转换回字符串格式。\n\n例如，将字符串 `\"hello\"` 转换为 `\"cello\"`：\n\n```go\ns := \"hello\"\nc := []byte(s)\nc[0] = 'c'\ns2 := string(c) // s2 == \"cello\"\n```\n\n所以，您可以通过操作切片来完成对字符串的操作。\n\n## 7.6.5 字节数组对比函数\n\n下面的 `Compare()` 函数会返回两个字节数组字典顺序的整数对比结果，即 `0 if a == b, -1 if a < b, 1 if a > b`。\n\n```go\nfunc Compare(a, b[]byte) int {\n    for i:=0; i < len(a) && i < len(b); i++ {\n        switch {\n        case a[i] > b[i]:\n            return 1\n        case a[i] < b[i]:\n            return -1\n        }\n    }\n    // 数组的长度可能不同\n    switch {\n    case len(a) < len(b):\n        return -1\n    case len(a) > len(b):\n        return 1\n    }\n    return 0 // 数组相等\n}\n```\n\n## 7.6.6 搜索及排序切片和数组\n\n标准库提供了 `sort` 包来实现常见的搜索和排序操作。您可以使用 `sort` 包中的函数 `func Ints(a []int)` 来实现对 `int` 类型的切片排序。例如 `sort.Ints(arri)`，其中变量 `arri` 就是需要被升序排序的数组或切片。为了检查某个数组是否已经被排序，可以通过函数 `IntsAreSorted(a []int) bool` 来检查，如果返回 `true` 则表示已经被排序。\n\n类似的，可以使用函数 `func Float64s(a []float64)` 来排序 `float64` 的元素，或使用函数 `func Strings(a []string)` 排序字符串元素。\n\n想要在数组或切片中搜索一个元素，该数组或切片必须先被排序（因为标准库的搜索算法使用的是二分法）。然后，您就可以使用函数 `func SearchInts(a []int, n int) int` 进行搜索，并返回对应结果的索引值。\n\n当然，还可以搜索 `float64` 和字符串：\n\n```go\nfunc SearchFloat64s(a []float64, x float64) int\nfunc SearchStrings(a []string, x string) int\n```\n\n您可以通过查看 [官方文档](http://golang.org/pkg/sort/) 来获取更详细的信息。\n\n这就是如何使用 `sort` 包的方法，我们会在[第 11.7 节](11.7.md) 对它的细节进行深入，并实现一个属于我们自己的版本。\n\n## 7.6.7 append() 函数常见操作\n\n我们在[第 7.5 节](07.5.md)提到的 `append()` 非常有用，它能够用于各种方面的操作：\n\n1. 将切片 `b` 的元素追加到切片 `a` 之后：`a = append(a, b...)`\n2. 复制切片 `a` 的元素到新的切片 `b` 上：\n\n    ```go\n    b = make([]T, len(a))\n    copy(b, a)\n    ```\n\n3. 删除位于索引 `i` 的元素：`a = append(a[:i], a[i+1:]...)`\n4. 切除切片 `a` 中从索引 `i` 至 `j` 位置的元素：`a = append(a[:i], a[j:]...)`\n5. 为切片 `a` 扩展 `j` 个元素长度：`a = append(a, make([]T, j)...)`\n6. 在索引 `i` 的位置插入元素 `x`：`a = append(a[:i], append([]T{x}, a[i:]...)...)`\n7. 在索引 `i` 的位置插入长度为 `j` 的新切片：`a = append(a[:i], append(make([]T, j), a[i:]...)...)`\n8. 在索引 `i` 的位置插入切片 `b` 的所有元素：`a = append(a[:i], append(b, a[i:]...)...)`\n9. 取出位于切片 `a` 最末尾的元素 `x`：`x, a = a[len(a)-1], a[:len(a)-1]`\n10. 将元素 `x` 追加到切片 `a`：`a = append(a, x)`\n\n因此，您可以使用切片和 `append()` 操作来表示任意可变长度的序列。\n\n从数学的角度来看，切片相当于向量，如果需要的话可以定义一个向量作为切片的别名来进行操作。\n\n如果您需要更加完整的方案，可以学习一下 Eleanor McHugh 编写的几个包：[`slices`](http://github.com/feyeleanor/slices)、[`chain`](http://github.com/feyeleanor/chain) 和 [`lists`](http://github.com/feyeleanor/lists)。\n\n## 7.6.8 切片和垃圾回收\n\n切片的底层指向一个数组，该数组的实际容量可能要大于切片所定义的容量。只有在没有任何切片指向的时候，底层的数组内存才会被释放，这种特性有时会导致程序占用多余的内存。\n\n**示例** 函数 `FindDigits()` 将一个文件加载到内存，然后搜索其中所有的数字并返回一个切片。\n\n```go\nvar digitRegexp = regexp.MustCompile(\"[0-9]+\")\n\nfunc FindDigits(filename string) []byte {\n    b, _ := ioutil.ReadFile(filename)\n    return digitRegexp.Find(b)\n}\n```\n\n这段代码可以顺利运行，但返回的 `[]byte` 指向的底层是整个文件的数据。只要该返回的切片不被释放，垃圾回收器就不能释放整个文件所占用的内存。换句话说，一点点有用的数据却占用了整个文件的内存。\n\n想要避免这个问题，可以通过拷贝我们需要的部分到一个新的切片中：\n\n```go\nfunc FindDigits(filename string) []byte {\n   b, _ := ioutil.ReadFile(filename)\n   b = digitRegexp.Find(b)\n   c := make([]byte, len(b))\n   copy(c, b)\n   return c\n}\n```\n事实上，上面这段代码只能找到第一个匹配正则表达式的数字串。要想找到所有的数字，可以尝试下面这段代码：\n```go\nfunc FindFileDigits(filename string) []byte {\n   fileBytes, _ := ioutil.ReadFile(filename)\n   b := digitRegexp.FindAll(fileBytes, len(fileBytes))\n   c := make([]byte, 0)\n   for _, bytes := range b {\n      c = append(c, bytes...)\n   }\n   return c\n}\n```\n\n**练习 7.12** [split_string.go](exercises/chapter_7/split_string.go)\n\n编写一个函数，要求其接受两个参数，原始字符串 `str` 和分割索引 `i`，然后返回两个分割后的字符串。\n\n**练习 7.13** [string_split2.go](exercises/chapter_7/string_split2.go)\n\n假设有字符串 `str`，那么 `str[len(str)/2:] + str[:len(str)/2]` 的结果是什么？\n\n**练习 7.14** [string_reverse.go](exercises/chapter_7/string_reverse.go)\n\n编写一个程序，要求能够反转字符串，即将 `\"Google\"` 转换成 `\"elgooG\"`（提示：使用 `[]byte` 类型的切片）。\n\n如果您使用两个切片来实现反转，请再尝试使用一个切片（提示：使用交换法）。\n\n如果您想要反转 Unicode 编码的字符串，请使用 `[]int32` 类型的切片。\n\n**练习 7.15** [Q29_uniq.go](exercises/chapter_7/uniq.go)\n\n编写一个程序，要求能够遍历一个字符数组，并将当前字符和前一个字符不相同的字符拷贝至另一个数组。\n\n**练习 7.16** [bubblesort.go](exercises/chapter_7/bubblesort.go)\n\n编写一个程序，使用冒泡排序的方法排序一个包含整数的切片（算法的定义可参考 [维基百科](http://en.wikipedia.org/wiki/Bubble_sort)）。\n\n**练习 7.17** [map_function.go](exercises/chapter_7/map_function.go)\n\n在函数式编程语言中，一个 map-function 是指能够接受一个函数原型和一个列表，并使用列表中的值依次执行函数原型，公式为：`map ( F(), (e1,e2, . . . ,en) ) = ( F(e1), F(e2), ... F(en) )`。\n\n编写一个函数 `mapFunc` 要求接受以下 2 个参数：\n\n- 一个将整数乘以 10 的函数\n- 一个整数列表\n\n最后返回保存运行结果的整数列表。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[切片的复制与追加](07.5.md)\n- 下一章：[Map](08.0.md)\n"
  },
  {
    "path": "eBook/08.0.md",
    "content": "# 8.0 Map\n\n`map` 是一种特殊的数据结构：一种元素对 (pair) 的无序集合，pair 的一个元素是 key，对应的另一个元素是 value，所以这个结构也称为关联数组或字典。这是一种快速寻找值的理想结构：给定 key，对应的 value 可以迅速定位。\n\n`map` 这种数据结构在其他编程语言中也称为字典 (Python) 、hash 和 HashTable 等。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[字符串、数组和切片的应用](07.6.md)\n- 下一节：[声明、初始化和 make](08.1.md)\n"
  },
  {
    "path": "eBook/08.1.md",
    "content": "# 8.1 声明、初始化和 make\n\n## 8.1.1 概念\n\n`map` 是引用类型，可以使用如下声明：\n\n```go\nvar map1 map[keytype]valuetype\nvar map1 map[string]int\n```\n\n（`[keytype]` 和 `valuetype` 之间允许有空格，但是 gofmt 移除了空格）\n\n在声明的时候不需要知道 `map` 的长度，`map` 是可以动态增长的。\n\n未初始化的 `map` 的值是 `nil`。\n\nkey 可以是任意可以用 `==` 或者 `!=` 操作符比较的类型，比如 `string`、`int`、`float32(64)`。所以数组、切片和结构体不能作为 key (译者注：含有数组切片的结构体不能作为 key，只包含内建类型的 `struct` 是可以作为 key 的），但是指针和接口类型可以。如果要用结构体作为 key 可以提供 `Key()` 和 `Hash()` 方法，这样可以通过结构体的域计算出唯一的数字或者字符串的 key。\n\nvalue 可以是任意类型的；通过使用空接口类型（详见[第 11.9 节](11.9.md)），我们可以存储任意值，但是使用这种类型作为值时需要先做一次类型断言（详见[第 11.3 节](11.3.md)）。\n\n`map` 传递给函数的代价很小：在 32 位机器上占 4 个字节，64 位机器上占 8 个字节，无论实际上存储了多少数据。通过 key 在 `map` 中寻找值是很快的，比线性查找快得多，但是仍然比从数组和切片的索引中直接读取要慢 100 倍；所以如果你很在乎性能的话还是建议用切片来解决问题。\n\n`map` 也可以用函数作为自己的值，这样就可以用来做分支结构（详见[第 5 章](05.0.md)）：key 用来选择要执行的函数。\n\n如果 `key1` 是 `map1` 的 key，那么 `map1[key1]` 就是对应 `key1` 的值，就如同数组索引符号一样（数组可以视为一种简单形式的 `map`，key 是从 0 开始的整数）。\n\n`key1` 对应的值可以通过赋值符号来设置为 val1：`map1[key1] = val1`。\n\n令 `v := map1[key1]` 可以将 `key1` 对应的值赋值给 `v`；如果 `map` 中没有 `key1` 存在，那么 `v` 将被赋值为 `map1` 的值类型的空值。\n\n常用的 `len(map1)` 方法可以获得 `map` 中的 pair 数目，这个数目是可以伸缩的，因为 map-pairs 在运行时可以动态添加和删除。\n\n示例 8.1 [make_maps.go](examples/chapter_8/make_maps.go)\n\n```go\npackage main\nimport \"fmt\"\n\nfunc main() {\n\tvar mapLit map[string]int\n\t//var mapCreated map[string]float32\n\tvar mapAssigned map[string]int\n\n\tmapLit = map[string]int{\"one\": 1, \"two\": 2}\n\tmapCreated := make(map[string]float32)\n\tmapAssigned = mapLit\n\n\tmapCreated[\"key1\"] = 4.5\n\tmapCreated[\"key2\"] = 3.14159\n\tmapAssigned[\"two\"] = 3\n\n\tfmt.Printf(\"Map literal at \\\"one\\\" is: %d\\n\", mapLit[\"one\"])\n\tfmt.Printf(\"Map created at \\\"key2\\\" is: %f\\n\", mapCreated[\"key2\"])\n\tfmt.Printf(\"Map assigned at \\\"two\\\" is: %d\\n\", mapLit[\"two\"])\n\tfmt.Printf(\"Map literal at \\\"ten\\\" is: %d\\n\", mapLit[\"ten\"])\n}\n```\n\n输出结果：\n\n\tMap literal at \"one\" is: 1\n\tMap created at \"key2\" is: 3.141590\n\tMap assigned at \"two\" is: 3\n\tMpa literal at \"ten\" is: 0\n\n`mapLit` 说明了 `map literals` 的使用方法： `map` 可以用 `{key1: val1, key2: val2}` 的描述方法来初始化，就像数组和结构体一样。\n\n`map` 是 **引用类型** 的： 内存用 `make()` 方法来分配。\n\n`map` 的初始化：`var map1 = make(map[keytype]valuetype)`。\n\n或者简写为：`map1 := make(map[keytype]valuetype)`。\n\n上面例子中的 `mapCreated` 就是用这种方式创建的：`mapCreated := make(map[string]float32)`。\n\n相当于：`mapCreated := map[string]float32{}`。\n\n`mapAssigned` 也是 `mapLit` 的引用，对 `mapAssigned` 的修改也会影响到 `mapLit` 的值。\n\n**不要使用 `new()`，永远用 `make()` 来构造 `map`**\n\n**注意** 如果你错误地使用 `new()` 分配了一个引用对象，你会获得一个空引用的指针，相当于声明了一个未初始化的变量并且取了它的地址：\n\n```go\nmapCreated := new(map[string]float32)\n```\n\n接下来当我们调用：`mapCreated[\"key1\"] = 4.5` 的时候，编译器会报错：\n\n\tinvalid operation: mapCreated[\"key1\"] (index of type *map[string]float32).\n\n为了说明值可以是任意类型的，这里给出了一个使用 `func() int` 作为值的 `map`：\n\n示例 8.2 [map_func.go](examples/chapter_8/map_func.go)\n\n```go\npackage main\nimport \"fmt\"\n\nfunc main() {\n\tmf := map[int]func() int{\n\t\t1: func() int { return 10 },\n\t\t2: func() int { return 20 },\n\t\t5: func() int { return 50 },\n\t}\n\tfmt.Println(mf)\n}\n```\n\n输出结果为：`map[1:0x10903be0 5:0x10903ba0 2:0x10903bc0]`: 整型都被映射到函数地址。\n\n## 8.1.2 map 容量\n\n和数组不同，`map` 可以根据新增的 key-value 对动态的伸缩，因此它不存在固定长度或者最大限制。但是你也可以选择标明 `map` 的初始容量 `capacity`，就像这样：`make(map[keytype]valuetype, cap)`。例如：\n\n```go\nmap2 := make(map[string]float32, 100)\n```\n\n当 `map` 增长到容量上限的时候，如果再增加新的 key-value 对，`map` 的大小会自动加 1。所以出于性能的考虑，对于大的 `map` 或者会快速扩张的 `map`，即使只是大概知道容量，也最好先标明。\n\n这里有一个 `map` 的具体例子，即将音阶和对应的音频映射起来：\n\n```go\nnoteFrequency := map[string]float32 {\n\t\"C0\": 16.35, \"D0\": 18.35, \"E0\": 20.60, \"F0\": 21.83,\n\t\"G0\": 24.50, \"A0\": 27.50, \"B0\": 30.87, \"A4\": 440}\n```\n\n## 8.1.3 用切片作为 map 的值\n\n既然一个 key 只能对应一个 value，而 value 又是一个原始类型，那么如果一个 key 要对应多个值怎么办？例如，当我们要处理 Unix 机器上的所有进程，以父进程（pid 为整型）作为 key，所有的子进程（以所有子进程的 pid 组成的切片）作为 value。通过将 value 定义为 `[]int` 类型或者其他类型的切片，就可以优雅地解决这个问题。\n\n这里有一些定义这种 `map` 的例子：\n\n```go\nmp1 := make(map[int][]int)\nmp2 := make(map[int]*[]int)\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[Map](08.0.md)\n- 下一节：[测试键值对是否存在及删除元素](08.2.md)\n"
  },
  {
    "path": "eBook/08.2.md",
    "content": "# 8.2 测试键值对是否存在及删除元素\n\n测试 `map1` 中是否存在 `key1`：\n\n在例子 8.1 中，我们已经见过可以使用 `val1 = map1[key1]` 的方法获取 `key1` 对应的值 `val1`。如果 `map` 中不存在 `key1`，`val1` 就是一个值类型的空值。\n\n这就会给我们带来困惑了：现在我们没法区分到底是 `key1` 不存在还是它对应的 `value` 就是空值。\n\n为了解决这个问题，我们可以这么用：`val1, isPresent = map1[key1]`\n\n`isPresent` 返回一个 `bool` 值：如果 `key1` 存在于 `map1`，`val1` 就是 `key1` 对应的 `value` 值，并且 `isPresent` 为 `true`；如果 `key1` 不存在，`val1` 就是一个空值，并且 `isPresent` 会返回 `false`。\n\n如果你只是想判断某个 `key` 是否存在而不关心它对应的值到底是多少，你可以这么做：\n\n```go\n_, ok := map1[key1] // 如果key1存在则ok == true，否则ok为false\n```\n\n或者和 `if` 混合使用：\n\n```go\nif _, ok := map1[key1]; ok {\n\t// ...\n}\n```\n\n从 `map1` 中删除 `key1`：\n\n直接 `delete(map1, key1)` 就可以。\n\n如果 `key1` 不存在，该操作不会产生错误。\n\n示例 8.4 [map_testelement.go](examples/chapter_8/map_testelement.go)\n\n```go\npackage main\nimport \"fmt\"\n\nfunc main() {\n\tvar value int\n\tvar isPresent bool\n\n\tmap1 := make(map[string]int)\n\tmap1[\"New Delhi\"] = 55\n\tmap1[\"Beijing\"] = 20\n\tmap1[\"Washington\"] = 25\n\tvalue, isPresent = map1[\"Beijing\"]\n\tif isPresent {\n\t\tfmt.Printf(\"The value of \\\"Beijing\\\" in map1 is: %d\\n\", value)\n\t} else {\n\t\tfmt.Printf(\"map1 does not contain Beijing\")\n\t}\n\n\tvalue, isPresent = map1[\"Paris\"]\n\tfmt.Printf(\"Is \\\"Paris\\\" in map1 ?: %t\\n\", isPresent)\n\tfmt.Printf(\"Value is: %d\\n\", value)\n\n\t// delete an item:\n\tdelete(map1, \"Washington\")\n\tvalue, isPresent = map1[\"Washington\"]\n\tif isPresent {\n\t\tfmt.Printf(\"The value of \\\"Washington\\\" in map1 is: %d\\n\", value)\n\t} else {\n\t\tfmt.Println(\"map1 does not contain Washington\")\n\t}\n}\n```\n\n输出结果：\n\n    The value of \"Beijing\" in map1 is: 20\n    Is \"Paris\" in map1 ?: false\n    Value is: 0\n    map1 does not contain Washington\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[声明、初始化和 make](08.1.md)\n- 下一节：[for-range 的配套用法](08.3.md)\n"
  },
  {
    "path": "eBook/08.3.md",
    "content": "# 8.3 for-range 的配套用法\n\n可以使用 `for` 循环读取 `map`：\n\n```go\nfor key, value := range map1 {\n\t...\n}\n```\n\n第一个返回值 `key` 是 `map` 中的 key 值，第二个返回值则是该 key 对应的 value 值；这两个都是仅 `for` 循环内部可见的局部变量。其中第一个返回值 `key` 值是一个可选元素。如果你只关心值，可以这么使用：\n\n```go\nfor _, value := range map1 {\n\t...\n}\n```\n\n如果只想获取 `key`，你可以这么使用：\n\n```go\nfor key := range map1 {\n\tfmt.Printf(\"key is: %d\\n\", key)\n}\n```\n\n示例 8.5 [maps_forrange.go](examples/chapter_8/maps_forrange.go)：\n\n```go\npackage main\nimport \"fmt\"\n\nfunc main() {\n\tmap1 := make(map[int]float32)\n\tmap1[1] = 1.0\n\tmap1[2] = 2.0\n\tmap1[3] = 3.0\n\tmap1[4] = 4.0\n\tfor key, value := range map1 {\n\t\tfmt.Printf(\"key is: %d - value is: %f\\n\", key, value)\n\t}\n}\n```\n\n输出结果：\n\n\tkey is: 3 - value is: 3.000000\n\tkey is: 1 - value is: 1.000000\n\tkey is: 4 - value is: 4.000000\n\tkey is: 2 - value is: 2.000000\n\n注意 `map` 不是按照 key 的顺序排列的，也不是按照 value 的序排列的。\n\n> 译者注：map 的本质是散列表，而 map 的增长扩容会导致重新进行散列，这就可能使 map 的遍历结果在扩容前后变得不可靠，Go 设计者为了让大家不依赖遍历的顺序，每次遍历的起点--即起始 bucket 的位置不一样，即不让遍历都从某个固定的 bucket0 开始，所以即使未扩容时我们遍历出来的 map 也总是无序的。\n\n问题 8.1： 下面这段代码的输出是什么？\n\n```go\ncapitals := map[string] string {\"France\":\"Paris\", \"Italy\":\"Rome\", \"Japan\":\"Tokyo\" }\nfor key := range capitals {\n\tfmt.Println(\"Map item: Capital of\", key, \"is\", capitals[key])\n}\n```\n\n**练习 8.1** [map_days.go](exercises/chapter_8/map_days.go)\n\n创建一个 `map` 来保存每周 7 天的名字，将它们打印出来并且测试是否存在 `\"Tuesday\"` 和 `\"Hollyday\"`。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[测试键值对是否存在及删除元素](08.2.md)\n- 下一节：[map 类型的切片](08.4.md)\n"
  },
  {
    "path": "eBook/08.4.md",
    "content": "# 8.4 map 类型的切片\n\n假设我们想获取一个 `map` 类型的切片，我们必须使用两次 `make()` 函数，第一次分配切片，第二次分配切片中每个 `map` 元素（参见下面的例子 8.4）。\n\n示例 8.4 [maps_forrange2.go](examples/chapter_8/maps_forrange2.go)：\n\n```go\npackage main\nimport \"fmt\"\n\nfunc main() {\n\t// Version A:\n\titems := make([]map[int]int, 5)\n\tfor i:= range items {\n\t\titems[i] = make(map[int]int, 1)\n\t\titems[i][1] = 2\n\t}\n\tfmt.Printf(\"Version A: Value of items: %v\\n\", items)\n\n\t// Version B: NOT GOOD!\n\titems2 := make([]map[int]int, 5)\n\tfor _, item := range items2 {\n\t\titem = make(map[int]int, 1) // item is only a copy of the slice element.\n\t\titem[1] = 2 // This 'item' will be lost on the next iteration.\n\t}\n\tfmt.Printf(\"Version B: Value of items: %v\\n\", items2)\n}\n```\n\n输出结果：\n\n\tVersion A: Value of items: [map[1:2] map[1:2] map[1:2] map[1:2] map[1:2]]\n\tVersion B: Value of items: [map[] map[] map[] map[] map[]]\n\n需要注意的是，应当像 A 版本那样通过索引使用切片的 `map` 元素。在 B 版本中获得的项只是 `map` 值的一个拷贝而已，所以真正的 `map` 元素没有得到初始化。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[for-range 的配套用法](08.3.md)\n- 下一节：[map 的排序](08.5.md)\n"
  },
  {
    "path": "eBook/08.5.md",
    "content": "# 8.5 map 的排序\n\n`map` 默认是无序的，不管是按照 key 还是按照 value 默认都不排序（详见第 8.3 节）。\n\n如果你想为 `map` 排序，需要将 key（或者 value）拷贝到一个切片，再对切片排序（使用 `sort` 包，详见[第 7.6.6 节](07.6.md)），然后可以使用切片的 for-range 方法打印出所有的 key 和 value。\n\n下面有一个示例：\n\n示例 8.6 [sort_map.go](examples/chapter_8/sort_map.go)：\n\n```go\n// the telephone alphabet:\npackage main\nimport (\n\t\"fmt\"\n\t\"sort\"\n)\n\nvar (\n\tbarVal = map[string]int{\"alpha\": 34, \"bravo\": 56, \"charlie\": 23,\n\t\t\t\t\t\t\t\"delta\": 87, \"echo\": 56, \"foxtrot\": 12,\n\t\t\t\t\t\t\t\"golf\": 34, \"hotel\": 16, \"indio\": 87,\n\t\t\t\t\t\t\t\"juliet\": 65, \"kili\": 43, \"lima\": 98}\n)\n\nfunc main() {\n\tfmt.Println(\"unsorted:\")\n\tfor k, v := range barVal {\n\t\tfmt.Printf(\"Key: %v, Value: %v / \", k, v)\n\t}\n\tkeys := make([]string, len(barVal))\n\ti := 0\n\tfor k, _ := range barVal {\n\t\tkeys[i] = k\n\t\ti++\n\t}\n\tsort.Strings(keys)\n\tfmt.Println()\n\tfmt.Println(\"sorted:\")\n\tfor _, k := range keys {\n\t\tfmt.Printf(\"Key: %v, Value: %v / \", k, barVal[k])\n\t}\n}\n```\n\n输出结果：\n\n\tunsorted:\n\tKey: bravo, Value: 56 / Key: echo, Value: 56 / Key: indio, Value: 87 / Key: juliet, Value: 65 / Key: alpha, Value: 34 / Key: charlie, Value: 23 / Key: delta, Value: 87 / Key: foxtrot, Value: 12 / Key: golf, Value: 34 / Key: hotel, Value: 16 / Key: kili, Value: 43 / Key: lima, Value: 98 /\n\tsorted:\n\tKey: alpha, Value: 34 / Key: bravo, Value: 56 / Key: charlie, Value: 23 / Key: delta, Value: 87 / Key: echo, Value: 56 / Key: foxtrot, Value: 12 / Key: golf, Value: 34 / Key: hotel, Value: 16 / Key: indio, Value: 87 / Key: juliet, Value: 65 / Key: kili, Value: 43 / Key: lima, Value: 98 /\n\n但是如果你想要一个排序的列表，那么最好使用结构体切片，这样会更有效：\n\n```go\ntype name struct {\n\tkey string\n\tvalue int\n}\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[map 类型的切片](08.4.md)\n- 下一节：[将 map 的键值对调](08.6.md)\n"
  },
  {
    "path": "eBook/08.6.md",
    "content": "# 8.6 将 map 的键值对调\n\n这里对调是指调换 key 和 value。如果 `map` 的值类型可以作为 key 且所有的 value 是唯一的，那么通过下面的方法可以简单的做到键值对调。\n\n示例 8.7 [invert_map.go](examples/chapter_8/invert_map.go)：\n\n```go\npackage main\nimport (\n\t\"fmt\"\n)\n\nvar (\n\tbarVal = map[string]int{\"alpha\": 34, \"bravo\": 56, \"charlie\": 23,\n\t\t\t\t\t\t\t\"delta\": 87, \"echo\": 56, \"foxtrot\": 12,\n\t\t\t\t\t\t\t\"golf\": 34, \"hotel\": 16, \"indio\": 87,\n\t\t\t\t\t\t\t\"juliet\": 65, \"kili\": 43, \"lima\": 98}\n)\n\nfunc main() {\n\tinvMap := make(map[int]string, len(barVal))\n\tfor k, v := range barVal {\n\t\tinvMap[v] = k\n\t}\n\tfmt.Println(\"inverted:\")\n\tfor k, v := range invMap {\n\t\tfmt.Printf(\"Key: %v, Value: %v / \", k, v)\n\t}\n}\n```\n\n输出结果：\n\n\tinverted:\n\tKey: 34, Value: golf / Key: 23, Value: charlie / Key: 16, Value: hotel / Key: 87, Value: delta / Key: 98, Value: lima / Key: 12, Value: foxtrot / Key: 43, Value: kili / Key: 56, Value: bravo / Key: 65, Value: juliet /\n\n如果原始 value 值不唯一那这么做肯定会出问题；这种情况下不会报错，但是当遇到不唯一的 key 时应当直接停止对调，且此时对调后的 `map` 很可能没有包含原 `map` 的所有键值对！一种解决方法就是仔细检查唯一性并且使用多值 `map`，比如使用 `map[int][]string` 类型。\n\n**练习 8.2** [map_drinks.go](exercises/chapter_8/map_drinks.go)\n\n构造一个将英文饮料名映射为法语（或者任意你的母语）的集合；先打印所有的饮料，然后打印原名和翻译后的名字。接下来按照英文名排序后再打印出来。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[map 的排序](08.5.md)\n- 下一章：[包 (package)](09.0.md)\n"
  },
  {
    "path": "eBook/09.0.md",
    "content": "# 9.0 包 (package)\n\n本章主要针对 Go 语言的包展开讲解。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[将 map 的键值对调](08.6.md)\n- 下一节：[标准库概述](09.1.md)\n\n"
  },
  {
    "path": "eBook/09.1.md",
    "content": "# 9.1 标准库概述\n\n像 `fmt`、`os` 等这样具有常用功能的内置包在 Go 语言中有 150 个以上，它们被称为标准库，大部分(一些底层的除外)内置于 Go 本身。完整列表可以在 [Go Walker](https://gowalker.org/search?q=gorepos) 查看。\n\n在贯穿本书的例子和练习中，我们都是用标准库的包。可以通过查阅第 350 页包中的内容快速找到相关的包的实例。这里我们只是按功能进行分组来介绍这些包的简单用途，我们不会深入讨论他们的内部结构。\n\n- `unsafe`: 包含了一些打破 Go 语言“类型安全”的命令，一般的程序中不会被使用，可用在 C/C++ 程序的调用中。\n- `syscall`-`os`-`os/exec`:  \n\t- `os`: 提供给我们一个平台无关性的操作系统功能接口，采用类 Unix 设计，隐藏了不同操作系统间的差异，让不同的文件系统和操作系统对象表现一致。  \n\t- `os/exec`: 提供我们运行外部操作系统命令和程序的方式。  \n\t- `syscall`: 底层的外部包，提供了操作系统底层调用的基本接口。\n\n通过一个 Go 程序让Linux重启来体现它的能力。\n\n示例 9.1 [reboot.go](examples/chapter_9/reboot.go)：\n\n```go\npackage main\nimport (\n\t\"syscall\"\n)\n\nconst LINUX_REBOOT_MAGIC1 uintptr = 0xfee1dead\nconst LINUX_REBOOT_MAGIC2 uintptr = 672274793\nconst LINUX_REBOOT_CMD_RESTART uintptr = 0x1234567\n\nfunc main() {\n\tsyscall.Syscall(syscall.SYS_REBOOT,\n\t\tLINUX_REBOOT_MAGIC1,\n\t\tLINUX_REBOOT_MAGIC2,\n\t\tLINUX_REBOOT_CMD_RESTART)\n}\n```\n\n- `archive/tar` 和 `/zip-compress`：压缩（解压缩）文件功能。\n- `fmt`-`io`-`bufio`-`path/filepath`-`flag`:  \n\t- `fmt`: 提供了格式化输入输出功能。  \n\t- `io`: 提供了基本输入输出功能，大多数是围绕系统功能的封装。  \n\t- `bufio`: 缓冲输入输出功能的封装。  \n\t- `path/filepath`: 用来操作在当前系统中的目标文件名路径。  \n\t- `flag`: 对命令行参数的操作。　　\n- `strings`-`strconv`-`unicode`-`regexp`-`bytes`:  \n\t- `strings`: 提供对字符串的操作。  \n\t- `strconv`: 提供将字符串转换为基础类型的功能。\n\t- `unicode`: 为 unicode 型的字符串提供特殊的功能。\n\t- `regexp`: 正则表达式功能。  \n\t- `bytes`: 提供对字符型分片的操作。  \n\t- `index/suffixarray`: 子字符串快速查询。\n- `math`-`math/cmath`-`math/big`-`math/rand`-`sort`:  \n\t- `math`: 基本的数学函数。  \n\t- `math/cmath`: 对复数的操作。  \n\t- `math/rand`: 伪随机数生成。  \n\t- `sort`: 为数组排序和自定义集合。  \n\t- `math/big`: 大数的实现和计算。  　　\n- `container`-`/list-ring-heap`: 实现对集合的操作。  \n\t- `list`: 双链表。\n\t- `ring`: 环形链表。\n\n下面代码演示了如何遍历一个链表(当 l 是 `*List`)：\n\n```go\nfor e := l.Front(); e != nil; e = e.Next() {\n\t//do something with e.Value\n}\n```\n\n- `time`-`log`:  \n\t- `time`: 日期和时间的基本操作。  \n\t- `log`: 记录程序运行时产生的日志，我们将在后面的章节使用它。\n- `encoding/json`-`encoding/xml`-`text/template`:\n\t- `encoding/json`: 读取并解码和写入并编码 JSON 数据。  \n\t- `encoding/xml`: 简单的 XML1.0 解析器，有关 JSON 和 XML 的实例请查阅第 [12.9](12.9.md)/[10](10.0.md) 章节。  \n\t- `text/template`:生成像 HTML 一样的数据与文本混合的数据驱动模板（参见[第 15.7 节](15.7.md)）。  \n- `net`-`net/http`-`html`:（参见[第 15 章](15.0.md)）\n\t- `net`: 网络数据的基本操作。  \n\t- `http`: 提供了一个可扩展的 HTTP 服务器和基础客户端，解析 HTTP 请求和回复。  \n\t- `html`: HTML5 解析器。  \n- `runtime`: Go 程序运行时的交互操作，例如垃圾回收和协程创建。  \n- `reflect`: 实现通过程序运行时反射，让程序操作任意类型的变量。  \n\n`exp` 包中有许多将被编译为新包的实验性的包。在下次稳定版本发布的时候，它们将成为独立的包。如果前一个版本已经存在了，它们将被作为过时的包被回收。然而 Go1.0 发布的时候并没有包含过时或者实验性的包。\n\n**练习 9.1** [Q20_linked_list.go](exercises/chapter_9/dlinked_list.go)\n\n使用 `container/list` 包实现一个双向链表，将 `101`、`102` 和 `103` 放入其中并打印出来。\n\n**练习 9.2** [size_int.go](exercises/chapter_9/size_int.go)\n\n通过使用 `unsafe` 包中的方法来测试你电脑上一个整型变量占用多少个字节。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[包 (package)](09.0.md)\n- 下一节：[regexp 包](09.2.md)\n"
  },
  {
    "path": "eBook/09.10.md",
    "content": "# 9.10 Go 的外部包和项目\n\n现在我们知道如何使用 Go 以及它的标准库了，但是 Go 的生态要比这大的多。当着手自己的 Go 项目时，最好先查找下是否有些存在的第三方的包或者项目能不能使用。大多数可以通过 go install 来进行安装。\n\n[Go Walker](https://gowalker.org) 支持根据包名在海量数据中查询。\n\n目前已经有许多非常好的外部库，如：\n\n- MySQL(GoMySQL), PostgreSQL(go-pgsql), MongoDB (mgo, gomongo), CouchDB (couch-go), ODBC (godbcl), Redis (redis.go) and SQLite3 (gosqlite) database drivers\n- SDL bindings\n- Google's Protocal Buffers(goprotobuf)\n- XML-RPC(go-xmlrpc)\n- Twitter(twitterstream)\n- OAuth libraries(GoAuth)\n\t\n## 链接\n\n- [目录](directory.md)\n- 上一节：[通过 git 打包和安装](09.9.md)\n- 下一节：[在 Go 程序中使用外部库](09.11.md)\n\t\n"
  },
  {
    "path": "eBook/09.11.md",
    "content": "# 9.11 在 Go 程序中使用外部库\n\n（本节我们将创建一个 Web 应用和它的 Google App Engine 版本，在第 19 和 21 章分别说明，当你阅读到这些章节时可以再回到这个例子。)\n\n当开始一个新项目或增加新的功能到现有的项目，你可以通过在应用程序中使用已经存在的库来节省开发时间。为了做到这一点，你必须理解库的 API（应用编程接口），那就是：库中有哪些方法可以调用，如何调用。你可能没有这个库的源代码，但作者肯定有记载的 API 以及详细介绍了如何使用它。\n\n作为一个例子，我们将使用谷歌的 API 的 urlshortener 编写一个小程序：你可以尝试一下在 http://goo.gl/ 输入一个像 \"http://www.destandaard.be\" 这样的 URL，你会看到一个像 \"http://goo.gl/O9SUO\" 这样更短的 URL 返回，也就是说，在 Twitter 之类的服务中这是非常容易嵌入的。谷歌 urlshortener 服务的文档可以在 \"http://code.google.com/apis/urlshortener/\" 找到。([第 19 章](19.0.md)，我们将开发自己版本的 urlshortener)。\n\n谷歌将这项技术提供给其他开发者，我们可以在我们自己的应用程序中调用  API （释放到指定的限制）。他们也生成了一个 Go 语言客户端库使调用变得更容易。\n\n备注：谷歌让通过使用 Google API Go 客户端服务的开发者生活变得更简单，Go 客户端程序自动生成于 Google 库的 JSON 描述。更多详情在 [项目页面](http://code.google.com/p/google-api-go-client/) 查看。\n\n下载并安装 Go 客户端库:\n将通过 go install 实现。但是首先要验证环境变量中是否含有 `GOPATH` 变量，因为外部源码将被下载到 `$GOPATH/src` 目录下并被安装到 `$GOPATH/PKG/\"machine_arch\"/` 目录下。\n\n我们将通过在终端调用以下命令来安装 API:\n\n\tgo install google.golang.org/api/urlshortener/v1\n\ngo install 将下载源码，编译并安装包\n\n使用 urlshortener 服务的 web 程序:\n现在我们可以通过导入并赋予别名来使用已安装的包：\n\n\timport  \"google.golang.org/api/urlshortener/v1\"\n\n现在我们写一个 Web 应用（参见[第 15 章 4-8 节](15.4.md)）通过表单实现短地址和长地址的相互转换。我们将使用 `template` 包并写三个处理函数：`root()` 函数通过执行表单模板来展示表单，`short()` 函数将长地址转换为短地址，`long()` 函数逆向转换。\n\n要调用 `urlshortener` 接口必须先通过 `http` 包中的默认客户端创建一个服务实例 `urlshortenerSvc`：  \n```go\nurlshortenerSvc, _ := urlshortener.New(http.DefaultClient)\n```\n\n我们通过调用服务中的 `Url.Insert` 中的 `Do` 方法传入包含长地址的 `Url` 数据结构从而获取短地址：\n\n```go\nurl, _ := urlshortenerSvc.Url.Insert(&urlshortener.Url{LongUrl: longUrl}).Do()\n```\n\n返回 `url` 的 `Id` 便是我们需要的短地址。\n\n我们通过调用服务中的 `Url.Get` 中的 `Do` 方法传入包含短地址的 Url 数据结构从而获取长地址：\n\n```go\nurl, error := urlshortenerSvc.Url.Get(shwortUrl).Do()\n```\n\n返回的长地址便是转换前的原始地址。\n\n示例 9.9\t[urlshortener.go](examples/chapter_9/use_urlshortener.go)\n\n```go\npackage main\n\nimport (\n\t \"fmt\"\n\t \"net/http\"\n\t \"text/template\"\n\n\t \"google.golang.org/api/urlshortener/v1\"\n)\nfunc main() {\n\t http.HandleFunc(\"/\", root)\n\t http.HandleFunc(\"/short\", short)\n\t http.HandleFunc(\"/long\", long)\n\n\t http.ListenAndServe(\"localhost:8080\", nil)\n}\n// the template used to show the forms and the results web page to the user\nvar rootHtmlTmpl = template.Must(template.New(\"rootHtml\").Parse(`\n<html><body>\n<h1>URL SHORTENER</h1>\n{{if .}}{{.}}<br /><br />{{end}}\n<form action=\"/short\" type=\"POST\">\nShorten this: <input type=\"text\" name=\"longUrl\" />\n<input type=\"submit\" value=\"Give me the short URL\" />\n</form>\n<br />\n<form action=\"/long\" type=\"POST\">\nExpand this: http://goo.gl/<input type=\"text\" name=\"shortUrl\" />\n<input type=\"submit\" value=\"Give me the long URL\" />\n</form>\n</body></html>\n`))\nfunc root(w http.ResponseWriter, r *http.Request) {\n\trootHtmlTmpl.Execute(w, nil)\n}\nfunc short(w http.ResponseWriter, r *http.Request) {\n\t longUrl := r.FormValue(\"longUrl\")\n\t urlshortenerSvc, _ := urlshortener.New(http.DefaultClient)\n\t url, _ := urlshortenerSvc.Url.Insert(&urlshortener.Url{LongUrl:\n\t longUrl,}).Do()\n\t rootHtmlTmpl.Execute(w, fmt.Sprintf(\"Shortened version of %s is : %s\",\n\t longUrl, url.Id))\n}\n\nfunc long(w http.ResponseWriter, r *http.Request) {\n\t shortUrl := \"http://goo.gl/\" + r.FormValue(\"shortUrl\")\n\t urlshortenerSvc, _ := urlshortener.New(http.DefaultClient)\n\t url, err := urlshortenerSvc.Url.Get(shortUrl).Do()\n\t if err != nil {\n\t\t fmt.Println(\"error: %v\", err)\n\t\t return\n\n\t }\n\t rootHtmlTmpl.Execute(w, fmt.Sprintf(\"Longer version of %s is : %s\",\n\t shortUrl, url.LongUrl))\n}\n```\n\n执行这段代码：\n\n\tgo run urlshortener.go\n\n通过浏览 `http://localhost:8080/` 的页面来测试。\n\n为了代码的简洁我们并没有检测返回的错误状态，但是在真实的生产环境的应用中一定要做检测。\n\n将应用放入 Google App Engine，我们只需要在之前的代码中作出如下改变：\n\n\tpackage main -> package urlshort\n\tfunc main() -> func init()\n\n创建一个和包同名的目录 `urlshort`，并将以下两个安装目录复制到这个目录：\n\n\tgoogle.golang.org/api/urlshortener\n\tgoogle.golang.org/api/googleapi\n\n此外还要配置下配置文件 `app.yaml`，内容如下：\n\n```yaml\napplication: urlshort\nversion: 0-1-test\nruntime: go\napi_version: 3\nhandlers:\n- url: /.*\nscript: _go_app\n```\n\n现在你可以到你的项目目录并在终端运行：`dev_appserver.py urlshort`\n\n在浏览器打开你的 Web应用：http://localhost:8080。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[Go 的外部包和项目](09.10.md)\n- 下一章：[结构 (struct) 与方法 (method)](10.0.md)\n\n"
  },
  {
    "path": "eBook/09.2.md",
    "content": "# 9.2 regexp 包\n\n正则表达式语法和使用的详细信息请参考 [维基百科](http://en.wikipedia.org/wiki/Regular_expression)。\n\n在下面的程序里，我们将在字符串中对正则表达式模式 (pattern) 进行匹配。\n\n如果是简单模式，使用 `Match()` 方法便可：\n\n```go\nok, _ := regexp.Match(pat, []byte(searchIn))\n```\n\n变量 `ok` 将返回 `true` 或者 `false`，我们也可以使用 `MatchString()`：\n\n```go\nok, _ := regexp.MatchString(pat, searchIn)\n```\n\n更多方法中，必须先将正则模式通过 `Compile()` 方法返回一个 `Regexp` 对象。然后我们将掌握一些匹配，查找，替换相关的功能。\n\n示例 9.2 [pattern.go](examples/chapter_9/pattern.go)：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"regexp\"\n\t\"strconv\"\n)\n\nfunc main() {\n\t//目标字符串\n\tsearchIn := \"John: 2578.34 William: 4567.23 Steve: 5632.18\"\n\tpat := \"[0-9]+.[0-9]+\" //正则\n\n\tf := func(s string) string {\n\t\tv, _ := strconv.ParseFloat(s, 32)\n\t\treturn strconv.FormatFloat(v*2, 'f', 2, 32)\n\t}\n\n\tif ok, _ := regexp.Match(pat, []byte(searchIn)); ok {\n\t\tfmt.Println(\"Match Found!\")\n\t}\n\n\tre, _ := regexp.Compile(pat)\n\t//将匹配到的部分替换为\"##.#\"\n\tstr := re.ReplaceAllString(searchIn, \"##.#\")\n\tfmt.Println(str)\n\t//参数为函数时\n\tstr2 := re.ReplaceAllStringFunc(searchIn, f)\n\tfmt.Println(str2)\n}\n```\n\n输出结果：\n\n\tMatch Found!\n\tJohn: ##.# William: ##.# Steve: ##.#\n\tJohn: 5156.68 William: 9134.46 Steve: 11264.36\n\n`Compile()` 函数也可能返回一个错误，我们在使用时忽略对错误的判断是因为我们确信自己正则表达式是有效的。当用户输入或从数据中获取正则表达式的时候，我们有必要去检验它的正确性。另外我们也可以使用 `MustCompile()` 方法，它可以像 `Compile()` 方法一样检验正则的有效性，但是当正则不合法时程序将 `panic()`（详情查看[第 13.2 节](13.2.md)）。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[标准库概述](09.1.md)\n- 下一节：[锁和 sync 包](09.3.md)\n"
  },
  {
    "path": "eBook/09.3.md",
    "content": "# 9.3 锁和 sync 包\n\n在一些复杂的程序中，通常通过不同线程执行不同应用来实现程序的并发。当不同线程要使用同一个变量时，经常会出现一个问题：无法预知变量被不同线程修改的顺序！（这通常被称为资源竞争，指不同线程对同一变量使用的竞争）显然这无法让人容忍，那我们该如何解决这个问题呢？\n\n经典的做法是一次只能让一个线程对共享变量进行操作。当变量被一个线程改变时（临界区），我们为它上锁，直到这个线程执行完成并解锁后，其他线程才能访问它。\n\n特别是我们之前章节学习的 `map` 类型是不存在锁的机制来实现这种效果（出于对性能的考虑），所以 map 类型是非线程安全的。当并行访问一个共享的 `map` 类型的数据，`map` 数据将会出错。\n\n在 Go 语言中这种锁的机制是通过 `sync` 包中 `Mutex` 来实现的。sync 来源于 \"synchronized\" 一词，这意味着线程将有序的对同一变量进行访问。\n\n`sync.Mutex` 是一个互斥锁，它的作用是守护在临界区入口来确保同一时间只能有一个线程进入临界区。\n\n假设 `info` 是一个需要上锁的放在共享内存中的变量。通过包含 `Mutex` 来实现的一个典型例子如下：\n\n```go\nimport  \"sync\"\n\ntype Info struct {\n\tmu sync.Mutex\n\t// ... other fields, e.g.: Str string\n}\n```\n\n如果一个函数想要改变这个变量可以这样写:\n\n```go\nfunc Update(info *Info) {\n\tinfo.mu.Lock()\n    // critical section:\n    info.Str = // new value\n    // end critical section\n    info.mu.Unlock()\n}\n```\n\n还有一个很有用的例子是通过 `Mutex` 来实现一个可以上锁的共享缓冲器:\n\n```go\ntype SyncedBuffer struct {\n\tlock \tsync.Mutex\n\tbuffer  bytes.Buffer\n}\n```\n\n在 `sync` 包中还有一个 `RWMutex` 锁：它能通过 `RLock()` 来允许同一时间多个线程对变量进行读操作，但是只能一个线程进行写操作。如果使用 `Lock()` 将和普通的 `Mutex` 作用相同。包中还有一个方便的 `Once` 类型变量的方法 `once.Do(call)`，这个方法确保被调用函数只能被调用一次。\n\n相对简单的情况下，通过使用 `sync` 包可以解决同一时间只能一个线程访问变量或 `map` 类型数据的问题。如果这种方式导致程序明显变慢或者引起其他问题，我们要重新思考来通过 goroutines 和 channels 来解决问题，这是在 Go 语言中所提倡用来实现并发的技术。我们将在[第 14 章](14.0.md)对其深入了解，并在[第 14.7 节](14.7.md)中对这两种方式进行比较。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[regexp 包](09.2.md)\n- 下一节：[精密计算和 big 包](09.4.md)\n"
  },
  {
    "path": "eBook/09.4.md",
    "content": "# 9.4 精密计算和 big 包\n\n我们知道有些时候通过编程的方式去进行计算是不精确的。如果你使用 Go 语言中的 `float64` 类型进行浮点运算，返回结果将精确到 15 位，足以满足大多数的任务。当对超出 `int64` 或者 `uint64` 类型这样的大数进行计算时，如果对精度没有要求，`float32` 或者 `float64` 可以胜任，但如果对精度有严格要求的时候，我们不能使用浮点数，在内存中它们只能被近似的表示。\n\n对于整数的高精度计算 Go 语言中提供了 `big` 包，被包含在 `math` 包下：有用来表示大整数的 `big.Int` 和表示大有理数的 `big.Rat` 类型（可以表示为 2/5 或 3.1416 这样的分数，而不是无理数或 π）。这些类型可以实现任意位类型的数字，只要内存足够大。缺点是更大的内存和处理开销使它们使用起来要比内置的数字类型慢很多。\n\n大的整型数字是通过 `big.NewInt(n)` 来构造的，其中 `n` 为 `int64` 类型整数。而大有理数是通过 `big.NewRat(n, d)` 方法构造。`n`（分子）和 `d`（分母）都是 `int64` 型整数。因为 Go 语言不支持运算符重载，所以所有大数字类型都有像是 `Add()` 和 `Mul()` 这样的方法。它们作用于作为 receiver 的整数和有理数，大多数情况下它们修改 receiver 并以 receiver 作为返回结果。因为没有必要创建 `big.Int` 类型的临时变量来存放中间结果，所以运算可以被链式地调用，并节省内存。\n\n示例 9.2 [big.go](examples/chapter_9/big.go)：\n\n``` go\n// big.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"math/big\"\n)\n\nfunc main() {\n\t// Here are some calculations with bigInts:\n\tim := big.NewInt(math.MaxInt64)\n\tin := im\n\tio := big.NewInt(1956)\n\tip := big.NewInt(1)\n\tip.Mul(im, in).Add(ip, im).Div(ip, io)\n\tfmt.Printf(\"Big Int: %v\\n\", ip)\n\t// Here are some calculations with bigInts:\n\trm := big.NewRat(math.MaxInt64, 1956)\n\trn := big.NewRat(-1956, math.MaxInt64)\n\tro := big.NewRat(19, 56)\n\trp := big.NewRat(1111, 2222)\n\trq := big.NewRat(1, 1)\n\trq.Mul(rm, rn).Add(rq, ro).Mul(rq, rp)\n\tfmt.Printf(\"Big Rat: %v\\n\", rq)\n}\n\n/* Output:\nBig Int: 43492122561469640008497075573153004\nBig Rat: -37/112\n*/\n```\n\n输出结果：\n\n\tBig Int: 43492122561469640008497075573153004\n\tBig Rat: -37/112\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[锁和 sync 包](09.3.md)\n- 下一节：[自定义包和可见性](09.5.md)\n"
  },
  {
    "path": "eBook/09.5.md",
    "content": "# 9.5 自定义包和可见性\r\n\r\n包是 Go 语言中代码组织和代码编译的主要方式。关于它们的很多基本信息已经在 4.2 章节中给出，最引人注目的便是可见性。现在我们来看看具体如何来使用自己写的包。在下一节，我们将回顾一些标准库中的包，自定义的包和标准库以外的包。\r\n\r\n当写自己包的时候，要使用短小的不含有 `_`（下划线）的小写单词来为文件命名。这里有个简单例子来说明包是如何相互调用以及可见性是如何实现的。\r\n\r\n当前目录下（examples/chapter_9/book/）有一个名为 package_mytest.go 的程序, 它使用了自定义包 pack1 中 pack1.go 的代码。这段程序（连同编译链接生成的 pack1.a）存放在当前目录下一个名为 pack1 的文件夹下。所以链接器将包的对象和主程序对象链接在一起。\r\n\r\n示例 9.4 [pack1.go](examples/chapter_9/book/pack1/pack1.go)：\r\n\r\n```go\r\npackage pack1\r\nvar Pack1Int int = 42\r\nvar pack1Float = 3.14\r\n\r\nfunc ReturnStr() string {\r\n\treturn \"Hello main!\"\r\n}\r\n```\r\n\r\n它包含了一个整型变量 `Pack1Int` 和一个返回字符串的函数 `ReturnStr`。这段程序在运行时不做任何的事情，因为它没有一个 main 函数。\r\n\r\n在主程序 package_mytest.go 中这个包通过声明的方式被导入, 只到包的目录一层。\r\n\r\n```go\r\nimport \"./pack1\"\r\n```\r\n\r\nimport 的一般格式如下:\r\n\r\n\timport \"包的路径或 URL 地址\" \r\n\r\n例如：\r\n\r\n\timport \"github.com/org1/pack1”\r\n\r\n路径是指当前目录的相对路径。\r\n\r\n示例 9.5 [package_mytest.go](examples/chapter_9/book/package_mytest.go)：\r\n\r\n```go\r\npackage main\r\n\r\nimport (\r\n\t\"fmt\"\r\n\t\"./pack1\"\r\n)\r\n\r\nfunc main() {\r\n\tvar test1 string\r\n\ttest1 = pack1.ReturnStr()\r\n\tfmt.Printf(\"ReturnStr from package1: %s\\n\", test1)\r\n\tfmt.Printf(\"Integer from package1: %d\\n\", pack1.Pack1Int)\r\n\t// fmt.Printf(\"Float from package1: %f\\n\", pack1.pack1Float)\r\n}\r\n```\r\n\r\n输出结果：\r\n\r\n\tReturnStr from package1: Hello main!\r\n\tInteger from package1: 42\r\n\r\n如果包 pack1 和我们的程序在同一路径下，我们可以通过 `\"import ./pack1\"` 这样的方式来引入，但这不被视为一个好的方法。\r\n\r\n下面的代码试图访问一个未引用的变量或者函数，甚至没有编译。将会返回一个错误：\r\n\r\n```go\r\nfmt.Printf(\"Float from package1: %f\\n\", pack1.pack1Float)\r\n```\r\n\r\n错误：\r\n\t\r\n\tcannot refer to unexported name pack1.pack1Float\r\n\r\n主程序利用的包必须在主程序编写之前被编译。主程序中每个 pack1 项目都要通过包名来使用：`pack1.Item`。具体使用方法请参见示例 4.6 和 4.7。\r\n\r\n因此，按照惯例，子目录和包之间有着密切的联系：为了区分，不同包存放在不同的目录下，每个包（所有属于这个包中的 go 文件）都存放在和包名相同的子目录下：\r\n\r\nImport with `.` :  \r\n\t\r\n\timport . \"./pack1\"\r\n\r\n当使用 `.` 作为包的别名时，你可以不通过包名来使用其中的项目。例如：`test := ReturnStr()`。\r\n\r\n在当前的命名空间导入 pack1 包，一般是为了具有更好的测试效果。\r\n\r\nImport with `_` : \r\n\r\n\timport _ \"./pack1/pack1\"\r\n\r\n`pack1` 包只导入其副作用，也就是说，只执行它的 `init()` 函数并初始化其中的全局变量。\r\n\r\n**导入外部安装包:**\r\n\r\n如果你要在你的应用中使用一个或多个外部包，首先你必须使用 `go install`（参见[第 9.7 节](09.7.md)）在你的本地机器上安装它们。\r\n\r\n\r\n假设你想使用 `http://codesite.ext/author/goExample/goex` 这种托管在 Google Code、GitHub 和 Launchpad 等代码网站上的包。\r\n\r\n你可以通过如下命令安装：\r\n\r\n\tgo install codesite.ext/author/goExample/goex\r\n\r\n将一个名为 `codesite.ext/author/goExample/goex` 的 map 安装在 `$GOROOT/src/` 目录下。\r\n\r\n通过以下方式，一次性安装，并导入到你的代码中：\r\n\r\n\timport goex \"codesite.ext/author/goExample/goex\"\r\n\r\n因此该包的 URL 将用作导入路径。\r\n\r\n在 `http://golang.org/cmd/goinstall/` 的 `go install` 文档中列出了一些广泛被使用的托管在网络代码仓库的包的导入路径\r\n\r\n**包的初始化:**\r\n\r\n程序的执行开始于导入包，初始化 `main` 包然后调用 `main()` 函数。\r\n\r\n一个没有导入的包将通过分配初始值给所有的包级变量和调用源码中定义的包级 `init()` 函数来初始化。一个包可能有多个 `init()` 函数甚至在一个源码文件中。它们的执行是无序的。这是最好的例子来测定包的值是否只依赖于相同包下的其他值或者函数。\r\n\r\n`init()` 函数是不能被调用的。\r\n\r\n导入的包在包自身初始化前被初始化，而一个包在程序执行中只能初始化一次。\r\n\r\n**编译并安装一个包（参见[第 9.7 节](09.7.md)）：**\r\n\r\n\r\n在 Linux/OS X 下可以用类似[第 3.9 节](03.9.md)的 Makefile 脚本做到这一点：\r\n\r\n\r\n\tinclude $(GOROOT)/src/Make.inc\r\n\tTARG=pack1\r\n\tGOFILES=\\\r\n\t \tpack1.go\\\r\n\t \tpack1b.go\\\r\n\tinclude $(GOROOT)/src/Make.pkg\r\n\r\n通过 `chmod 777 ./Makefile` 确保它的可执行性。\r\n\r\n上面脚本内的 `include` 语句引入了相应的功能，将自动检测机器的架构并调用正确的编译器和链接器。\r\n\r\n然后终端执行 `make` 或 `gomake` 工具：他们都会生成一个包含静态库 `pack1.a` 的 `_obj` 目录。\r\n\r\ngo install（参见[第 9.7 节](09.7.md)，从 Go1 的首选方式）同样复制 `pack1.a` 到本地的 `$GOROOT/pkg` 的目录中一个以操作系统为名的子目录下。像 `import \"pack1\"` 代替 `import \"path to pack1\"`，这样只通过名字就可以将包在程序中导入。\r\n\r\n\r\n当[第 13 章](13.0.md) 我们遇到使用测试工具进行测试的时候我们将重新回到自己的包的制作和编译这个话题。\r\n\r\n\r\n**问题 9.1**\r\n\r\na）一个包能分成多个源文件么？\r\n\r\nb）一个源文件是否能包含多个包？\r\n\r\n**练习 9.3** [main_greetings.go](exercises/chapter_9/main_greetings.go)\r\n\r\n创建一个程序 main_greetings.go 能够和用户说 `\"Good Day\"` 或者 `\"Good Night\"`。不同的问候应该放到单独的 `greetings` 包中。\r\n\r\n在同一个包中创建一个 `IsAM` 函数返回一个布尔值用来判断当前时间是 AM 还是 PM，同样创建 `IsAfternoon` 和 `IsEvening` 函数。\r\n\r\n使用 main_greetings 作出合适的问候（提示：使用 `time` 包）。\r\n\r\n**练习 9.4** 创建一个程序 [main_oddven.go](exercises/chapter_9/main_oddeven.go) 判断前 100 个整数是不是偶数，将判断所用的函数编写在 `even` 包里。\r\n\r\n**练习 9.5** 使用[第 6.6 节](06.6.md)的斐波那契程序：\r\n\r\n\r\n1）将斐波那契功能放入自己的 `fibo` 包中并通过主程序调用它，存储最后输入的值在函数的全局变量。\r\n\r\n2）扩展 `fibo` 包将通过调用斐波那契的时候，操作也作为一个参数。实验 `\"+\"` 和 `\"*\"`\r\n\r\n[main_fibo.go](exercises/chapter_9/main_fibo.go) / [fibonacci.go](exercises/chapter_6/fibonacci.go)\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[精密计算和 big 包](09.4.md)\r\n- 下一节：[为自定义包使用 godoc](09.6.md)\r\n"
  },
  {
    "path": "eBook/09.6.md",
    "content": "# 9.6 为自定义包使用 godoc\n\ngodoc 工具（[第 3.6 节](03.6.md)）在显示自定义包中的注释也有很好的效果：注释必须以 `//` 开始并无空行放在声明（包，类型，函数）前。godoc 会为每个文件生成一系列的网页。\n\n例如：\n\n- 在 [doc_examples](examples/chapter_9/doc_example) 目录下我们有[第 11.7 节](11.7.md)中的用来排序的 go 文件，文件中有一些注释（文件需要未编译）\n- 命令行下进入目录下并输入命令：\n\n\t`godoc -http=:6060 -goroot=\".\"`\n\n（`.` 是指当前目录，`-goroot` 参数可以是 `/path/to/my/package1` 这样的形式指出 `package1` 在你源码中的位置或接受用冒号形式分隔的路径，无根目录的路径为相对于当前目录的相对路径）\n\n- 在浏览器打开地址：http://localhost:6060\n\n然后你会看到本地的 godoc 页面（详见[第 3.6 节](03.6.md)）从左到右一次显示出目录中的包：\n\n```\ndoc_example:\n\ndoc_example | Packages | Commands | Specification\n```\n\n下面是链接到源码和所有对象时有序概述（所以是很好的浏览和查找源代码的方式），连同文件/注释：\n\n`sort` 包\n\n```go\nfunc Float64sAreSorted\n\ntype IntArray\n\nfunc IntsAreSortedfunc IsSortedfunc Sort\n\nfunc (IntArray) Len\n\nfunc SortFloat64s\n\nfunc (IntArray) Less\n\nfunc SortInts\n\nfunc (IntArray) Swap\n\nfunc SortStrings type Interface\n\nfunc StringsAreSorted type StringArray type Float64Array\n\nfunc (StringArray) Len\n\nfunc (Float64Array) Len\n\nfunc (StringArray) Less\n\nfunc (Float64Array) Less\n\nfunc (StringArray) Swap\n\nfunc (Float64Array) Swap\n\n// Other packages\nimport \"doc_example\" \n```\n\n使用通用的接口排序:\n```\nfunc Float64sAreSorted[Top]\nfunc Float64sAreSorted(a []float64) bool\n\nfunc IntsAreSorted[Top]\nfunc IntsAreSorted(a []int) bool\n\nfunc IsSorted[Top]\nfunc IsSorted(data Interface) bool\nTest if data is sorted\n\nfunc Sort[Top]\nfunc Sort(data Interface)\nGeneral sort function\n\nfunc SortInts[Top]\nfunc SortInts(a []int)\n\nConvenience wrappers for common cases: type IntArray[Top]\nConvenience types for common cases: IntArray type IntArray []int  \n```\n\n如果你在一个团队中工作，并且源代码树被存储在网络硬盘上，就可以使用 godoc 给所有团队成员连续文档的支持。通过设置 `sync_minutes=n`，你甚至可以让它每 `n` 分钟自动更新您的文档！\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[自定义包和可见性](09.5.md)\n- 下一节：[使用 go install 安装自定义包](09.7.md)\n"
  },
  {
    "path": "eBook/09.7.md",
    "content": "# 9.7 使用 go install 安装自定义包\n\ngo install 是 Go 中自动包安装工具：如需要将包安装到本地它会从远端仓库下载包：检出、编译和安装一气呵成。\n\n在包安装前的先决条件是要自动处理包自身依赖关系的安装。被依赖的包也会安装到子目录下，但是没有文档和示例：可以到网上浏览。\n\ngo install 使用了 GOPATH 变量（详见[第 2.2 节](02.2.md)）。\n\n远端包（详见[第 9.5 节](09.5.md)）：\n\n假设我们要安装一个有趣的包 `tideland`（它包含了许多帮助示例，参见[项目主页](http://code.google.com/p/tideland-cgl)）。\n\n因为我们需要创建目录在 Go 安装目录下，所以我们需要使用 root 或者 su 的身份执行命令。\n\n确保 Go 环境变量已经设置在 root 用户下的 `./bashrc` 文件中。\n\n使用命令安装：`go install tideland-cgl.googlecode.com/hg`。\n\n可执行文件 `hg.a` 将被放到 `$GOROOT/pkg/linux_amd64/tideland-cgl.googlecode.com` 目录下，源码文件被放置在 `$GOROOT/src/tideland-cgl.googlecode.com/hg` 目录下，同样有个 `hg.a` 放置在 `_obj` 的子目录下。\n\n现在就可以在 go 代码中使用这个包中的功能了，例如使用包名 cgl 导入：\n\n```go\nimport cgl \"tideland-cgl.googlecode.com/hg\"\n```\n\n从 Go1 起 go install 安装 Google Code 的导入路径形式是：`\"code.google.com/p/tideland-cgl\"`\n\n升级到新的版本：\n\n更新到新版本的 Go 之后本地安装包的二进制文件将全被删除。如果你想更新，重编译、重安装所有的 go 安装包可以使用：`go install -a`。\n\ngo 的版本发布的很频繁，所以需要注意发布版本和包的兼容性。go1 之后都是自己编译自己了。\n\ngo install 同样可以使用 go install 编译链接并安装本地自己的包（详见[第 9.8.2 节](09.8.md)）。\n\n更多信息可以在 [官方网站](http://golang.org/cmd/go/) 找到。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[为自定义包使用 godoc](09.6.md)\n- 下一节：[自定义包的目录结构、go install 和 go test](09.8.md)\n"
  },
  {
    "path": "eBook/09.8.md",
    "content": "# 9.8 自定义包的目录结构、go install 和 go test\n\n为了示范，我们创建了一个名为 `uc` 的简单包，它含有一个 `UpperCase` 函数将字符串的所有字母转换为大写。当然这并不值得创建一个自定义包，同样的功能已被包含在 `strings` 包里，但是同样的技巧也可以应用在更复杂的包中。\n\n## 9.8.1 自定义包的目录结构\n\n下面的结构给了你一个好的示范（`uc` 代表通用包名, 名字为粗体的代表目录，斜体代表可执行文件）:\n\n\t/home/user/goprograms\n\t\tucmain.go\t(uc 包主程序)\n\t\tMakefile (ucmain 的 makefile)\n\t\tucmain\n\t\tsrc/uc\t (包含 uc 包的 go 源码)\n\t\t\tuc.go\n\t\t \tuc_test.go\n\t\t \tMakefile (包的 makefile)\n\t\t \tuc.a\n\t\t \t_obj\n\t\t\t\tuc.a\n\t\t\t_test\n\t\t\t\tuc.a\n\t\tbin\t\t(包含最终的执行文件)\n\t\t\tucmain\n\t\tpkg/linux_amd64\n\t\t\tuc.a\t(包的目标文件)\n\n将你的项目放在 goprograms 目录下(你可以创建一个环境变量 `GOPATH`，详见第 [2.2](02.2.md)/[3](02.3.md) 章节：在 `.profile` 和 `.bashrc` 文件中添加 `export GOPATH=/home/user/goprograms`)，而你的项目将作为 `src` 的子目录。`uc` 包中的功能在 uc.go 中实现。\n\n示例 9.6 [uc.go](examples/chapter_9/uc.go)：\n\n```go\npackage uc\nimport \"strings\"\n\nfunc UpperCase(str string) string {\n\treturn strings.ToUpper(str)\n}\n```\n\n包通常附带一个或多个测试文件，在这我们创建了一个 uc_test.go 文件，如[第 9.8 节](09.8.md)所述。\n\n示例 9.7 [test.go](examples/chapter_9/test.go)\n\n```go\npackage uc\nimport \"testing\"\n\ntype ucTest struct {\n\tin, out string\n}\n\nvar ucTests = []ucTest {\n\tucTest{\"abc\", \"ABC\"},\n\tucTest{\"cvo-az\", \"CVO-AZ\"},\n\tucTest{\"Antwerp\", \"ANTWERP\"},\n}\n\nfunc TestUC(t *testing.T) {\n\tfor _, ut := range ucTests {\n\t\tuc := UpperCase(ut.in)\n\t\tif uc != ut.out {\n\t\t\tt.Errorf(\"UpperCase(%s) = %s, must be %s\", ut.in, uc,\n\t\t\tut.out)\n\t\t}\n\t}\n}\n```\n\n通过指令编译并安装包到本地：`go install uc`, 这会将 uc.a 复制到 pkg/linux_amd64 下面。\n\n另外，使用 make ，通过以下内容创建一个包的 Makefile 在 src/uc 目录下:\n\n```\ninclude $(GOROOT)/src/Make.inc\n\nTARG=uc\nGOFILES=\\\n\t\tuc.go\\\n\ninclude $(GOROOT)/src/Make.pkg\n```\n\n在该目录下的命令行调用: gomake\n\n这将创建一个 _obj 目录并将包编译生成的存档 uc.a 放在该目录下。\n\n这个包可以通过 go test 测试。\n\n创建一个 uc.a 的测试文件在目录下，输出为 `PASS` 时测试通过。\n\n在[第 13.8 节](13.8.md)我们将给出另外一个测试例子并进行深入研究。\n\n备注：有可能你当前的用户不具有足够的资格使用 go install（没有权限）。这种情况下，选择 root 用户 su。确保 Go 环境变量和 Go 源码路径也设置给 su，同样也适用你的普通用户（详见[第 2.3 节](02.3.md)）。\n\n接下来我们创建主程序 ucmain.go:\n\n示例 9.8 [ucmain.go](/examples/chapter_9/ucmain.go)：\n\n```go\npackage main\nimport (\n\t\"./src/uc\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tstr1 := \"USING package uc!\"\n\tfmt.Println(uc.UpperCase(str1))\n}\n```\n\n然后在这个目录下输入 `go install`。\n\n另外复制 uc.a 到 /home/user/goprograms 目录并创建一个 Makefile 并写入文本：\n\n```\ninclude $(GOROOT)/src/Make.inc\nTARG=ucmain\nGOFILES=\\\n\tucmain.go\\\n\ninclude $(GOROOT)/src/Make.cmd\n```\n\n执行 gomake 编译 `ucmain.go` 生成可执行文件 ucmain\n\n运行 `./ucmain` 显示: `USING PACKAGE UC!`。\n\n## 9.8.2 本地安装包\n\n本地包在用户目录下，使用给出的目录结构，以下命令用来从源码安装本地包：\n\n\tgo install /home/user/goprograms/src/uc # 编译安装 uc\n\tcd /home/user/goprograms/uc\n\tgo install ./uc \t# 编译安装 uc（和之前的指令一样）\n\tcd ..\n\tgo install .\t# 编译安装 ucmain\n\n安装到 `$GOPATH` 下：\n\n如果我们想安装的包在系统上的其他 Go 程序中被使用，它一定要安装到 `$GOPATH` 下。\n这样做，在 .profile 和 .bashrc 中设置 `export GOPATH=/home/user/goprograms`。\n\n然后执行 `go install uc` 将会复制包存档到 `$GOPATH/pkg/LINUX_AMD64/uc`。\n\n现在，uc 包可以通过 `import \"uc\"` 在任何 Go 程序中被引用。\n\n## 9.8.3 依赖系统的代码\n\n在不同的操作系统上运行的程序以不同的代码实现是非常少见的：绝大多数情况下语言和标准库解决了大部分的可移植性问题。\n\n你有一个很好的理由去写平台特定的代码，例如汇编语言。这种情况下，按照下面的约定是合理的：\n\n\tprog1.go\n\tprog1_linux.go\n\tprog1_darwin.go\n\tprog1_windows.go\n\nprog1.go 定义了不同操作系统通用的接口，并将系统特定的代码写到 prog1_os.go 中。\n对于 Go 工具你可以指定 `prog1_$GOOS.go` 或 `prog1_$GOARCH.go`\n或在平台 Makefile 中：`prog1_$(GOOS).go\\` 或 `prog1_$(GOARCH).go\\`。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[使用 go install 安装自定义包](09.7.md)\n- 下一节：[通过 Git 打包和安装](09.9.md)\n"
  },
  {
    "path": "eBook/09.9.md",
    "content": "# 9.9 通过 Git 打包和安装\n\n## 9.9.1 安装到 GitHub\n\n以上的方式对于本地包来说是可以的，但是我们如何打包代码到开发者圈子呢？那么我们需要一个云端的源码的版本控制系统，比如著名的 Git。\n\n在 Linux 和 OS X 的机器上 Git 是默认安装的，在 Windows 上你必须先自行安装，参见 [GitHub 帮助页面](http://help.github.com/win-set-up-git/)。\n\n这里将通过为[第 9.8 节](09.8.md)中的 `uc` 包创建一个 git 仓库作为演示\n\n进入到 `uc` 包目录下并创建一个 Git 仓库在里面: `git init`。\n\n信息提示: `Initialized empty git repository in $PWD/uc`。\n\n每一个 Git 项目都需要一个对包进行描述的 README.md 文件，所以需要打开你的文本编辑器（gedit、notepad 或 LiteIde）并添加一些说明进去。\n\n- 添加所有文件到仓库：`git add README.md uc.go uc_test.go Makefile`。\n- 标记为第一个版本：`git commit -m \"initial rivision\"`。\n\n现在必须登录 [GitHub 网站](https://github.com)。 \n\n如果您还没有账号，可以去注册一个开源项目的免费帐号。输入正确的帐号密码和有效的邮箱地址并进一步创建用户。然后你将获得一个 Git 命令的列表。本地仓库的操作命令已经完成。一个优秀的系统在你遇到任何问题的时候将 [引导你](http://help.github.com/)。\n\n在云端创建一个新的 uc 仓库;发布的指令为（`NNNN` 替代用户名）:\n\n```\ngit remote add origin git@github.com:NNNN/uc.git  \ngit push -u origin master\n```\n\n操作完成后检查 GitHub 上的包页面: `http://github.com/NNNN/uc`。\n\n## 9.9.2 从 GitHub 安装\n\n如果有人想安装您的远端项目到本地机器，打开终端并执行（`NNNN` 是你在 GitHub 上的用户名）：`go get github.com/NNNN/uc`。\n\n这样现在这台机器上的其他 Go 应用程序也可以通过导入路径：`\"github.com/NNNN/uc\"` 代替 `\"./uc/uc\"` 来使用。\n\n也可以将其缩写为：`import uc \"github.com/NNNN/uc\"`。\n\n然后修改 Makefile: 将 `TARG=uc` 替换为 `TARG=github.com/NNNN/uc`。\n\nGomake（和 go install）将通过 `$GOPATH` 下的本地版本进行工作。\n\n网站和版本控制系统的其他的选择(括号中为网站所使用的版本控制系统)：\n\n- BitBucket(hg/Git)\n- GitHub(Git)\n- Google Code(hg/Git/svn)\n- Launchpad(bzr)\n\n版本控制系统可以选择你熟悉的或者本地使用的代码版本控制。Go 核心代码的仓库是使用 Mercurial(hg) 来控制的，所以它是一个最可能保证你可以得到开发者项目中最好的软件。Git 也很出名，同样也适用。如果你从未使用过版本控制，这些网站有一些很好的帮助并且你可以通过在谷歌搜索 \"{name} tutorial\"（name为你想要使用的版本控制系统）得到许多很好的教程。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[自定义包的目录结构、go install 和 go test](09.8.md)\n- 下一节：[Go 的外部包和项目](09.10.md)\n"
  },
  {
    "path": "eBook/10.0.md",
    "content": "# 10.0 结构 (struct) 与方法 (method)\n\nGo 通过类型别名 (alias types) 和结构体的形式支持用户自定义类型，或者叫定制类型。一个带属性的结构体试图表示一个现实世界中的实体。结构体是复合类型 (composite types)，当需要定义一个类型，它由一系列属性组成，每个属性都有自己的类型和值的时候，就应该使用结构体，它把数据聚集在一起。然后可以访问这些数据，就好像它是一个独立实体的一部分。结构体也是值类型，因此可以通过 **new** 函数来创建。\n\n组成结构体类型的那些数据称为 **字段 (fields)**。每个字段都有一个类型和一个名字；在一个结构体中，字段名字必须是唯一的。\n\n结构体的概念在软件工程上旧的术语叫 ADT（抽象数据类型：Abstract Data Type），在一些老的编程语言中叫 **记录 (Record)**，比如 Cobol，在 C 家族的编程语言中它也存在，并且名字也是 **struct**，在面向对象的编程语言中，跟一个无方法的轻量级类一样。不过因为 Go 语言中没有类的概念，因此在 Go 中结构体有着更为重要的地位。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[在 Go 程序中使用外部库](09.11.md)\n- 下一节：[结构体定义](10.1.md)\n"
  },
  {
    "path": "eBook/10.1.md",
    "content": "# 10.1 结构体定义\n\n结构体定义的一般方式如下：\n\n```go\ntype identifier struct {\n    field1 type1\n    field2 type2\n    ...\n}\n```\n\n`type T struct {a, b int}` 也是合法的语法，它更适用于简单的结构体。\n\n结构体里的字段都有 **名字**，像 `field1`、`field2` 等，如果字段在代码中从来也不会被用到，那么可以命名它为 `_`。\n\n结构体的字段可以是任何类型，甚至是结构体本身（参考第 [10.5](10.5.md) 节），也可以是函数或者接口（参考[第 11 章](11.0.md)）。可以声明结构体类型的一个变量，然后像下面这样给它的字段赋值：\n\n```go\nvar s T\ns.a = 5\ns.b = 8\n```\n\n数组可以看作是一种结构体类型，不过它使用下标而不是具名的字段。\n\n**使用 `new()`**\n\n使用 `new()` 函数给一个新的结构体变量分配内存，它返回指向已分配内存的指针：`var t *T = new(T)`，如果需要可以把这条语句放在不同的行（比如定义是包范围的，但是分配却没有必要在开始就做）。\n\n```go\nvar t *T\nt = new(T)\n```\n\n写这条语句的惯用方法是：`t := new(T)`，变量 `t` 是一个指向 `T` 的指针，此时结构体字段的值是它们所属类型的零值。\n\n声明 `var t T` 也会给 `t` 分配内存，并零值化内存，但是这个时候 `t` 是类型 `T` 。在这两种方式中，`t` 通常被称做类型 T 的一个实例 (instance) 或对象 (object)。\n\n示例 10.1 [structs_fields.go](examples/chapter_10/structs_fields.go) 给出了一个非常简单的例子：\n\n```go\npackage main\nimport \"fmt\"\n\ntype struct1 struct {\n    i1  int\n    f1  float32\n    str string\n}\n\nfunc main() {\n    ms := new(struct1)\n    ms.i1 = 10\n    ms.f1 = 15.5\n    ms.str= \"Chris\"\n\n    fmt.Printf(\"The int is: %d\\n\", ms.i1)\n    fmt.Printf(\"The float is: %f\\n\", ms.f1)\n    fmt.Printf(\"The string is: %s\\n\", ms.str)\n    fmt.Println(ms)\n}\n```\n\n输出：\n\n    The int is: 10\n    The float is: 15.500000\n    The string is: Chris\n    &{10 15.5 Chris}\n\n使用 `fmt.Println()` 打印一个结构体的默认输出可以很好的显示它的内容，类似使用 `%v` 选项。\n\n就像在面向对象语言所作的那样，可以使用点号符给字段赋值：`structname.fieldname = value`。\n\n同样的，使用点号符可以获取结构体字段的值：`structname.fieldname`。\n\n在 Go 语言中这叫 **选择器 (selector)**。无论变量是一个结构体类型还是一个结构体类型指针，都使用同样的 **选择器符 (selector-notation)** 来引用结构体的字段：\n\n```go\ntype myStruct struct { i int }\nvar v myStruct    // v 是结构体类型变量\nvar p *myStruct   // p 是指向一个结构体类型变量的指针\nv.i\np.i\n```\n\n初始化一个结构体实例（一个结构体字面量：struct-literal）的更简短和惯用的方式如下：\n\n```go\n    ms := &struct1{10, 15.5, \"Chris\"}\n    // 此时 ms 的类型是 *struct1\n```\n\n或者：\n\n```go\n    var ms struct1\n    ms = struct1{10, 15.5, \"Chris\"}\n```\n\n混合字面量语法 (composite literal syntax) `&struct1{a, b, c}` 是一种简写，底层仍然会调用 `new()`，这里值的顺序必须按照字段顺序来写。在下面的例子中能看到可以通过在值的前面放上字段名来初始化字段的方式。表达式 `new(Type)` 和 `&Type{}` 是等价的。\n\n时间间隔（开始和结束时间以秒为单位）是使用结构体的一个典型例子：\n\n```go\ntype Interval struct {\n    start int\n    end   int\n}\n```\n\n初始化方式：\n\n```go\nintr := Interval{0, 3}            (A)\nintr := Interval{end:5, start:1}  (B)\nintr := Interval{end:5}           (C)\n```\n\n在 (A) 中，值必须以字段在结构体定义时的顺序给出，`&` 不是必须的。(B) 显示了另一种方式，字段名加一个冒号放在值的前面，这种情况下值的顺序不必一致，并且某些字段还可以被忽略掉，就像 (C) 中那样。\n\n结构体类型和字段的命名遵循可见性规则（第 [4.2](04.2.md) 节），一个导出的结构体类型中有些字段是导出的，另一些不是，这是可能的。\n\n下图说明了结构体类型实例和一个指向它的指针的内存布局：\n\n```go\ntype Point struct { x, y int }\n```\n\n使用 `new()` 初始化：\n\n![](images/10.1_fig10.1-1.jpg?raw=true)\n\n作为结构体字面量初始化：\n\n![](images/10.1_fig10.1-2.jpg?raw=true)\n\n类型 `struct1` 在定义它的包 `pack1` 中必须是唯一的，它的完全类型名是：`pack1.struct1`。\n\n下面的例子 [Listing 10.2—person.go](examples/chapter_10/person.go) 显示了一个结构体 `Person`，一个方法 `upPerson()`，方法有一个类型为 `*Person` 的参数（因此对象本身是可以被改变的），以及三种调用这个方法的不同方式：\n\n```go\npackage main\nimport (\n    \"fmt\"\n    \"strings\"\n)\n\ntype Person struct {\n    firstName   string\n    lastName    string\n}\n\nfunc upPerson(p *Person) {\n    p.firstName = strings.ToUpper(p.firstName)\n    p.lastName = strings.ToUpper(p.lastName)\n}\n\nfunc main() {\n    // 1-struct as a value type:\n    var pers1 Person\n    pers1.firstName = \"Chris\"\n    pers1.lastName = \"Woodward\"\n    upPerson(&pers1)\n    fmt.Printf(\"The name of the person is %s %s\\n\", pers1.firstName, pers1.lastName)\n\n    // 2—struct as a pointer:\n    pers2 := new(Person)\n    pers2.firstName = \"Chris\"\n    pers2.lastName = \"Woodward\"\n    (*pers2).lastName = \"Woodward\"  // 这是合法的\n    upPerson(pers2)\n    fmt.Printf(\"The name of the person is %s %s\\n\", pers2.firstName, pers2.lastName)\n\n    // 3—struct as a literal:\n    pers3 := &Person{\"Chris\",\"Woodward\"}\n    upPerson(pers3)\n    fmt.Printf(\"The name of the person is %s %s\\n\", pers3.firstName, pers3.lastName)\n}\n```\n\n输出：\n\n    The name of the person is CHRIS WOODWARD\n    The name of the person is CHRIS WOODWARD\n    The name of the person is CHRIS WOODWARD\n\n在上面例子的第二种情况中，可以直接通过指针，像 `pers2.lastName = \"Woodward\"` 这样给结构体字段赋值，没有像 C++ 中那样需要使用 `->` 操作符，Go 会自动做这样的转换。\n\n注意也可以通过解指针的方式来设置值：`(*pers2).lastName = \"Woodward\"`\n\n**结构体的内存布局**\n\nGo 语言中，结构体和它所包含的数据在内存中是以连续块的形式存在的，即使结构体中嵌套有其他的结构体，这在性能上带来了很大的优势。不像 Java 中的引用类型，一个对象和它里面包含的对象可能会在不同的内存空间中，这点和 Go 语言中的指针很像。下面的例子清晰地说明了这些情况：\n\n```go\ntype Rect1 struct {Min, Max Point }\ntype Rect2 struct {Min, Max *Point }\n```\n\n![](images/10.1_fig10.2.jpg?raw=true)\n\n**递归结构体**\n\n结构体类型可以通过引用自身来定义。这在定义链表或二叉树的元素（通常叫节点）时特别有用，此时节点包含指向临近节点的链接（地址）。如下所示，链表中的 `su`，树中的 `ri` 和 `le` 分别是指向别的节点的指针。\n\n链表：\n\n![](images/10.1_fig10.3.jpg?raw=true)\n\n这块的 `data` 字段用于存放有效数据（比如 `float64`），`su` 指针指向后继节点。\n\nGo 代码：\n\n```go\ntype Node struct {\n    data    float64\n    su      *Node\n}\n```\n\n链表中的第一个元素叫 `head`，它指向第二个元素；最后一个元素叫 `tail`，它没有后继元素，所以它的 `su` 为 `nil` 值。当然真实的链接会有很多数据节点，并且链表可以动态增长或收缩。\n\n同样地可以定义一个双向链表，它有一个前趋节点 `pr` 和一个后继节点 `su`：\n\n```go\ntype Node struct {\n    pr      *Node\n    data    float64\n    su      *Node\n}\n```\n\n二叉树：\n\n<img src=\"images/10.1_fig10.4.jpg?raw=true\" style=\"zoom: 80%;\" />\n\n二叉树中每个节点最多能链接至两个节点：左节点 (`le`) 和右节点  (`ri`)，这两个节点本身又可以有左右节点，依次类推。树的顶层节点叫根节点 (**root**)，底层没有子节点的节点叫叶子节点 (**leaves**)，叶子节点的 `le` 和 `ri` 指针为 `nil` 值。在 Go 中可以如下定义二叉树：\n\n```go\ntype Tree struct {\n    le      *Tree\n    data    float64\n    ri      *Tree\n}\n```\n\n**结构体转换**\n\nGo 中的类型转换遵循严格的规则。当为结构体定义了一个 `alias` 类型时，此结构体类型和它的 `alias` 类型都有相同的底层类型，它们可以如示例 10.3 那样互相转换，同时需要注意其中非法赋值或转换引起的编译错误。\n\n示例 10.3：\n\n```go\npackage main\nimport \"fmt\"\n\ntype number struct {\n    f float32\n}\n\ntype nr number   // alias type\n\nfunc main() {\n    a := number{5.0}\n    b := nr{5.0}\n    // var i float32 = b   // compile-error: cannot use b (type nr) as type float32 in assignment\n    // var i = float32(b)  // compile-error: cannot convert b (type nr) to type float32\n    // var c number = b    // compile-error: cannot use b (type nr) as type number in assignment\n    // needs a conversion:\n    var c = number(b)\n    fmt.Println(a, b, c)\n}\n```\n\n输出：\n\n    {5} {5} {5}\n\n**练习 10.1** [vcard.go](exercises/chapter_10/vcard.go)：\n\n定义结构体 `Address` 和 `VCard`，后者包含一个人的名字、地址编号、出生日期和图像，试着选择正确的数据类型。构建一个自己的 `vcard` 并打印它的内容。\n\n    提示：\n    VCard 必须包含住址，它应该以值类型还是以指针类型放在 VCard 中呢？\n    第二种会好点，因为它占用内存少。包含一个名字和两个指向地址的指针的 Address 结构体可以使用 %v 打印：\n    {Kersschot 0x126d2b80 0x126d2be0}\n\n**练习 10.2** [personex1.go](exercises/chapter_10/personex1.go)：\n\n修改 personex1.go，使它的参数 `upPerson` 不是一个指针，解释下二者的区别。\n\n**练习 10.3** [point.go](exercises/chapter_10/point.go)：\n\n使用坐标 `X`、`Y` 定义一个二维 `Point` 结构体。同样地，对一个三维点使用它的极坐标定义一个 `Polar` 结构体。实现一个 `Abs()` 方法来计算一个 `Point` 表示的向量的长度，实现一个 `Scale()` 方法，它将点的坐标乘以一个尺度因子（提示：使用 `math` 包里的 `Sqrt()` 函数）(function Scale that multiplies the coordinates of a point with a scale factor)。\n\n**练习 10.4** [rectangle.go](exercises/chapter_10/rectangle.go)：\n\n定义一个 `Rectangle` 结构体，它的长和宽是 `int` 类型，并定义方法 `Area()` 和 `Perimeter()`，然后进行测试。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[结构 (struct) 与方法 (method)](10.0.md)\n- 下一节：[使用工厂方法创建结构体](10.2.md)\n"
  },
  {
    "path": "eBook/10.2.md",
    "content": "# 10.2 使用工厂方法创建结构体实例\n\n## 10.2.1 结构体工厂\n\nGo 语言不支持面向对象编程语言中那样的构造子方法，但是可以很容易的在 Go 中实现 “构造子工厂”方法。为了方便通常会为类型定义一个工厂，按惯例，工厂的名字以 `new...` 或 `New...` 开头。假设定义了如下的 `File` 结构体类型：\n\n```go\ntype File struct {\n    fd      int     // 文件描述符\n    name    string  // 文件名\n}\n```\n\n下面是这个结构体类型对应的工厂方法，它返回一个指向结构体实例的指针：\n\n```go\nfunc NewFile(fd int, name string) *File {\n    if fd < 0 {\n        return nil\n    }\n\n    return &File{fd, name}\n}\n```\n\n然后这样调用它：\n\n```go\nf := NewFile(10, \"./test.txt\")\n```\n\n在 Go 语言中常常像上面这样在工厂方法里使用初始化来简便的实现构造函数。\n\n如果 `File` 是一个结构体类型，那么表达式 `new(File)` 和 `&File{}` 是等价的。\n\n这可以和大多数面向对象编程语言中笨拙的初始化方式做个比较：`File f = new File(...)`。\n\n我们可以说是工厂实例化了类型的一个对象，就像在基于类的 OO 语言中那样。\n\n如果想知道结构体类型 `T` 的一个实例占用了多少内存，可以使用：`size := unsafe.Sizeof(T{})`。\n\n**如何强制使用工厂方法**\n\n通过应用可见性规则参考 [4.2.1节](04.2.md)、[9.5 节](09.5.md) 就可以禁止使用 `new()` 函数，强制用户使用工厂方法，从而使类型变成私有的，就像在面向对象语言中那样。\n\n```go\ntype matrix struct {\n    ...\n}\n\nfunc NewMatrix(params) *matrix {\n    m := new(matrix) // 初始化 m\n    return m\n}\n```\n\n在其他包里使用工厂方法：\n\n```go\npackage main\nimport \"matrix\"\n...\nwrong := new(matrix.matrix)     // 编译失败（matrix 是私有的）\nright := matrix.NewMatrix(...)  // 实例化 matrix 的唯一方式\n```\n\n## 10.2.2 map 和 struct vs new() 和 make()\n\n`new()` 和 `make()` 这两个内置函数已经在第 [7.2.4](07.2.md) 节通过切片的例子说明过一次。\n\n现在为止我们已经见到了可以使用 `make()` 的三种类型中的其中两个：\n\n    slices  /  maps / channels（见第 14 章）\n\n下面的例子说明了在映射上使用 `new()` 和 `make()` 的区别以及可能发生的错误：\n\n示例 10.4 [new_make.go](examples/chapter_10/new_make.go)（不能编译）\n\n```go\npackage main\n\ntype Foo map[string]string\ntype Bar struct {\n    thingOne string\n    thingTwo int\n}\n\nfunc main() {\n    // OK\n    y := new(Bar)\n    (*y).thingOne = \"hello\"\n    (*y).thingTwo = 1\n\n    // NOT OK\n    z := make(Bar) // 编译错误：cannot make type Bar\n    (*z).thingOne = \"hello\"\n    (*z).thingTwo = 1\n\n    // OK\n    x := make(Foo)\n    x[\"x\"] = \"goodbye\"\n    x[\"y\"] = \"world\"\n\n    // NOT OK\n    u := new(Foo)\n    (*u)[\"x\"] = \"goodbye\" // 运行时错误!! panic: assignment to entry in nil map\n    (*u)[\"y\"] = \"world\"\n}\n```\n\n试图 `make()` 一个结构体变量，会引发一个编译错误，这还不是太糟糕，但是 `new()` 一个 `map` 并试图向其填充数据，将会引发运行时错误！ 因为 `new(Foo)` 返回的是一个指向 `nil` 的指针，它尚未被分配内存。所以在使用 `map` 时要特别谨慎。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[结构体定义](10.1.md)\n- 下一节：[使用自定义包中的结构体](10.3.md)\n"
  },
  {
    "path": "eBook/10.3.md",
    "content": "# 10.3 使用自定义包中的结构体\r\n\r\n下面的例子中，main.go 使用了一个结构体，它来自 struct_pack 下的包 `structPack`。\r\n\r\n示例 10.5 [structPack.go](examples/chapter_10/struct_pack/structPack.go)：\r\n\r\n```go\r\npackage structPack\r\n\r\ntype ExpStruct struct {\r\n    Mi1 int\r\n    Mf1 float32\r\n}\r\n```\r\n\r\n示例 10.6 [main.go](examples/chapter_10/main.go)：\r\n\r\n```go\r\npackage main\r\nimport (\r\n    \"fmt\"\r\n    \"./struct_pack/structPack\"\r\n)\r\n\r\nfunc main() {\r\n    struct1 := new(structPack.ExpStruct)\r\n    struct1.Mi1 = 10\r\n    struct1.Mf1 = 16.\r\n\r\n    fmt.Printf(\"Mi1 = %d\\n\", struct1.Mi1)\r\n    fmt.Printf(\"Mf1 = %f\\n\", struct1.Mf1)\r\n}\r\n```\r\n\r\n输出：\r\n\r\n    Mi1 = 10\r\n    Mf1 = 16.000000\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[使用工厂方法创建结构体实例](10.2.md)\r\n- 下一节：[带标签的结构体](10.4.md)\r\n"
  },
  {
    "path": "eBook/10.4.md",
    "content": "# 10.4 带标签的结构体\r\n\r\n结构体中的字段除了有名字和类型外，还可以有一个可选的标签 (tag)：它是一个附属于字段的字符串，可以是文档或其他的重要标记。标签的内容不可以在一般的编程中使用，只有包 `reflect` 能获取它。我们将在下一章（[第 11.10 节](11.10.md)中深入的探讨 `reflect` 包，它可以在运行时自省类型、属性和方法，比如：在一个变量上调用 `reflect.TypeOf()` 可以获取变量的正确类型，如果变量是一个结构体类型，就可以通过 Field 来索引结构体的字段，然后就可以使用 Tag 属性。\r\n\r\n\r\n示例 10.7 [struct_tag.go](examples/chapter_10/struct_tag.go)：\r\n\r\n```go\r\npackage main\r\n\r\nimport (\r\n\t\"fmt\"\r\n\t\"reflect\"\r\n)\r\n\r\ntype TagType struct { // tags\r\n\tfield1 bool   \"An important answer\"\r\n\tfield2 string \"The name of the thing\"\r\n\tfield3 int    \"How much there are\"\r\n}\r\n\r\nfunc main() {\r\n\ttt := TagType{true, \"Barak Obama\", 1}\r\n\tfor i := 0; i < 3; i++ {\r\n\t\trefTag(tt, i)\r\n\t}\r\n}\r\n\r\nfunc refTag(tt TagType, ix int) {\r\n\tttType := reflect.TypeOf(tt)\r\n\tixField := ttType.Field(ix)\r\n\tfmt.Printf(\"%v\\n\", ixField.Tag)\r\n}\r\n```\r\n\r\n输出：\r\n\r\n    An important answer\r\n    The name of the thing\r\n    How much there are\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[使用自定义包中的结构体](10.3.md)\r\n- 下一节：[匿名字段和内嵌结构体](10.5.md)\r\n"
  },
  {
    "path": "eBook/10.5.md",
    "content": "# 10.5 匿名字段和内嵌结构体\r\n\r\n## 10.5.1 定义\r\n\r\n结构体可以包含一个或多个 **匿名（或内嵌）字段**，即这些字段没有显式的名字，只有字段的类型是必须的，此时类型就是字段的名字。匿名字段本身可以是一个结构体类型，即 **结构体可以包含内嵌结构体**。\r\n\r\n可以粗略地将这个和面向对象语言中的继承概念相比较，随后将会看到它被用来模拟类似继承的行为。Go 语言中的继承是通过内嵌或组合来实现的，所以可以说，在 Go 语言中，相比较于继承，组合更受青睐。\r\n\r\n考虑如下的程序：\r\n\r\n示例 10.8 [structs_anonymous_fields.go](examples/chapter_10/structs_anonymous_fields.go)：\r\n\r\n```go\r\npackage main\r\n\r\nimport \"fmt\"\r\n\r\ntype innerS struct {\r\n\tin1 int\r\n\tin2 int\r\n}\r\n\r\ntype outerS struct {\r\n\tb    int\r\n\tc    float32\r\n\tint  // anonymous field\r\n\tinnerS //anonymous field\r\n}\r\n\r\nfunc main() {\r\n\touter := new(outerS)\r\n\touter.b = 6\r\n\touter.c = 7.5\r\n\touter.int = 60\r\n\touter.in1 = 5\r\n\touter.in2 = 10\r\n\r\n\tfmt.Printf(\"outer.b is: %d\\n\", outer.b)\r\n\tfmt.Printf(\"outer.c is: %f\\n\", outer.c)\r\n\tfmt.Printf(\"outer.int is: %d\\n\", outer.int)\r\n\tfmt.Printf(\"outer.in1 is: %d\\n\", outer.in1)\r\n\tfmt.Printf(\"outer.in2 is: %d\\n\", outer.in2)\r\n\r\n\t// 使用结构体字面量\r\n\touter2 := outerS{6, 7.5, 60, innerS{5, 10}}\r\n\tfmt.Println(\"outer2 is:\", outer2)\r\n}\r\n```\r\n\r\n输出：\r\n\r\n    outer.b is: 6\r\n    outer.c is: 7.500000\r\n    outer.int is: 60\r\n    outer.in1 is: 5\r\n    outer.in2 is: 10\r\n    outer2 is:{6 7.5 60 {5 10}}\r\n\r\n通过类型 `outer.int` 的名字来获取存储在匿名字段中的数据，于是可以得出一个结论：在一个结构体中对于每一种数据类型只能有一个匿名字段。\r\n\r\n## 10.5.2 内嵌结构体\r\n\r\n同样地结构体也是一种数据类型，所以它也可以作为一个匿名字段来使用，如同上面例子中那样。外层结构体通过 `outer.in1` 直接进入内层结构体的字段，内嵌结构体甚至可以来自其他包。内层结构体被简单的插入或者内嵌进外层结构体。这个简单的“继承”机制提供了一种方式，使得可以从另外一个或一些类型继承部分或全部实现。\r\n\r\n另外一个例子：\r\n\r\n示例 10.9 [embedd_struct.go](examples/chapter_10/embedd_struct.go)：\r\n\r\n```go\r\npackage main\r\n\r\nimport \"fmt\"\r\n\r\ntype A struct {\r\n\tax, ay int\r\n}\r\n\r\ntype B struct {\r\n\tA\r\n\tbx, by float32\r\n}\r\n\r\nfunc main() {\r\n\tb := B{A{1, 2}, 3.0, 4.0}\r\n\tfmt.Println(b.ax, b.ay, b.bx, b.by)\r\n\tfmt.Println(b.A)\r\n}\r\n```\r\n\r\n输出：\r\n\r\n    1 2 3 4\r\n    {1 2}\r\n\r\n**练习 10.5** [anonymous_struct.go](exercises/chapter_10/anonymous_struct.go)：\r\n\r\n创建一个结构体，它有一个具名的 `float32` 字段，2 个匿名字段，类型分别是 `int` 和 `string`。通过结构体字面量新建一个结构体实例并打印它的内容。\r\n\r\n## 10.5.3 命名冲突\r\n\r\n当两个字段拥有相同的名字（可能是继承来的名字）时该怎么办呢？\r\n\r\n1. 外层名字会覆盖内层名字（但是两者的内存空间都保留），这提供了一种重载字段或方法的方式；\r\n2. 如果相同的名字在同一级别出现了两次，如果这个名字被程序使用了，将会引发一个错误（不使用没关系）。没有办法来解决这种问题引起的二义性，必须由程序员自己修正。\r\n\r\n例子：\r\n\r\n```go\r\ntype A struct {a int}\r\ntype B struct {a, b int}\r\n\r\ntype C struct {A; B}\r\nvar c C\r\n```\r\n\r\n规则 2：使用 `c.a` 是错误的，到底是 `c.A.a` 还是 `c.B.a` 呢？会导致编译器错误：**`ambiguous DOT reference c.a disambiguate with either c.A.a or c.B.a`**。\r\n\r\n```go\r\ntype D struct {B; b float32}\r\nvar d D\r\n```\r\n\r\n规则1：使用 `d.b` 是没问题的：它是 `float32`，而不是 `B` 的 `b`。如果想要内层的 `b` 可以通过 `d.B.b` 得到。\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[带标签的结构体](10.4.md)\r\n- 下一节：[方法](10.6.md)\r\n"
  },
  {
    "path": "eBook/10.6.md",
    "content": "# 10.6 方法\n\n## 10.6.1 方法是什么\n\n在 Go 语言中，结构体就像是类的一种简化形式，那么面向对象程序员可能会问：类的方法在哪里呢？在 Go 中有一个概念，它和方法有着同样的名字，并且大体上意思相同：Go 方法是作用在接收者 (receiver) 上的一个函数，接收者是某种类型的变量。因此方法是一种特殊类型的函数。\n\n接收者类型可以是（几乎）任何类型，不仅仅是结构体类型：任何类型都可以有方法，甚至可以是函数类型，可以是 `int`、`bool`、`string` 或数组的别名类型。但是接收者不能是一个接口类型（参考[第 11 章](11.0.md)），因为接口是一个抽象定义，但是方法却是具体实现；如果这样做会引发一个编译错误：`invalid receiver type...`。\n\n最后接收者不能是一个指针类型，但是它可以是任何其他允许类型的指针。\n\n一个类型加上它的方法等价于面向对象中的一个类。一个重要的区别是：在 Go 中，类型的代码和绑定在它上面的方法的代码可以不放置在一起，它们可以存在在不同的源文件，唯一的要求是：它们必须是同一个包的。\n\n类型 `T`（或 `*T`）上的所有方法的集合叫做类型 `T`（或 `*T`）的方法集 (method set)。\n\n因为方法是函数，所以同样的，不允许方法重载，即对于一个类型只能有一个给定名称的方法。但是如果基于接收者类型，是有重载的：具有同样名字的方法可以在 2 个或多个不同的接收者类型上存在，比如在同一个包里这么做是允许的：\n\n```go\nfunc (a *denseMatrix) Add(b Matrix) Matrix\nfunc (a *sparseMatrix) Add(b Matrix) Matrix\n```\n\n别名类型没有原始类型上已经定义过的方法。\n\n定义方法的一般格式如下：\n\n```go\nfunc (recv receiver_type) methodName(parameter_list) (return_value_list) { ... }\n```\n\n在方法名之前，`func` 关键字之后的括号中指定 receiver。\n\n如果 `recv` 是 receiver 的实例，`Method1` 是它的方法名，那么方法调用遵循传统的 `object.name` 选择器符号：`recv.Method1()`。\n\n如果 `recv` 是一个指针，Go 会自动解引用。\n\n如果方法不需要使用 `recv` 的值，可以用 **_** 替换它，比如：\n\n```go\nfunc (_ receiver_type) methodName(parameter_list) (return_value_list) { ... }\n```\n\n`recv` 就像是面向对象语言中的 `this` 或 `self`，但是 Go 中并没有这两个关键字。随个人喜好，你可以使用 `this` 或 `self` 作为 receiver 的名字。下面是一个结构体上的简单方法的例子：\n\n示例 10.10 [method1 .go](examples/chapter_10/method1.go)：\n\n```go\npackage main\n\nimport \"fmt\"\n\ntype TwoInts struct {\n\ta int\n\tb int\n}\n\nfunc main() {\n\ttwo1 := new(TwoInts)\n\ttwo1.a = 12\n\ttwo1.b = 10\n\n\tfmt.Printf(\"The sum is: %d\\n\", two1.AddThem())\n\tfmt.Printf(\"Add them to the param: %d\\n\", two1.AddToParam(20))\n\n\ttwo2 := TwoInts{3, 4}\n\tfmt.Printf(\"The sum is: %d\\n\", two2.AddThem())\n}\n\nfunc (tn *TwoInts) AddThem() int {\n\treturn tn.a + tn.b\n}\n\nfunc (tn *TwoInts) AddToParam(param int) int {\n\treturn tn.a + tn.b + param\n}\n```\n\n输出：\n\n    The sum is: 22\n    Add them to the param: 42\n    The sum is: 7\n\n下面是非结构体类型上方法的例子：\n\n示例 10.11 [method2.go](examples/chapter_10/method2.go)：\n\n```go\npackage main\n\nimport \"fmt\"\n\ntype IntVector []int\n\nfunc (v IntVector) Sum() (s int) {\n\tfor _, x := range v {\n\t\ts += x\n\t}\n\treturn\n}\n\nfunc main() {\n\tfmt.Println(IntVector{1, 2, 3}.Sum()) // 输出是6\n}\n```\n\n**练习 10.6** [employee_salary.go](exercises/chapter_10/employee_salary.go)\n\n定义结构体 `employee`，它有一个 `salary` 字段，给这个结构体定义一个方法 `giveRaise` 来按照指定的百分比增加薪水。\n\n**练习 10.7** [iteration_list.go](exercises/chapter_10/iteration_list.go)\n\n下面这段代码有什么错？\n\n```go\npackage main\n\nimport \"container/list\"\n\nfunc (p *list.List) Iter() {\n\t// ...\n}\n\nfunc main() {\n\tlst := new(list.List)\n\tfor _= range lst.Iter() {\n\t}\n}\n```\n\n类型和作用在它上面定义的方法必须在同一个包里定义，这就是为什么不能在 `int`、`float32(64)` 或类似这些的类型上定义方法。试图在 `int` 类型上定义方法会得到一个编译错误：\n\n    cannot define new methods on non-local type int\n\n比如想在 `time.Time` 上定义如下方法：\n\n```go\nfunc (t time.Time) first3Chars() string {\n\treturn time.LocalTime().String()[0:3]\n}\n```\n\n类型在其他的，或是非本地的包里定义，在它上面定义方法都会得到和上面同样的错误。\n\n但是有一个间接的方式：可以先定义该类型（比如：`int` 或 `float32(64)`）的别名类型，然后再为别名类型定义方法。或者像下面这样将它作为匿名类型嵌入在一个新的结构体中。当然方法只在这个别名类型上有效。\n\n示例 10.12 [method_on_time.go](examples/chapter_10/method_on_time.go)：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\ntype myTime struct {\n\ttime.Time //anonymous field\n}\n\nfunc (t myTime) first3Chars() string {\n\treturn t.Time.String()[0:3]\n}\nfunc main() {\n\tm := myTime{time.Now()}\n\t// 调用匿名 Time 上的 String 方法\n\tfmt.Println(\"Full time now:\", m.String())\n\t// 调用 myTime.first3Chars\n\tfmt.Println(\"First 3 chars:\", m.first3Chars())\n}\n\n/* Output:\nFull time now: Mon Oct 24 15:34:54 Romance Daylight Time 2011\nFirst 3 chars: Mon\n*/\n```\n\n## 10.6.2 函数和方法的区别\n\n函数将变量作为参数：`Function1(recv)`\n\n方法在变量上被调用：`recv.Method1()`\n\n在接收者是指针时，方法可以改变接收者的值（或状态），这点函数也可以做到（当参数作为指针传递，即通过引用调用时，函数也可以改变参数的状态）。\n\n**不要忘记 `Method1()` 后边的括号 `()`，否则会引发编译器错误：`method recv.Method1 is not an expression, must be called`**\n\n接收者必须有一个显式的名字，这个名字必须在方法中被使用。\n\n`receiver_type` 叫做 **（接收者）基本类型**，这个类型必须在和方法同样的包中被声明。\n\n在 Go 中，（接收者）类型关联的方法不写在类型结构里面，就像类那样；耦合更加宽松；类型和方法之间的关联由接收者来建立。\n\n**方法没有和数据定义（结构体）混在一起：它们是正交的类型；表示（数据）和行为（方法）是独立的。**\n\n## 10.6.3 指针或值作为接收者\n\n鉴于性能的原因，`recv` 最常见的是一个指向 `receiver_type` 的指针（因为我们不想要一个实例的拷贝，如果按值调用的话就会是这样），特别是在 receiver 类型是结构体时，就更是如此了。\n\n如果想要方法改变接收者的数据，就在接收者的指针类型上定义该方法。否则，就在普通的值类型上定义方法。\n\n下面的例子 `pointer_value.go` 作了说明：`change()`接受一个指向 `B` 的指针，并改变它内部的成员；`write()` 通过拷贝接受 `B` 的值并只输出 `B` 的内容。注意 Go 为我们做了探测工作，我们自己并没有指出是否在指针上调用方法，Go 替我们做了这些事情。`b1` 是值而 `b2` 是指针，方法都支持运行了。\n\n示例 10.13 [pointer_value.go](examples/chapter_10/pointer_value.go)：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\ntype B struct {\n\tthing int\n}\n\nfunc (b *B) change() { b.thing = 1 }\n\nfunc (b B) write() string { return fmt.Sprint(b) }\n\nfunc main() {\n\tvar b1 B // b1 是值\n\tb1.change()\n\tfmt.Println(b1.write())\n\n\tb2 := new(B) // b2 是指针\n\tb2.change()\n\tfmt.Println(b2.write())\n}\n\n/* 输出：\n{1}\n{1}\n*/\n```\n\n试着在 `write()` 中改变接收者 `b` 的值：将会看到它可以正常编译，但是开始的 `b` 没有被改变。\n\n我们知道方法将指针作为接收者不是必须的，如下面的例子，我们只是需要 `Point3` 的值来做计算：\n\n```go\ntype Point3 struct { x, y, z float64 }\n// A method on Point3\nfunc (p Point3) Abs() float64 {\n    return math.Sqrt(p.x*p.x + p.y*p.y + p.z*p.z)\n}\n```\n\n这样做稍微有点昂贵，因为 `Point3` 是作为值传递给方法的，因此传递的是它的拷贝，这在 Go 中是合法的。也可以在指向这个类型的指针上调用此方法（会自动解引用）。\n\n假设 `p3` 定义为一个指针：`p3 := &Point{ 3, 4, 5}`。\n\n可以使用 `p3.Abs()` 来替代 `(*p3).Abs()`。\n\n像例子 10.10 ([method1.go](examples/chapter_10/method1.go)) 中接收者类型是 `*TwoInts` 的方法 `AddThem()`，它能在类型 `TwoInts` 的值上被调用，这是自动间接发生的。\n\n因此 `two2.AddThem` 可以替代 `(&two2).AddThem()`。\n\n在值和指针上调用方法：\n\n可以有连接到类型的方法，也可以有连接到类型指针的方法。\n\n但是这没关系：对于类型 `T`，如果在 `\\*T` 上存在方法 `Meth()`，并且 `t` 是这个类型的变量，那么 `t.Meth()` 会被自动转换为 `(&t).Meth()`。\n\n**指针方法和值方法都可以在指针或非指针上被调用**，如下面程序所示，类型 `List` 在值上有一个方法 `Len()`，在指针上有一个方法 `Append()`，但是可以看到两个方法都可以在两种类型的变量上被调用。\n\n示例 10.14 [methodset1.go](examples/chapter_10/methodset1.go)：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\ntype List []int\n\nfunc (l List) Len() int        { return len(l) }\nfunc (l *List) Append(val int) { *l = append(*l, val) }\n\nfunc main() {\n\t// 值\n\tvar lst List\n\tlst.Append(1)\n\tfmt.Printf(\"%v (len: %d)\", lst, lst.Len()) // [1] (len: 1)\n\n\t// 指针\n\tplst := new(List)\n\tplst.Append(2)\n\tfmt.Printf(\"%v (len: %d)\", plst, plst.Len()) // &[2] (len: 1)\n}\n```\n\n## 10.6.4 方法和未导出字段\n\n考虑 `person2.go` 中的 `person` 包：类型 `Person` 被明确的导出了，但是它的字段没有被导出。例如在 `use_person2.go` 中 `p.firstName` 就是错误的。该如何在另一个程序中修改或者只是读取一个 `Person` 的名字呢？\n\n这可以通过面向对象语言一个众所周知的技术来完成：提供 `getter()` 和 `setter()` 方法。对于 `setter()` 方法使用 `Set...` 前缀，对于 `getter()` 方法只使用成员名。\n\n示例 10.15 [person2.go](examples/chapter_10/person2.go)：\n\n```go\npackage person\n\ntype Person struct {\n\tfirstName string\n\tlastName  string\n}\n\nfunc (p *Person) FirstName() string {\n\treturn p.firstName\n}\n\nfunc (p *Person) SetFirstName(newName string) {\n\tp.firstName = newName\n}\n```\n\n示例 10.16 [use_person2.go](examples/chapter_10/use_person2.go)：\n\n```go\npackage main\n\nimport (\n\t\"./person\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tp := new(person.Person)\n\t// p.firstName undefined\n\t// (cannot refer to unexported field or method firstName)\n\t// p.firstName = \"Eric\"\n\tp.SetFirstName(\"Eric\")\n\tfmt.Println(p.FirstName()) // Output: Eric\n}\n```\n\n**并发访问对象**\n\n对象的字段（属性）不应该由 2 个或 2 个以上的不同线程在同一时间去改变。如果在程序发生这种情况，为了安全并发访问，可以使用包 `sync`（参考[第 9.3 节](09.3.md)中的方法。在[第 14.17 节](14.17)中我们会通过 goroutines 和 channels 探索另一种方式。\n\n## 10.6.5 内嵌类型的方法和继承\n\n当一个匿名类型被内嵌在结构体中时，匿名类型的可见方法也同样被内嵌，这在效果上等同于外层类型 **继承** 了这些方法：**将父类型放在子类型中来实现亚型**。这个机制提供了一种简单的方式来模拟经典面向对象语言中的子类和继承相关的效果，也类似 Ruby 中的混入 (mixin)。\n\n下面是一个示例（可以在练习 10.8 中进一步学习）：假定有一个 `Engine` 接口类型，一个 `Car` 结构体类型，它包含一个 `Engine` 类型的匿名字段：\n\n```go\ntype Engine interface {\n\tStart()\n\tStop()\n}\n\ntype Car struct {\n\tEngine\n}\n```\n\n我们可以构建如下的代码：\n\n```go\nfunc (c *Car) GoToWorkIn() {\n\t// get in car\n\tc.Start()\n\t// drive to work\n\tc.Stop()\n\t// get out of car\n}\n```\n\n下面是 [method3.go](examples/chapter_10/method3.go) 的完整例子，它展示了内嵌结构体上的方法可以直接在外层类型的实例上调用：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n)\n\ntype Point struct {\n\tx, y float64\n}\n\nfunc (p *Point) Abs() float64 {\n\treturn math.Sqrt(p.x*p.x + p.y*p.y)\n}\n\ntype NamedPoint struct {\n\tPoint\n\tname string\n}\n\nfunc main() {\n\tn := &NamedPoint{Point{3, 4}, \"Pythagoras\"}\n\tfmt.Println(n.Abs()) // 打印 5\n}\n```\n\n内嵌将一个已存在类型的字段和方法注入到了另一个类型里：匿名字段上的方法“晋升”成为了外层类型的方法。当然类型可以有只作用于本身实例而不作用于内嵌“父”类型上的方法。\n\n可以覆写方法（像字段一样）：和内嵌类型方法具有同样名字的外层类型的方法会覆写内嵌类型对应的方法。\n\n在示例 10.18 [method4.go](examples/chapter_10/method4.go) 中添加：\n\n```go\nfunc (n *NamedPoint) Abs() float64 {\n\treturn n.Point.Abs() * 100.\n}\n```\n\n现在 `fmt.Println(n.Abs())` 会打印 `500`。\n\n因为一个结构体可以嵌入多个匿名类型，所以实际上我们可以有一个简单版本的多重继承，就像：`type Child struct { Father; Mother}`。在[第 10.6.7 节](10.6.md)中会进一步讨论这个问题。\n\n结构体内嵌和自己在同一个包中的结构体时，可以彼此访问对方所有的字段和方法。\n\n**练习 10.8** [inheritance_car.go](exercises/chapter_10/inheritance_car.go)\n\n创建一个上面 `Car` 和 `Engine` 可运行的例子，并且给 `Car` 类型一个 `wheelCount` 字段和一个 `numberOfWheels()` 方法。\n\n创建一个 `Mercedes` 类型，它内嵌 `Car`，并新建 `Mercedes` 的一个实例，然后调用它的方法。\n\n然后仅在 `Mercedes` 类型上创建方法 `sayHiToMerkel()` 并调用它。\n\n## 10.6.6 如何在类型中嵌入功能\n\n主要有两种方法来实现在类型中嵌入功能：\n\nA：聚合（或组合）：包含一个所需功能类型的具名字段。\n\nB：内嵌：内嵌（匿名地）所需功能类型，像前一节 10.6.5 所演示的那样。\n\n为了使这些概念具体化，假设有一个 `Customer` 类型，我们想让它通过 `Log` 类型来包含日志功能，`Log` 类型只是简单地包含一个累积的消息（当然它可以是复杂的）。如果想让特定类型都具备日志功能，你可以实现一个这样的 `Log` 类型，然后将它作为特定类型的一个字段，并提供 `Log()`，它返回这个日志的引用。\n\n方式 A 可以通过如下方法实现（使用了[第 10.7 节](10.7.md)中的 `String()` 功能）：\n\n示例 10.19 [embed_func1.go](examples/chapter_10/embed_func1.go)：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\ntype Log struct {\n\tmsg string\n}\n\ntype Customer struct {\n\tName string\n\tlog  *Log\n}\n\nfunc main() {\n\tc := new(Customer)\n\tc.Name = \"Barak Obama\"\n\tc.log = new(Log)\n\tc.log.msg = \"1 - Yes we can!\"\n\t// shorter\n\tc = &Customer{\"Barak Obama\", &Log{\"1 - Yes we can!\"}}\n\t// fmt.Println(c) &{Barak Obama 1 - Yes we can!}\n\tc.Log().Add(\"2 - After me the world will be a better place!\")\n\t//fmt.Println(c.log)\n\tfmt.Println(c.Log())\n\n}\n\nfunc (l *Log) Add(s string) {\n\tl.msg += \"\\n\" + s\n}\n\nfunc (l *Log) String() string {\n\treturn l.msg\n}\n\nfunc (c *Customer) Log() *Log {\n\treturn c.log\n}\n```\n\n输出：\n\n    1 - Yes we can!\n    2 - After me the world will be a better place!\n\n相对的方式 B 可能会像这样 ([embed_func2.go](examples/chapter_10/embed_func2.go))：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\ntype Log struct {\n\tmsg string\n}\n\ntype Customer struct {\n\tName string\n\tLog\n}\n\nfunc main() {\n\tc := &Customer{\"Barak Obama\", Log{\"1 - Yes we can!\"}}\n\tc.Add(\"2 - After me the world will be a better place!\")\n\tfmt.Println(c)\n\n}\n\nfunc (l *Log) Add(s string) {\n\tl.msg += \"\\n\" + s\n}\n\nfunc (l *Log) String() string {\n\treturn l.msg\n}\n\nfunc (c *Customer) String() string {\n\treturn c.Name + \"\\nLog:\" + fmt.Sprintln(c.Log.String())\n}\n```\n\n输出：\n\n    Barak Obama\n    Log:1 - Yes we can!\n    2 - After me the world will be a better place!\n\n内嵌的类型不需要指针，`Customer` 也不需要 `Add` 方法，它使用 `Log` 的 `Add` 方法，`Customer` 有自己的 `String` 方法，并且在它里面调用了 `Log` 的 `String` 方法。\n\n如果内嵌类型嵌入了其他类型，也是可以的，那些类型的方法可以直接在外层类型中使用。\n\n因此一个好的策略是创建一些小的、可复用的类型作为一个工具箱，用于组成域类型。\n\n## 10.6.7 多重继承\n\n多重继承指的是类型获得多个父类型行为的能力，它在传统的面向对象语言中通常是不被实现的（C++ 和 Python 例外）。因为在类继承层次中，多重继承会给编译器引入额外的复杂度。但是在 Go 语言中，通过在类型中嵌入所有必要的父类型，可以很简单的实现多重继承。\n\n作为一个例子，假设有一个类型 `CameraPhone`，通过它可以 `Call()`，也可以 `TakeAPicture()`，但是第一个方法属于类型 `Phone`，第二个方法属于类型 `Camera`。\n\n只要嵌入这两个类型就可以解决这个问题，如下所示 ([mult_inheritance.go](examples/chapter_10/mult_inheritance.go))：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\ntype Camera struct{}\n\nfunc (c *Camera) TakeAPicture() string {\n\treturn \"Click\"\n}\n\ntype Phone struct{}\n\nfunc (p *Phone) Call() string {\n\treturn \"Ring Ring\"\n}\n\ntype CameraPhone struct {\n\tCamera\n\tPhone\n}\n\nfunc main() {\n\tcp := new(CameraPhone)\n\tfmt.Println(\"Our new CameraPhone exhibits multiple behaviors...\")\n\tfmt.Println(\"It exhibits behavior of a Camera: \", cp.TakeAPicture())\n\tfmt.Println(\"It works like a Phone too: \", cp.Call())\n}\n```\n\n输出：\n\n    Our new CameraPhone exhibits multiple behaviors...\n    It exhibits behavior of a Camera: Click\n    It works like a Phone too: Ring Ring\n\n**练习 10.9** [point_methods.go](exercises/chapter_10/point_methods.go)：\n\n从 `point.go` 开始（[第 10.1 节](10.1)的练习）：使用方法来实现 `Abs()` 和 `Scale()`函数，`Point` 作为方法的接收者类型。也为 `Point3` 和 `Polar` 实现 `Abs()` 方法。完成了 `point.go` 中同样的事情，只是这次通过方法。\n\n**练习 10.10** [inherit_methods.go](exercises/chapter_10/inherit_methods.go)：\n\n定义一个结构体类型 `Base`，它包含一个字段 `id`，方法 `Id()` 返回 `id`，方法 `SetId()` 修改 `id`。结构体类型 `Person` 包含 `Base`，及 `FirstName` 和 `LastName` 字段。结构体类型 `Employee` 包含一个 `Person` 和 `salary` 字段。\n\n创建一个 `employee` 实例，然后显示它的 `id`。\n\n**练习 10.11** [magic.go](exercises/chapter_10/magic.go)：\n\n首先预测一下下面程序的结果，然后动手实验下：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\ntype Base struct{}\n\nfunc (Base) Magic() {\n\tfmt.Println(\"base magic\")\n}\n\nfunc (self Base) MoreMagic() {\n\tself.Magic()\n\tself.Magic()\n}\n\ntype Voodoo struct {\n\tBase\n}\n\nfunc (Voodoo) Magic() {\n\tfmt.Println(\"voodoo magic\")\n}\n\nfunc main() {\n\tv := new(Voodoo)\n\tv.Magic()\n\tv.MoreMagic()\n}\n```\n\n## 10.6.8 通用方法和方法命名\n\n在编程中一些基本操作会一遍又一遍的出现，比如打开 (Open)、关闭 (Close)、读 (Read)、写 (Write)、排序(Sort) 等等，并且它们都有一个大致的意思：打开 (Open)可以作用于一个文件、一个网络连接、一个数据库连接等等。具体的实现可能千差万别，但是基本的概念是一致的。在 Go 语言中，通过使用接口（参考[第 11 章](11.0.md)），标准库广泛的应用了这些规则，在标准库中这些通用方法都有一致的名字，比如 `Open()`、`Read()`、`Write()`等。想写规范的 Go 程序，就应该遵守这些约定，给方法合适的名字和签名，就像那些通用方法那样。这样做会使 Go 开发的软件更加具有一致性和可读性。比如：如果需要一个 `convert-to-string()` 方法，应该命名为 `String()`，而不是 `ToString()`（参考[第 10.7 节](10.7.md)）。\n\n## 10.6.9 和其他面向对象语言比较 Go 的类型和方法\n\n在如 C++、Java、C# 和 Ruby 这样的面向对象语言中，方法在类的上下文中被定义和继承：在一个对象上调用方法时，运行时会检测类以及它的超类中是否有此方法的定义，如果没有会导致异常发生。\n\n在 Go 语言中，这样的继承层次是完全没必要的：如果方法在此类型定义了，就可以调用它，和其他类型上是否存在这个方法没有关系。在这个意义上，Go 具有更大的灵活性。\n\n下面的模式就很好的说明了这个问题：\n\n<img src=\"images/10.6.9_fig10.4.jpg?raw=true\" style=\"zoom:80%;\" />\n\nGo 不需要一个显式的类定义，如同 Java、C++、C# 等那样，相反地，“类”是通过提供一组作用于一个共同类型的方法集来隐式定义的。类型可以是结构体或者任何用户自定义类型。\n\n比如：我们想定义自己的 `Integer` 类型，并添加一些类似转换成字符串的方法，在 Go 中可以如下定义：\n\n```go\ntype Integer int\nfunc (i *Integer) String() string {\n    return strconv.Itoa(int(*i))\n}\n```\n\n在 Java 或 C# 中，这个方法需要和类 `Integer` 的定义放在一起，在 Ruby 中可以直接在基本类型 `int` 上定义这个方法。\n\n**总结**\n\n在 Go 中，类型就是类（数据和关联的方法）。Go 不知道类似面向对象语言的类继承的概念。继承有两个好处：代码复用和多态。\n\n在 Go 中，代码复用通过组合和委托实现，多态通过接口的使用来实现：有时这也叫 **组件编程 (Component Programming)**。\n\n许多开发者说相比于类继承，Go 的接口提供了更强大、却更简单的多态行为。\n\n**备注**\n\n如果真的需要更多面向对象的能力，看一下 [`goop`](https://github.com/losalamos/goop) 包 (Go Object-Oriented Programming)，它由 Scott Pakin 编写: 它给 Go 提供了 JavaScript 风格的对象（基于原型的对象），并且支持多重继承和类型独立分派，通过它可以实现你喜欢的其他编程语言里的一些结构。\n\n**问题 10.1**\n\n我们在某个类型的变量上使用点号调用一个方法：`variable.method()`，在使用 Go 以前，在哪儿碰到过面向对象的点号？\n\n**问题 10.2**\n\na）假设定义： `type Integer int`，完成 `get()` 方法的方法体: `func (p Integer) get() int { ... }`。\n\nb）定义： `func f(i int) {}; var v Integer` ，如何就 `v` 作为参数调用f？\n\nc）假设 `Integer` 定义为 `type Integer struct {n int}`，完成 `get()` 方法的方法体：`func (p Integer) get() int { ... }`。\n\nd）对于新定义的 `Integer`，和 b）中同样的问题。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[匿名字段和内嵌结构体](10.5.md)\n- 下一节：[类型的 String() 方法和格式化描述符](10.7.md)\n"
  },
  {
    "path": "eBook/10.7.md",
    "content": "# 10.7 类型的 String() 方法和格式化描述符\r\n\r\n当定义了一个有很多方法的类型时，十之八九你会使用 `String()` 方法来定制类型的字符串形式的输出，换句话说：一种可阅读性和打印性的输出。如果类型定义了 `String()` 方法，它会被用在 `fmt.Printf()` 中生成默认的输出：等同于使用格式化描述符 `%v` 产生的输出。还有 `fmt.Print()` 和 `fmt.Println()` 也会自动使用 `String()` 方法。\r\n\r\n我们使用[第 10.4 节](10.4.md)中程序的类型来进行测试：\r\n\r\n\r\n示例 10.22 [method_string.go](examples/chapter_10/method_string.go)：\r\n\r\n```go\r\npackage main\r\n\r\nimport (\r\n\t\"fmt\"\r\n\t\"strconv\"\r\n)\r\n\r\ntype TwoInts struct {\r\n\ta int\r\n\tb int\r\n}\r\n\r\nfunc main() {\r\n\ttwo1 := new(TwoInts)\r\n\ttwo1.a = 12\r\n\ttwo1.b = 10\r\n\tfmt.Printf(\"two1 is: %v\\n\", two1)\r\n\tfmt.Println(\"two1 is:\", two1)\r\n\tfmt.Printf(\"two1 is: %T\\n\", two1)\r\n\tfmt.Printf(\"two1 is: %#v\\n\", two1)\r\n}\r\n\r\nfunc (tn *TwoInts) String() string {\r\n\treturn \"(\" + strconv.Itoa(tn.a) + \"/\" + strconv.Itoa(tn.b) + \")\"\r\n}\r\n```\r\n\r\n输出：\r\n\r\n    two1 is: (12/10)\r\n    two1 is: (12/10)\r\n    two1 is: *main.TwoInts\r\n    two1 is: &main.TwoInts{a:12, b:10}\r\n\r\n当你广泛使用一个自定义类型时，最好为它定义 `String()`方法。从上面的例子也可以看到，格式化描述符 `%T` 会给出类型的完全规格，`%#v` 会给出实例的完整输出，包括它的字段（在程序自动生成 `Go` 代码时也很有用）。\r\n\r\n**备注**\r\n\r\n不要在 `String()` 方法里面调用涉及 `String()` 方法的方法，它会导致意料之外的错误，比如下面的例子，它导致了一个无限递归调用（`TT.String()` 调用 `fmt.Sprintf`，而 `fmt.Sprintf` 又会反过来调用 `TT.String()`），很快就会导致内存溢出：\r\n\r\n\r\n```go\r\ntype TT float64\r\n\r\nfunc (t TT) String() string {\r\n    return fmt.Sprintf(\"%v\", t)\r\n}\r\nt.String()\r\n```\r\n\r\n**练习 10.12** [type_string.go](exercises/chapter_10/type_string.go)\r\n\r\n给定结构体类型 `T`:\r\n\r\n```go\r\ntype T struct {\r\n    a int\r\n    b float32\r\n    c string\r\n}\r\n```\r\n\r\n值 `t`: `t := &T{7, -2.35, \"abc\\tdef\"}`。给 T 定义 `String()`，使得 `fmt.Printf(\"%v\\n\", t)` 输出：`7 / -2.350000 / \"abc\\tdef\"`。\r\n\r\n**练习 10.13** [celsius.go](exercises/chapter_10/celsius.go)\r\n\r\n为 `float64` 定义一个别名类型 `Celsius`，并给它定义 `String()`，它输出一个十进制数和 °C 表示的温度值。\r\n\r\n**练习 10.14** [days.go](exercises/chapter_10/days.go)\r\n\r\n为 `int` 定义一个别名类型 `Day`，定义一个字符串数组它包含一周七天的名字，为类型 `Day` 定义 `String()` 方法，它输出星期几的名字。使用 `iota` 定义一个枚举常量用于表示一周的中每天（MO、TU...）。\r\n\r\n**练习 10.15** [timezones.go](exercises/chapter_10/timezones.go)\r\n\r\n为 `int` 定义别名类型 `TZ`，定义一些常量表示时区，比如 UTC，定义一个 `map`，它将时区的缩写映射为它的全称，比如：`UTC -> \"Universal Greenwich time\"`。为类型 `TZ` 定义 `String()` 方法，它输出时区的全称。\r\n\r\n**练习 10.16** [stack_arr.go](exercises/chapter_10/stack_arr.go) / [stack_struct.go](exercises/chapter_10/stack_struct.go)\r\n\r\n实现栈 (stack) 数据结构：\r\n\r\n![](images/10.7_fig.jpg?raw=true)\r\n\r\n它的格子包含数据，比如整数 `i`、`j`、`k` 和 `l` 等等，格子从底部（索引 0）至顶部（索引 n）来索引。这个例子中假定 `n = 3`，那么一共有 4 个格子。\r\n\r\n一个新栈中所有格子的值都是 `0`。\r\n\r\n将一个新值放到栈的最顶部一个空（包括零）的格子中，这叫做 push。\r\n\r\n获取栈的最顶部一个非空（非零）的格子的值，这叫做 pop。\r\n现在可以理解为什么栈是一个后进先出 (LIFO) 的结构了吧。\r\n\r\n为栈定义一个 `Stack` 类型，并为它定义 `Push` 和 `Pop` 方法，再为它定义 `String()` 方法（用于调试）输出栈的内容，比如：`[0:i] [1:j] [2:k] [3:l]`。\r\n\r\n1）[stack_arr.go](exercises/chapter_10/stack_arr.go)：使用长度为 4 的 int 数组作为底层数据结构。\r\n\r\n2） [stack_struct.go](exercises/chapter_10/stack_struct.go)：使用包含一个索引和一个 `int` 数组的结构体作为底层数据结构，索引表示第一个空闲的位置。\r\n\r\n3）使用常量 `LIMIT` 代替上面表示元素个数的 4 重新实现上面的 1）和 2），使它们更具有一般性。\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[方法](10.6.md)\r\n- 下一节：[垃圾回收和 SetFinalizer](10.8.md)\r\n"
  },
  {
    "path": "eBook/10.8.md",
    "content": "# 10.8 垃圾回收和 SetFinalizer\r\n\r\nGo 开发者不需要写代码来释放程序中不再使用的变量和结构占用的内存，在 Go 运行时中有一个独立的进程，即垃圾收集器 (GC)，会处理这些事情，它搜索不再使用的变量然后释放它们的内存。可以通过 `runtime` 包访问 GC 进程。\r\n\r\n通过调用 `runtime.GC()` 函数可以显式的触发 GC，但这只在某些罕见的场景下才有用，比如当内存资源不足时调用 `runtime.GC()`，它会在此函数执行的点上立即释放一大片内存，此时程序可能会有短时的性能下降（因为 `GC` 进程在执行）。\r\n\r\n如果想知道当前的内存状态，可以使用：\r\n\r\n```go\r\n// fmt.Printf(\"%d\\n\", runtime.MemStats.Alloc/1024)\r\n// 此处代码在 Go 1.5.1下不再有效，更正为\r\nvar m runtime.MemStats\r\nruntime.ReadMemStats(&m)\r\nfmt.Printf(\"%d Kb\\n\", m.Alloc / 1024)\r\n```\r\n\r\n上面的程序会给出已分配内存的总量，单位是 Kb。进一步的测量参考 [文档页面](http://golang.org/pkg/runtime/#MemStatsType)。\r\n\r\n如果需要在一个对象 `obj` 被从内存移除前执行一些特殊操作，比如写到日志文件中，可以通过如下方式调用函数来实现：\r\n\r\n```go\r\nruntime.SetFinalizer(obj, func(obj *typeObj))\r\n```\r\n\r\n`func(obj *typeObj)` 需要一个 `typeObj` 类型的指针参数 `obj`，特殊操作会在它上面执行。`func` 也可以是一个匿名函数。\r\n\r\n在对象被 GC 进程选中并从内存中移除以前，`SetFinalizer` 都不会执行，即使程序正常结束或者发生错误。\r\n\r\n**练习 10.17** [main_stack.go](exercises/chapter_10/main_stack.go)\r\n\r\n从练习 10.16 开始（它基于结构体实现了一个栈结构），为栈的实现 ([stack_struct.go](exercises/chapter_10/stack_struct.go)) 创建一个单独的包 `stack`，并从 `main` 包 `main.stack.go` 中调用它。\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[类型的 String() 方法和格式化描述符](10.7.md)\r\n- 下一章：[接口 (Interfaces) 与反射 (reflection)](11.0.md)\r\n"
  },
  {
    "path": "eBook/11.0.md",
    "content": "# 11.0 接口 (interface)与反射 (reflection)\n\n本章介绍 Go 语言中接口和反射的相关内容。\n\n## 链接\n\n- [目录](directory.md)\n- 上一章：[垃圾回收和 SetFinalizer](10.8.md)\n- 下一节：[接口是什么](11.1.md)\n"
  },
  {
    "path": "eBook/11.1.md",
    "content": "# 11.1 接口是什么\n\nGo 语言不是一种 *“传统”* 的面向对象编程语言：它里面没有类和继承的概念。\n\n但是 Go 语言里有非常灵活的 **接口** 概念，通过它可以实现很多面向对象的特性。接口提供了一种方式来 **说明** 对象的行为：如果谁能搞定这件事，它就可以用在这儿。\n\n接口定义了一组方法（方法集），但是这些方法不包含（实现）代码：它们没有被实现（它们是抽象的）。接口里也不能包含变量。\n\n通过如下格式定义接口：\n\n```go\ntype Namer interface {\n    Method1(param_list) return_type\n    Method2(param_list) return_type\n    ...\n}\n```\n\n上面的 `Namer` 是一个 **接口类型**。\n\n（按照约定，只包含一个方法的）接口的名字由方法名加 `er` 后缀组成，例如 `Printer`、`Reader`、`Writer`、`Logger`、`Converter` 等等。还有一些不常用的方式（当后缀 `er` 不合适时），比如 `Recoverable`，此时接口名以 `able` 结尾，或者以 `I` 开头（像 `.NET` 或 `Java` 中那样）。\n\nGo 语言中的接口都很简短，通常它们会包含 0 个、最多 3 个方法。\n\n不像大多数面向对象编程语言，在 Go 语言中接口可以有值，一个接口类型的变量或一个 **接口值** ：`var ai Namer`，`ai` 是一个多字（multiword）数据结构，它的值是 `nil`。它本质上是一个指针，虽然不完全是一回事。指向接口值的指针是非法的，它们不仅一点用也没有，还会导致代码错误。\n\n![](images/11.1_fig11.1.jpg?raw=true)\n\n此处的方法指针表是通过运行时反射能力构建的。\n\n类型（比如结构体）可以实现某个接口的方法集；这个实现可以描述为，该类型的变量上的每一个具体方法所组成的集合，包含了该接口的方法集。实现了 `Namer` 接口的类型的变量可以赋值给 `ai`（即 `receiver` 的值），方法表指针（method table ptr）就指向了当前的方法实现。当另一个实现了 `Namer` 接口的类型的变量被赋给 `ai`，`receiver` 的值和方法表指针也会相应改变。\n\n**类型不需要显式声明它实现了某个接口：接口被隐式地实现。多个类型可以实现同一个接口**。\n\n**实现某个接口的类型（除了实现接口方法外）可以有其他的方法**。\n\n**一个类型可以实现多个接口**。\n\n**接口类型可以包含一个实例的引用， 该实例的类型实现了此接口（接口是动态类型）**。\n\n即使接口在类型之后才定义，二者处于不同的包中，被单独编译：只要类型实现了接口中的方法，它就实现了此接口。\n\n所有这些特性使得接口具有很大的灵活性。\n\n第一个例子：\n\n示例 11.1 [interfaces.go](examples/chapter_11/interfaces.go)：\n\n```go\npackage main\n\nimport \"fmt\"\n\ntype Shaper interface {\n\tArea() float32\n}\n\ntype Square struct {\n\tside float32\n}\n\nfunc (sq *Square) Area() float32 {\n\treturn sq.side * sq.side\n}\n\nfunc main() {\n\tsq1 := new(Square)\n\tsq1.side = 5\n\n\tvar areaIntf Shaper\n\tareaIntf = sq1\n\t// shorter,without separate declaration:\n\t// areaIntf := Shaper(sq1)\n\t// or even:\n\t// areaIntf := sq1\n\tfmt.Printf(\"The square has area: %f\\n\", areaIntf.Area())\n}\n```\n\n输出：\n\n    The square has area: 25.000000\n\n上面的程序定义了一个结构体 `Square` 和一个接口 `Shaper`，接口有一个方法 `Area()`。\n\n在 `main()` 方法中创建了一个 `Square` 的实例。在主程序外边定义了一个接收者类型是 `Square` 方法的 `Area()`，用来计算正方形的面积：结构体 `Square` 实现了接口 `Shaper` 。\n\n所以可以将一个 `Square` 类型的变量赋值给一个接口类型的变量：`areaIntf = sq1` 。\n\n现在接口变量包含一个指向 `Square` 变量的引用，通过它可以调用 `Square` 上的方法 `Area()`。当然也可以直接在 `Square` 的实例上调用此方法，但是在接口实例上调用此方法更令人兴奋，它使此方法更具有一般性。接口变量里包含了接收者实例的值和指向对应方法表的指针。\n\n这是 **多态** 的 Go 版本，多态是面向对象编程中一个广为人知的概念：根据当前的类型选择正确的方法，或者说：同一种类型在不同的实例上似乎表现出不同的行为。\n\n如果 `Square` 没有实现 `Area()` 方法，编译器将会给出清晰的错误信息：\n\n    cannot use sq1 (type *Square) as type Shaper in assignment:\n    *Square does not implement Shaper (missing Area method)\n\n如果 `Shaper` 有另外一个方法 `Perimeter()`，但是 `Square` 没有实现它，即使没有人在 `Square` 实例上调用这个方法，编译器也会给出上面同样的错误。\n\n扩展一下上面的例子，类型 `Rectangle` 也实现了 `Shaper` 接口。接着创建一个 `Shaper` 类型的数组，迭代它的每一个元素并在上面调用 `Area()` 方法，以此来展示多态行为：\n\n示例 11.2 [interfaces_poly.go](examples/chapter_11/interfaces_poly.go)：\n\n```go\npackage main\n\nimport \"fmt\"\n\ntype Shaper interface {\n\tArea() float32\n}\n\ntype Square struct {\n\tside float32\n}\n\nfunc (sq *Square) Area() float32 {\n\treturn sq.side * sq.side\n}\n\ntype Rectangle struct {\n\tlength, width float32\n}\n\nfunc (r Rectangle) Area() float32 {\n\treturn r.length * r.width\n}\n\nfunc main() {\n\n\tr := Rectangle{5, 3} // Area() of Rectangle needs a value\n\tq := &Square{5}      // Area() of Square needs a pointer\n\t// shapes := []Shaper{Shaper(r), Shaper(q)}\n\t// or shorter\n\tshapes := []Shaper{r, q}\n\tfmt.Println(\"Looping through shapes for area ...\")\n\tfor n, _ := range shapes {\n\t\tfmt.Println(\"Shape details: \", shapes[n])\n\t\tfmt.Println(\"Area of this shape is: \", shapes[n].Area())\n\t}\n}\n```\n\n输出：\n\n    Looping through shapes for area ...\n    Shape details:  {5 3}\n    Area of this shape is:  15\n    Shape details:  &{5}\n    Area of this shape is:  25\n\n在调用 `shapes[n].Area()` 这个时，只知道 `shapes[n]` 是一个 `Shaper` 对象，最后它摇身一变成为了一个 `Square` 或 `Rectangle` 对象，并且表现出了相对应的行为。\n\n也许从现在开始你将看到通过接口如何产生 **更干净**、**更简单** 及 **更具有扩展性** 的代码。在 11.12.3 中将看到在开发中为类型添加新的接口是多么的容易。\n\n下面是一个更具体的例子：有两个类型 `stockPosition` 和 `car`，它们都有一个 `getValue()` 方法，我们可以定义一个具有此方法的接口 `valuable`。接着定义一个使用 `valuable` 类型作为参数的函数 `showValue()`，所有实现了 `valuable` 接口的类型都可以用这个函数。\n\n示例 11.3 [valuable.go](examples/chapter_11/valuable.go)：\n\n```go\npackage main\n\nimport \"fmt\"\n\ntype stockPosition struct {\n\tticker     string\n\tsharePrice float32\n\tcount      float32\n}\n\n/* method to determine the value of a stock position */\nfunc (s stockPosition) getValue() float32 {\n\treturn s.sharePrice * s.count\n}\n\ntype car struct {\n\tmake  string\n\tmodel string\n\tprice float32\n}\n\n/* method to determine the value of a car */\nfunc (c car) getValue() float32 {\n\treturn c.price\n}\n\n/* contract that defines different things that have value */\ntype valuable interface {\n\tgetValue() float32\n}\n\nfunc showValue(asset valuable) {\n\tfmt.Printf(\"Value of the asset is %f\\n\", asset.getValue())\n}\n\nfunc main() {\n\tvar o valuable = stockPosition{\"GOOG\", 577.20, 4}\n\tshowValue(o)\n\to = car{\"BMW\", \"M3\", 66500}\n\tshowValue(o)\n}\n```\n\n输出：\n\n    Value of the asset is 2308.800049\n    Value of the asset is 66500.000000\n\n**一个标准库的例子**\n\n`io` 包里有一个接口类型 `Reader`:\n\n```go\ntype Reader interface {\n    Read(p []byte) (n int, err error)\n}\n```\n\n定义变量 `r`：` var r io.Reader`\n\n那么就可以写如下的代码：\n\n```go\n\tvar r io.Reader\n\tr = os.Stdin    // see 12.1\n\tr = bufio.NewReader(r)\n\tr = new(bytes.Buffer)\n\tf,_ := os.Open(\"test.txt\")\n\tr = bufio.NewReader(f)\n```\n\n上面 `r` 右边的类型都实现了 `Read()` 方法，并且有相同的方法签名，`r` 的静态类型是 `io.Reader`。\n\n**备注**\n\n有的时候，也会以一种稍微不同的方式来使用接口这个词：从某个类型的角度来看，它的接口指的是：它的所有导出方法，只不过没有显式地为这些导出方法额外定一个接口而已。\n\n**练习 11.1** [simple_interface.go](exercises/chapter_11/simple_interface.go)：\n\n定义一个接口 `Simpler`，它有一个 `Get()` 方法和一个 `Set()`，`Get()` 返回一个整型值，`Set()` 有一个整型参数。创建一个结构体类型 `Simple` 实现这个接口。\n\n接着定一个函数，它有一个 `Simpler` 类型的参数，调用参数的 `Get()` 和 `Set()` 方法。在 `main` 函数里调用这个函数，看看它是否可以正确运行。\n\n**练习 11.2** [interfaces_poly2.go](exercises/chapter_11/interfaces_poly2.go)：\n\na) 扩展 [interfaces_poly.go](exercises/chapter_11/interfaces_poly.go) 中的例子，添加一个 `Circle` 类型\n\nb) 使用一个抽象类型 `Shape`（没有字段） 实现同样的功能，它实现接口 `Shaper`，然后在其他类型里内嵌此类型。扩展 [10.6.5](10.6.md) 中的例子来说明覆写。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[接口 (Interfaces) 与反射 (reflection)](11.0.md)\n- 下一节：[接口嵌套接口](11.2.md)\n"
  },
  {
    "path": "eBook/11.10.md",
    "content": "# 11.10 反射包\n\n## 11.10.1 方法和类型的反射\n\n在 [10.4](10.4.md) 节我们看到可以通过反射来分析一个结构体。本节我们进一步探讨强大的反射功能。反射是用程序检查其所拥有的结构，尤其是类型的一种能力；这是元编程的一种形式。反射可以在运行时检查类型和变量，例如：它的大小、它的方法以及它能“动态地”调用这些方法。这对于没有源代码的包尤其有用。这是一个强大的工具，除非真得有必要，否则应当避免使用或小心使用。\n\n变量的最基本信息就是类型和值：反射包的 `Type` 用来表示一个 Go 类型，反射包的 `Value` 为 Go 值提供了反射接口。\n\n两个简单的函数，`reflect.TypeOf` 和 `reflect.ValueOf`，返回被检查对象的类型和值。例如，x 被定义为：`var x float64 = 3.4`，那么 `reflect.TypeOf(x)` 返回 `float64`，`reflect.ValueOf(x)` 返回 `<float64 Value>`\n\n实际上，反射是通过检查一个接口的值，变量首先被转换成空接口。这从下面两个函数签名能够很明显的看出来：\n\n```go\nfunc TypeOf(i interface{}) Type\nfunc ValueOf(i interface{}) Value\n```\n\n接口的值包含一个 type 和 value。\n\n反射可以从接口值反射到对象，也可以从对象反射回接口值。\n\n`reflect.Type` 和 `reflect.Value` 都有许多方法用于检查和操作它们。一个重要的例子是 `Value` 有一个 `Type()` 方法返回 `reflect.Value` 的 `Type` 类型。另一个是 `Type` 和 `Value` 都有 `Kind()` 方法返回一个常量来表示类型：`Uint`、`Float64`、`Slice` 等等。同样 `Value` 有叫做 `Int()` 和 `Float()` 的方法可以获取存储在内部的值（跟 `int64` 和 `float64` 一样）\n\n```go\nconst (\n\tInvalid Kind = iota\n\tBool\n\tInt\n\tInt8\n\tInt16\n\tInt32\n\tInt64\n\tUint\n\tUint8\n\tUint16\n\tUint32\n\tUint64\n\tUintptr\n\tFloat32\n\tFloat64\n\tComplex64\n\tComplex128\n\tArray\n\tChan\n\tFunc\n\tInterface\n\tMap\n\tPtr\n\tSlice\n\tString\n\tStruct\n\tUnsafePointer\n)\n```\n\n对于 `float64` 类型的变量 `x`，如果 `v:=reflect.ValueOf(x)`，那么 `v.Kind()` 返回 `reflect.Float64` ，所以下面的表达式是 `true`：`v.Kind() == reflect.Float64`\n\n`Kind()` 总是返回底层类型：\n\n```go\ntype MyInt int\nvar m MyInt = 5\nv := reflect.ValueOf(m)\n```\n\n方法 `v.Kind()` 返回 `reflect.Int`。\n\n变量 `v` 的 `Interface()` 方法可以得到还原（接口）值，所以可以这样打印 `v` 的值：`fmt.Println(v.Interface())`\n\n\n尝试运行下面的代码：\n\n示例 11.11 [reflect1.go](examples/chapter_11/reflect1.go)：\n\n```go\n// blog: Laws of Reflection\npackage main\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\nfunc main() {\n\tvar x float64 = 3.4\n\tfmt.Println(\"type:\", reflect.TypeOf(x))\n\tv := reflect.ValueOf(x)\n\tfmt.Println(\"value:\", v)\n\tfmt.Println(\"type:\", v.Type())\n\tfmt.Println(\"kind:\", v.Kind())\n\tfmt.Println(\"value:\", v.Float())\n\tfmt.Println(v.Interface())\n\tfmt.Printf(\"value is %5.2e\\n\", v.Interface())\n\ty := v.Interface().(float64)\n\tfmt.Println(y)\n}\n```\n\n输出：\n\n```\ntype: float64\nvalue: 3.4\ntype: float64\nkind: float64\nvalue: 3.4\n3.4\nvalue is 3.40e+00\n3.4\n```\n\n`x` 是一个 `float64` 类型的值，`reflect.ValueOf(x).Float()` 返回这个 `float64` 类型的实际值；同样的适用于 `Int(), Bool(), Complex(), String()`\n\n## 11.10.2 通过反射修改（设置）值\n\n继续前面的例子（参阅 11.9 [reflect2.go](examples/chapter_11/reflect2.go)），假设我们要把 `x` 的值改为 `3.1415`。`Value` 有一些方法可以完成这个任务，但是必须小心使用：`v.SetFloat(3.1415)`。\n\n这将产生一个错误：`reflect.Value.SetFloat using unaddressable value`。\n\n为什么会这样呢？问题的原因是 `v` 不是可设置的（这里并不是说值不可寻址）。是否可设置是 `Value` 的一个属性，并且不是所有的反射值都有这个属性：可以使用 `CanSet()` 方法测试是否可设置。\n\n在例子中我们看到 `v.CanSet()` 返回 `false`： `settability of v: false`\n\n当 `v := reflect.ValueOf(x)` 函数通过传递一个 `x` 拷贝创建了 `v`，那么 `v` 的改变并不能更改原始的 `x`。要想 `v` 的更改能作用到 `x`，那就必须传递 x 的地址 `v = reflect.ValueOf(&x)`。\n\n通过 `Type()` 我们看到 `v` 现在的类型是 `*float64` 并且仍然是不可设置的。\n\n要想让其可设置我们需要使用 `Elem()` 函数，这间接地使用指针：`v = v.Elem()`\n\n现在 `v.CanSet()` 返回 `true` 并且 `v.SetFloat(3.1415)` 设置成功了！\n\n\n示例 11.12 [reflect2.go](examples/chapter_11/reflect2.go)：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\nfunc main() {\n\tvar x float64 = 3.4\n\tv := reflect.ValueOf(x)\n\t// setting a value:\n\t// v.SetFloat(3.1415) // Error: will panic: reflect.Value.SetFloat using unaddressable value\n\tfmt.Println(\"settability of v:\", v.CanSet())\n\tv = reflect.ValueOf(&x) // Note: take the address of x.\n\tfmt.Println(\"type of v:\", v.Type())\n\tfmt.Println(\"settability of v:\", v.CanSet())\n\tv = v.Elem()\n\tfmt.Println(\"The Elem of v is: \", v)\n\tfmt.Println(\"settability of v:\", v.CanSet())\n\tv.SetFloat(3.1415) // this works!\n\tfmt.Println(v.Interface())\n\tfmt.Println(v)\n}\n```\n\n输出：\n\n```\nsettability of v: false\ntype of v: *float64\nsettability of v: false\nThe Elem of v is:  <float64 Value>\nsettability of v: true\n3.1415\n<float64 Value>\n```\n\n反射中有些内容是需要用地址去改变它的状态的。\n\n## 11.10.3 反射结构\n\n有些时候需要反射一个结构类型。`NumField()` 方法返回结构内的字段数量；通过一个 `for` 循环用索引取得每个字段的值 `Field(i)`。\n\n我们同样能够调用签名在结构上的方法，例如，使用索引 `n` 来调用：`Method(n).Call(nil)`。\n\n示例 11.13 [reflect_struct.go](examples/chapter_11/reflect_struct.go)：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\ntype NotknownType struct {\n\ts1, s2, s3 string\n}\n\nfunc (n NotknownType) String() string {\n\treturn n.s1 + \" - \" + n.s2 + \" - \" + n.s3\n}\n\n// variable to investigate:\nvar secret interface{} = NotknownType{\"Ada\", \"Go\", \"Oberon\"}\n\nfunc main() {\n\tvalue := reflect.ValueOf(secret) // <main.NotknownType Value>\n\ttyp := reflect.TypeOf(secret)    // main.NotknownType\n\t// alternative:\n\t// typ := value.Type()  // main.NotknownType\n\tfmt.Println(typ)\n\tknd := value.Kind() // struct\n\tfmt.Println(knd)\n\n\t// iterate through the fields of the struct:\n\tfor i := 0; i < value.NumField(); i++ {\n\t\tfmt.Printf(\"Field %d: %v\\n\", i, value.Field(i))\n\t\t// error: panic: reflect.Value.SetString using value obtained using unexported field\n\t\t// value.Field(i).SetString(\"C#\")\n\t}\n\n\t// call the first method, which is String():\n\tresults := value.Method(0).Call(nil)\n\tfmt.Println(results) // [Ada - Go - Oberon]\n}\n```\n\n输出：\n\n```\nmain.NotknownType\nstruct\nField 0: Ada\nField 1: Go\nField 2: Oberon\n[Ada - Go - Oberon]\n```\n\n但是如果尝试更改一个值，会得到一个错误：\n\n```\npanic: reflect.Value.SetString using value obtained using unexported field\n```\n\n这是因为结构中只有被导出字段（首字母大写）才是可设置的；来看下面的例子：\n\n示例 11.14 [reflect_struct2.go](examples/chapter_11/reflect_struct2.go)：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\ntype T struct {\n\tA int\n\tB string\n}\n\nfunc main() {\n\tt := T{23, \"skidoo\"}\n\ts := reflect.ValueOf(&t).Elem()\n\ttypeOfT := s.Type()\n\tfor i := 0; i < s.NumField(); i++ {\n\t\tf := s.Field(i)\n\t\tfmt.Printf(\"%d: %s %s = %v\\n\", i,\n\t\t\ttypeOfT.Field(i).Name, f.Type(), f.Interface())\n\t}\n\ts.Field(0).SetInt(77)\n\ts.Field(1).SetString(\"Sunset Strip\")\n\tfmt.Println(\"t is now\", t)\n}\n```\n\n输出：\n\n```\n0: A int = 23\n1: B string = skidoo\nt is now {77 Sunset Strip}\n```\n\n附录 37 深入阐述了反射概念。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[空接口](11.9.md)\n- 下一节：[Printf 和反射](11.11.md)\n"
  },
  {
    "path": "eBook/11.11.md",
    "content": "# 11.11 Printf() 和反射\n\n在 Go 语言的标准库中，前几节所述的反射的功能被大量地使用。举个例子，`fmt` 包中的 `Printf()`（以及其他格式化输出函数）都会使用反射来分析它的 `...` 参数。\n\n`Printf()` 的函数声明为：\n\n```go\nfunc Printf(format string, args ... interface{}) (n int, err error)\n```\n\n`Printf()` 中的 `...` 参数为空接口类型。`Printf()` 使用反射包来解析这个参数列表。所以，`Printf()` 能够知道它每个参数的类型。因此格式化字符串中只有 `%d` 而没有 `%u` 和 `%ld`，因为它知道这个参数是 unsigned 还是 long。这也是为什么 `Print()` 和 `Println()` 在没有格式字符串的情况下还能如此漂亮地输出。\n\n为了让大家更加具体地了解 `Printf()` 中的反射，我们实现了一个简单的通用输出函数。其中使用了 type-switch 来推导参数类型，并根据类型来输出每个参数的值（这里用了 [10.7](10.7.md) 节中练习 10.13 的部分代码）\n\n示例 11.15 [print.go](examples/chapter_11/print.go)：\n\n```go\npackage main\n\nimport (\n\t\"os\"\n\t\"strconv\"\n)\n\ntype Stringer interface {\n\tString() string\n}\n\ntype Celsius float64\n\nfunc (c Celsius) String() string {\n\treturn strconv.FormatFloat(float64(c),'f', 1, 64) + \" °C\"\n}\n\ntype Day int\n\nvar dayName = []string{\"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\", \"Sunday\"}\n\nfunc (day Day) String() string {\n\treturn dayName[day]\n}\n\nfunc print(args ...interface{}) {\n\tfor i, arg := range args {\n\t\tif i > 0 {os.Stdout.WriteString(\" \")}\n\t\tswitch a := arg.(type) { // type switch\n\t\t\tcase Stringer:\tos.Stdout.WriteString(a.String())\n\t\t\tcase int:\t\tos.Stdout.WriteString(strconv.Itoa(a))\n\t\t\tcase string:\tos.Stdout.WriteString(a)\n\t\t\t// more types\n\t\t\tdefault:\t\tos.Stdout.WriteString(\"???\")\n\t\t}\n\t}\n}\n\nfunc main() {\n\tprint(Day(1), \"was\", Celsius(18.36))  // Tuesday was 18.4 °C\n}\n```\n\n在 [12.8](12.8.md) 节中我们将阐释 `fmt.Fprintf()` 是怎么运用同样的反射原则的。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[反射包](11.10.md)\n- 下一节：[接口和动态类型](11.12.md)\n"
  },
  {
    "path": "eBook/11.12.md",
    "content": "# 11.12 接口与动态类型\n\n## 11.12.1 Go 的动态类型\n\n在经典的面向对象语言（像 C++，Java 和 C#）中数据和方法被封装为*类*的概念：类包含它们两者，并且不能剥离。\n\nGo 没有类：数据（结构体或更一般的类型）和方法是一种松耦合的正交关系。\n\nGo 中的接口跟 Java/C# 类似：都是必须提供一个指定方法集的实现。但是更加灵活通用：任何提供了接口方法实现代码的类型都隐式地实现了该接口，而不用显式地声明。\n\n和其它语言相比，Go 是唯一结合了接口值，静态类型检查（是否该类型实现了某个接口），运行时动态转换的语言，并且不需要显式地声明类型是否满足某个接口。该特性允许我们在不改变已有的代码的情况下定义和使用新接口。\n\n接收一个（或多个）接口类型作为参数的函数，其**实参**可以是任何实现了该接口的类型的变量。 *实现了某个接口的类型可以被传给任何以此接口为参数的函数*。\n\n类似于 Python 和 Ruby 这类动态语言中的动态类型 (duck typing)；这意味着对象可以根据提供的方法被处理（例如，作为参数传递给函数），而忽略它们的实际类型：它们能做什么比它们是什么更重要。\n\n这在程序 [duck_dance.go](examples/chapter_11/duck_dance.go) 中得以阐明，函数 `DuckDance()` 接受一个 `IDuck` 接口类型变量。仅当 `DuckDance()` 被实现了 `IDuck` 接口的类型调用时程序才能编译通过。\n\n示例 11.16 [duck_dance.go](examples/chapter_11/duck_dance.go)：\n\n```go\npackage main\n\nimport \"fmt\"\n\ntype IDuck interface {\n\tQuack()\n\tWalk()\n}\n\nfunc DuckDance(duck IDuck) {\n\tfor i := 1; i <= 3; i++ {\n\t\tduck.Quack()\n\t\tduck.Walk()\n\t}\n}\n\ntype Bird struct {\n\t// ...\n}\n\nfunc (b *Bird) Quack() {\n\tfmt.Println(\"I am quacking!\")\n}\n\nfunc (b *Bird) Walk()  {\n\tfmt.Println(\"I am walking!\")\n}\n\nfunc main() {\n\tb := new(Bird)\n\tDuckDance(b)\n}\n```\n\n输出：\n\n```\nI am quacking!\nI am walking!\nI am quacking!\nI am walking!\nI am quacking!\nI am walking!\n```\n\n如果 `Bird` 没有实现 `Walk()`（把它注释掉），会得到一个编译错误：\n\n```\ncannot use b (type *Bird) as type IDuck in function argument:\n*Bird does not implement IDuck (missing Walk method)\n```\n\n如果对 `cat` 调用函数 `DuckDance()`，Go 会提示编译错误，但是 Python 和 Ruby 会以运行时错误结束。\n\n## 11.12.2 动态方法调用\n\n像 Python，Ruby 这类语言，动态类型是延迟绑定的（在运行时进行）：方法只是用参数和变量简单地调用，然后在运行时才解析（它们很可能有像 `responds_to` 这样的方法来检查对象是否可以响应某个方法，但是这也意味着更大的编码量和更多的测试工作）\n\nGo 的实现与此相反，通常需要编译器静态检查的支持：当变量被赋值给一个接口类型的变量时，编译器会检查其是否实现了该接口的所有函数。如果方法调用作用于像 `interface{}` 这样的“泛型”上，你可以通过类型断言（参见 [11.3](11.3.md) 节）来检查变量是否实现了相应接口。\n\n例如，你用不同的类型表示 XML 输出流中的不同实体。然后我们为 XML 定义一个如下的“写”接口（甚至可以把它定义为私有接口）：\n\n```go\ntype xmlWriter interface {\n\tWriteXML(w io.Writer) error\n}\n```\n\n现在我们可以实现适用于该流类型的任何变量的 `StreamXML()` 函数，并用类型断言检查传入的变量是否实现了该接口；如果没有，我们就调用内建的 `encodeToXML()` 来完成相应工作：\n\n```go\n// Exported XML streaming function.\nfunc StreamXML(v interface{}, w io.Writer) error {\n\tif xw, ok := v.(xmlWriter); ok {\n\t\t// It’s an  xmlWriter, use method of asserted type.\n\t\treturn xw.WriteXML(w)\n\t}\n\t// No implementation, so we have to use our own function (with perhaps reflection):\n\treturn encodeToXML(v, w)\n}\n\n// Internal XML encoding function.\nfunc encodeToXML(v interface{}, w io.Writer) error {\n\t// ...\n}\n```\n\nGo 在这里用了和 `gob` 相同的机制：定义了两个接口 `GobEncoder` 和 `GobDecoder`。这样就允许类型自己实现从流编解码的具体方式；如果没有实现就使用标准的反射方式。\n\n因此 Go 提供了动态语言的优点，却没有其他动态语言在运行时可能发生错误的缺点。\n\n对于动态语言非常重要的单元测试来说，这样即可以减少单元测试的部分需求，又可以发挥相当大的作用。\n\nGo 的接口提高了代码的分离度，改善了代码的复用性，使得代码开发过程中的设计模式更容易实现。用 Go 接口还能实现“依赖注入模式”。\n\n## 11.12.3 接口的提取\n\n*提取接口*是非常有用的设计模式，可以减少需要的类型和方法数量，而且不需要像传统的基于类的面向对象语言那样维护整个的类层次结构。\n\nGo 接口可以让开发者找出自己写的程序中的类型。假设有一些拥有共同行为的对象，并且开发者想要抽象出这些行为，这时就可以创建一个接口来使用。\n\n我们来扩展 11.1 节的示例 11.2 [interfaces_poly.go](examples/chapter_11/interfaces_poly.go)，假设我们需要一个新的接口 `TopologicalGenus`，用来给 `shape` 排序（这里简单地实现为返回 `int`）。我们需要做的是给想要满足接口的类型实现 `Rank()` 方法：\n\n示例 11.17 [multi_interfaces_poly.go](examples/chapter_11/multi_interfaces_poly.go)：\n\n```go\n//multi_interfaces_poly.go\npackage main\n\nimport \"fmt\"\n\ntype Shaper interface {\n\tArea() float32\n}\n\ntype TopologicalGenus interface {\n\tRank() int\n}\n\ntype Square struct {\n\tside float32\n}\n\nfunc (sq *Square) Area() float32 {\n\treturn sq.side * sq.side\n}\n\nfunc (sq *Square) Rank() int {\n\treturn 1\n}\n\ntype Rectangle struct {\n\tlength, width float32\n}\n\nfunc (r Rectangle) Area() float32 {\n\treturn r.length * r.width\n}\n\nfunc (r Rectangle) Rank() int {\n\treturn 2\n}\n\nfunc main() {\n\tr := Rectangle{5, 3} // Area() of Rectangle needs a value\n\tq := &Square{5}      // Area() of Square needs a pointer\n\tshapes := []Shaper{r, q}\n\tfmt.Println(\"Looping through shapes for area ...\")\n\tfor n, _ := range shapes {\n\t\tfmt.Println(\"Shape details: \", shapes[n])\n\t\tfmt.Println(\"Area of this shape is: \", shapes[n].Area())\n\t}\n\ttopgen := []TopologicalGenus{r, q}\n\tfmt.Println(\"Looping through topgen for rank ...\")\n\tfor n, _ := range topgen {\n\t\tfmt.Println(\"Shape details: \", topgen[n])\n\t\tfmt.Println(\"Topological Genus of this shape is: \", topgen[n].Rank())\n\t}\n}\n```\n\n输出：\n\n```\nLooping through shapes for area ...\nShape details:  {5 3}\nArea of this shape is:  15\nShape details:  &{5}\nArea of this shape is:  25\nLooping through topgen for rank ...\nShape details:  {5 3}\nTopological Genus of this shape is:  2\nShape details:  &{5}\nTopological Genus of this shape is:  1\n```\n\n所以你不用提前设计出所有的接口；*整个设计可以持续演进，而不用废弃之前的决定*。类型要实现某个接口，它本身不用改变，你只需要在这个类型上实现新的方法。\n\n## 11.12.4 显式地指明类型实现了某个接口\n\n如果你希望满足某个接口的类型显式地声明它们实现了这个接口，你可以向接口的方法集中添加一个具有描述性名字的方法。例如：\n\n```go\ntype Fooer interface {\n\tFoo()\n\tImplementsFooer()\n}\n```\n\n类型 Bar 必须实现 `ImplementsFooer` 方法来满足 `Fooer` 接口，以清楚地记录这个事实。\n\n```go\ntype Bar struct{}\nfunc (b Bar) ImplementsFooer() {}\nfunc (b Bar) Foo() {}\n```\n\n大部分代码并不使用这样的约束，因为它限制了接口的实用性。\n\n但是有些时候，这样的约束在大量相似的接口中被用来解决歧义。\n\n## 11.12.5 空接口和函数重载\n\n在 [6.1](06.1.md) 节中, 我们看到函数重载是不被允许的。在 Go 语言中函数重载可以用可变参数 `...T` 作为函数最后一个参数来实现（参见 [6.3](06.3.md) 节）。如果我们把 `T` 换为空接口，那么可以知道任何类型的变量都是满足 `T` (空接口）类型的，这样就允许我们传递任何数量任何类型的参数给函数，即重载的实际含义。\n\n函数 `fmt.Printf` 就是这样做的：\n\n```go\nfmt.Printf(format string, a ...interface{}) (n int, errno error)\n```\n\n这个函数通过枚举 slice 类型的实参动态确定所有参数的类型，并查看每个类型是否实现了 `String()` 方法，如果是就用于产生输出信息。我们可以回到 [11.10](11.10.md) 节查看这些细节。\n\n## 11.12.6 接口的继承\n\n当一个类型包含（内嵌）另一个类型（实现了一个或多个接口）的指针时，这个类型就可以使用（另一个类型）所有的接口方法。\n\n例如：\n\n```go\ntype Task struct {\n\tCommand string\n\t*log.Logger\n}\n```\n\n这个类型的工厂方法像这样：\n\n```go\nfunc NewTask(command string, logger *log.Logger) *Task {\n\treturn &Task{command, logger}\n}\n```\n\n当 `log.Logger` 实现了 `Log()` 方法后，`Task` 的实例 `task` 就可以调用该方法：\n\n```go\ntask.Log()\n```\n\n类型可以通过继承多个接口来提供像*多重继承*一样的特性：\n\n```go\ntype ReaderWriter struct {\n\t*io.Reader\n\t*io.Writer\n}\n```\n\n上面概述的原理被应用于整个 Go 包，多态用得越多，代码就相对越少（参见 [12.8 节](12.8.md)）。这被认为是 Go 编程中的重要的最佳实践。\n\n有用的接口可以在开发的过程中被归纳出来。添加新接口非常容易，因为已有的类型不用变动（仅仅需要实现新接口的方法）。已有的函数可以扩展为使用接口类型的约束性参数：通常只有函数签名需要改变。对比基于类的 OO 类型的语言在这种情况下则需要适应整个类层次结构的变化。\n\n**练习 11.11**：[map_function_interface.go](exercises/chapter_11/map_function_interface.go)：\n\n在练习 7.13 中我们定义了一个 `map()` 函数来使用 `int` 切片 ([map_function.go](exercises/chapter_7/map_function.go))。\n\n通过空接口和类型断言，现在我们可以写一个可以应用于许多类型的*泛型*的 `map()` 函数，为 `int` 和 `string` 构建一个把 `int` 值加倍和将字符串值与其自身连接（译者注：即 `\"abc\"` 变成 `\"abcabc\"` ）的 `map()` 函数 `mapFunc()`。\n\n提示：为了可读性可以定义一个 `interface{}` 的别名，比如：`type obj interface{}`。\n\n**练习 11.12**：[map_function_interface_var.go](exercises/chapter_11/map_function_interface_var.go)：\n\n稍微改变练习 11.11，允许 `mapFunc()` 接收不定数量的 `items`。\n\n**练习 11.13**：[main_stack.go](exercises/chapter_11/main_stack.go)—[stack/stack_general.go](exercises/chapter_11/stack/stack_general.go)：\n\n在练习 10.16 和 10.17 中我们开发了一些栈结构类型。但是它们被限制为某种固定的内建类型。现在用一个元素类型是 `interface{}`（空接口）的切片开发一个通用的栈类型。\n\n实现下面的栈方法：\n\n```go\nLen() int\nIsEmpty() bool\nPush(x interface{})\nPop() (interface{}, error)\n```\n\n`Pop()` 改变栈并返回最顶部的元素；`Top()` 只返回最顶部元素。\n\n在主程序中构建一个充满不同类型元素的栈，然后弹出并打印所有元素的值。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[Printf 和反射](11.11.md)\n- 下一节：[总结：Go 中的面向对象](11.13.md)\n"
  },
  {
    "path": "eBook/11.13.md",
    "content": "# 11.13 总结：Go 中的面向对象\n\n我们总结一下前面看到的：Go 没有类，而是松耦合的类型、方法对接口的实现。\n\nOO 语言最重要的三个方面分别是：封装、继承和多态，在 Go 中它们是怎样表现的呢？\n\n- 封装（数据隐藏）：和别的 OO 语言有 4 个或更多的访问层次相比，Go 把它简化为了 2 层（参见 [4.2 节](04.2.md)的可见性规则）:\n\n\t1）包范围内的：通过标识符首字母小写，*对象*只在它所在的包内可见\n\n\t2）可导出的：通过标识符首字母大写，*对象*对所在包以外也可见\n\n类型只拥有自己所在包中定义的方法。\n\n- 继承：用组合实现：内嵌一个（或多个）包含想要的行为（字段和方法）的类型；多重继承可以通过内嵌多个类型实现\n- 多态：用接口实现：某个类型的实例可以赋给它所实现的任意接口类型的变量。类型和接口是松耦合的，并且多重继承可以通过实现多个接口实现。Go 接口不是 Java 和 C# 接口的变体，而且接口间是不相关的，并且是大规模编程和可适应的演进型设计的关键。\n\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[接口与动态类型](11.12.md)\n- 下一节：[结构体，集合和高阶函数](11.14.md)\n"
  },
  {
    "path": "eBook/11.14.md",
    "content": "# 11.14 结构体、集合和高阶函数\n\n通常你在应用中定义了一个结构体，那么你也可能需要这个结构体的（指针）对象集合，比如：\n\n```go\ntype Any interface{}\ntype Car struct {\n\tModel        string\n\tManufacturer string\n\tBuildYear    int\n\t// ...\n}\n\ntype Cars []*Car\n```\n\n然后我们就可以使用高阶函数，实际上也就是把函数作为定义所需方法（其他函数）的参数，例如：\n\n1）定义一个通用的 `Process()` 函数，它接收一个作用于每一辆 car 的 f 函数作参数：\n\n```go\n// Process all cars with the given function f:\nfunc (cs Cars) Process(f func(car *Car)) {\n\tfor _, c := range cs {\n\t\tf(c)\n\t}\n}\n```\n\n2）在上面的基础上，实现一个查找函数来获取子集合，并在 `Process()` 中传入一个闭包执行（这样就可以访问局部切片 `cars`）：\n\n```go\n// Find all cars matching a given criteria.\nfunc (cs Cars) FindAll(f func(car *Car) bool) Cars {\n\n\tcars := make([]*Car, 0)\n\tcs.Process(func(c *Car) {\n\t\tif f(c) {\n\t\t\tcars = append(cars, c)\n\t\t}\n\t})\n\treturn cars\n}\n```\n\n3）实现对应作用的功效 (Map-functionality)，从每个 `car` 对象当中产出某些东西：\n\n```go\n// Process cars and create new data.\nfunc (cs Cars) Map(f func(car *Car) Any) []Any {\n\tresult := make([]Any, 0)\n\tix := 0\n\tcs.Process(func(c *Car) {\n\t\tresult[ix] = f(c)\n\t\tix++\n\t})\n\treturn result\n}\n```\n\n现在我们可以定义下面这样的具体查询：\n\n```go\nallNewBMWs := allCars.FindAll(func(car *Car) bool {\n\treturn (car.Manufacturer == \"BMW\") && (car.BuildYear > 2010)\n})\n```\n\n4）我们也可以根据参数返回不同的函数。也许我们想根据不同的厂商添加汽车到不同的集合，但是这（这种映射关系）可能会是会改变的。所以我们可以定义一个函数来产生特定的添加函数和 `map` 集：\n\n```go\nfunc MakeSortedAppender(manufacturers []string)(func(car *Car),map[string]Cars) {\n\t// Prepare maps of sorted cars.\n\tsortedCars := make(map[string]Cars)\n\tfor _, m := range manufacturers {\n\t\tsortedCars[m] = make([]*Car, 0)\n\t}\n\tsortedCars[\"Default\"] = make([]*Car, 0)\n\t// Prepare appender function:\n\tappender := func(c *Car) {\n\t\tif _, ok := sortedCars[c.Manufacturer]; ok {\n\t\t\tsortedCars[c.Manufacturer] = append(sortedCars[c.Manufacturer], c)\n\t\t} else {\n\t\t\tsortedCars[\"Default\"] = append(sortedCars[\"Default\"], c)\n\t\t}\n\n\t}\n\treturn appender, sortedCars\n}\n```\n\n现在我们可以用它把汽车分类为独立的集合，像这样：\n\n```go\nmanufacturers := []string{\"Ford\", \"Aston Martin\", \"Land Rover\", \"BMW\", \"Jaguar\"}\nsortedAppender, sortedCars := MakeSortedAppender(manufacturers)\nallUnsortedCars.Process(sortedAppender)\nBMWCount := len(sortedCars[\"BMW\"])\n```\n\n我们让这些代码在下面的程序 cars.go 中执行：\n\n示例 11.18 [cars.go](examples/chapter_11/cars.go)：\n\n```go\n// cars.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\ntype Any interface{}\ntype Car struct {\n\tModel        string\n\tManufacturer string\n\tBuildYear    int\n\t// ...\n}\ntype Cars []*Car\n\nfunc main() {\n\t// make some cars:\n\tford := &Car{\"Fiesta\", \"Ford\", 2008}\n\tbmw := &Car{\"XL 450\", \"BMW\", 2011}\n\tmerc := &Car{\"D600\", \"Mercedes\", 2009}\n\tbmw2 := &Car{\"X 800\", \"BMW\", 2008}\n\t// query:\n\tallCars := Cars([]*Car{ford, bmw, merc, bmw2})\n\tallNewBMWs := allCars.FindAll(func(car *Car) bool {\n\t\treturn (car.Manufacturer == \"BMW\") && (car.BuildYear > 2010)\n\t})\n\tfmt.Println(\"AllCars: \", allCars)\n\tfmt.Println(\"New BMWs: \", allNewBMWs)\n\t//\n\tmanufacturers := []string{\"Ford\", \"Aston Martin\", \"Land Rover\", \"BMW\", \"Jaguar\"}\n\tsortedAppender, sortedCars := MakeSortedAppender(manufacturers)\n\tallCars.Process(sortedAppender)\n\tfmt.Println(\"Map sortedCars: \", sortedCars)\n\tBMWCount := len(sortedCars[\"BMW\"])\n\tfmt.Println(\"We have \", BMWCount, \" BMWs\")\n}\n\n// Process all cars with the given function f:\nfunc (cs Cars) Process(f func(car *Car)) {\n\tfor _, c := range cs {\n\t\tf(c)\n\t}\n}\n\n// Find all cars matching a given criteria.\nfunc (cs Cars) FindAll(f func(car *Car) bool) Cars {\n\tcars := make([]*Car, 0)\n\n\tcs.Process(func(c *Car) {\n\t\tif f(c) {\n\t\t\tcars = append(cars, c)\n\t\t}\n\t})\n\treturn cars\n}\n\n// Process cars and create new data.\nfunc (cs Cars) Map(f func(car *Car) Any) []Any {\n\tresult := make([]Any, len(cs))\n\tix := 0\n\tcs.Process(func(c *Car) {\n\t\tresult[ix] = f(c)\n\t\tix++\n\t})\n\treturn result\n}\n\nfunc MakeSortedAppender(manufacturers []string) (func(car *Car), map[string]Cars) {\n\t// Prepare maps of sorted cars.\n\tsortedCars := make(map[string]Cars)\n\n\tfor _, m := range manufacturers {\n\t\tsortedCars[m] = make([]*Car, 0)\n\t}\n\tsortedCars[\"Default\"] = make([]*Car, 0)\n\n\t// Prepare appender function:\n\tappender := func(c *Car) {\n\t\tif _, ok := sortedCars[c.Manufacturer]; ok {\n\t\t\tsortedCars[c.Manufacturer] = append(sortedCars[c.Manufacturer], c)\n\t\t} else {\n\t\t\tsortedCars[\"Default\"] = append(sortedCars[\"Default\"], c)\n\t\t}\n\t}\n\treturn appender, sortedCars\n}\n```\n\n输出：\n\n```\nAllCars:  [0xf8400038a0 0xf840003bd0 0xf840003ba0 0xf840003b70]\nNew BMWs:  [0xf840003bd0]\nMap sortedCars:  map[Default:[0xf840003ba0] Jaguar:[] Land Rover:[] BMW:[0xf840003bd0 0xf840003b70] Aston Martin:[] Ford:[0xf8400038a0]]\nWe have  2  BMWs\n```\n\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[Go 中的面向对象](11.13.md)\n- 下一章：[读写数据](12.0.md)\n"
  },
  {
    "path": "eBook/11.2.md",
    "content": "# 11.2 接口嵌套接口\n\n一个接口可以包含一个或多个其他的接口，这相当于直接将这些内嵌接口的方法列举在外层接口中一样。\n\n比如接口 `File` 包含了 `ReadWrite` 和 `Lock` 的所有方法，它还额外有一个 `Close()` 方法。\n\n```go\ntype ReadWrite interface {\n    Read(b Buffer) bool\n    Write(b Buffer) bool\n}\n\ntype Lock interface {\n    Lock()\n    Unlock()\n}\n\ntype File interface {\n    ReadWrite\n    Lock\n    Close()\n}\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[接口是什么](11.1.md)\n- 下一节：[如何检测和转换接口变量的类型：类型断言](11.3.md)\n"
  },
  {
    "path": "eBook/11.3.md",
    "content": "# 11.3 类型断言：如何检测和转换接口变量的类型\n\n一个接口类型的变量 `varI` 中可以包含任何类型的值，必须有一种方式来检测它的 **动态** 类型，即运行时在变量中存储的值的实际类型。在执行过程中动态类型可能会有所不同，但是它总是可以分配给接口变量本身的类型。通常我们可以使用 **类型断言** 来测试在某个时刻 `varI` 是否包含类型 `T` 的值：\n\n```go\nv := varI.(T)       // unchecked type assertion\n```\n\n**`varI` 必须是一个接口变量**，否则编译器会报错：`invalid type assertion: varI.(T) (non-interface type (type of varI) on left)` 。\n\n类型断言可能是无效的，虽然编译器会尽力检查转换是否有效，但是它不可能预见所有的可能性。如果转换在程序运行时失败会导致错误发生。更安全的方式是使用以下形式来进行类型断言：\n\n```go\nif v, ok := varI.(T); ok {  // checked type assertion\n    Process(v)\n    return\n}\n// varI is not of type T\n```\n\n如果转换合法，`v` 是 `varI` 转换到类型 `T` 的值，`ok` 会是 `true`；否则 `v` 是类型 `T` 的零值，`ok` 是 `false`，也没有运行时错误发生。\n\n**应该总是使用上面的方式来进行类型断言**。\n\n多数情况下，我们可能只是想在 `if` 中测试一下 `ok` 的值，此时使用以下的方法会是最方便的：\n\n```go\nif _, ok := varI.(T); ok {\n    // ...\n}\n```\n\n示例 11.4 [type_interfaces.go](examples/chapter_11/type_interfaces.go)：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n)\n\ntype Square struct {\n\tside float32\n}\n\ntype Circle struct {\n\tradius float32\n}\n\ntype Shaper interface {\n\tArea() float32\n}\n\nfunc main() {\n\tvar areaIntf Shaper\n\tsq1 := new(Square)\n\tsq1.side = 5\n\n\tareaIntf = sq1\n\t// Is Square the type of areaIntf?\n\tif t, ok := areaIntf.(*Square); ok {\n\t\tfmt.Printf(\"The type of areaIntf is: %T\\n\", t)\n\t}\n\tif u, ok := areaIntf.(*Circle); ok {\n\t\tfmt.Printf(\"The type of areaIntf is: %T\\n\", u)\n\t} else {\n\t\tfmt.Println(\"areaIntf does not contain a variable of type Circle\")\n\t}\n}\n\nfunc (sq *Square) Area() float32 {\n\treturn sq.side * sq.side\n}\n\nfunc (ci *Circle) Area() float32 {\n\treturn ci.radius * ci.radius * math.Pi\n}\n```\n\n输出：\n\n    The type of areaIntf is: *main.Square\n    areaIntf does not contain a variable of type Circle\n\n程序中定义了一个新类型 `Circle`，它也实现了 `Shaper` 接口。 `if t, ok := areaIntf.(*Square); ok` 测试 `areaIntf` 里是否有一个包含 `*Square` 类型的变量，结果是确定的；然后我们测试它是否包含一个 `*Circle` 类型的变量，结果是否定的。\n\n**备注**\n\n如果忽略 `areaIntf.(*Square)` 中的 `*` 号，会导致编译错误：`impossible type assertion: Square does not implement Shaper (Area method has pointer receiver)`。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[接口嵌套接口](11.2.md)\n- 下一节：[类型判断：type-switch](11.4.md)\n"
  },
  {
    "path": "eBook/11.4.md",
    "content": "# 11.4 类型判断：type-switch\n\n接口变量的类型也可以使用一种特殊形式的 `switch` 来检测：**type-switch** （下面是示例 11.4 的第二部分）：\n\n```go\nswitch t := areaIntf.(type) {\ncase *Square:\n\tfmt.Printf(\"Type Square %T with value %v\\n\", t, t)\ncase *Circle:\n\tfmt.Printf(\"Type Circle %T with value %v\\n\", t, t)\ncase nil:\n\tfmt.Printf(\"nil value: nothing to check?\\n\")\ndefault:\n\tfmt.Printf(\"Unexpected type %T\\n\", t)\n}\n```\n\n输出：\n\n    Type Square *main.Square with value &{5}\n\n变量 `t` 得到了 `areaIntf` 的值和类型，所有 `case` 语句中列举的类型（`nil` 除外）都必须实现对应的接口（在上例中即 `Shaper`），如果被检测类型没有在 `case` 语句列举的类型中，就会执行 `default` 语句。\n\n可以用 `type-switch` 进行运行时类型分析，但是在 `type-switch` 不允许有 `fallthrough` 。\n\n如果仅仅是测试变量的类型，不用它的值，那么就可以不需要赋值语句，比如：\n\n```go\nswitch areaIntf.(type) {\ncase *Square:\n\t// TODO\ncase *Circle:\n\t// TODO\n...\ndefault:\n\t// TODO\n}\n```\n\n下面的代码片段展示了一个类型分类函数，它有一个可变长度参数，可以是任意类型的数组，它会根据数组元素的实际类型执行不同的动作：\n\n```go\nfunc classifier(items ...interface{}) {\n\tfor i, x := range items {\n\t\tswitch x.(type) {\n\t\tcase bool:\n\t\t\tfmt.Printf(\"Param #%d is a bool\\n\", i)\n\t\tcase float64:\n\t\t\tfmt.Printf(\"Param #%d is a float64\\n\", i)\n\t\tcase int, int64:\n\t\t\tfmt.Printf(\"Param #%d is a int\\n\", i)\n\t\tcase nil:\n\t\t\tfmt.Printf(\"Param #%d is a nil\\n\", i)\n\t\tcase string:\n\t\t\tfmt.Printf(\"Param #%d is a string\\n\", i)\n\t\tdefault:\n\t\t\tfmt.Printf(\"Param #%d is unknown\\n\", i)\n\t\t}\n\t}\n}\n```\n\n可以这样调用此方法：`classifier(13, -14.3, \"BELGIUM\", complex(1, 2), nil, false)` 。\n\n在处理来自于外部的、类型未知的数据时，比如解析诸如 JSON 或 XML 编码的数据，类型测试和转换会非常有用。\n\n在示例 12.17 ([xml.go](examples/chapter_12/xml.go)) 中解析 XML 文档时，我们就会用到 `type-switch`。\n\n**练习 11.4** [simple_interface2.go](exercises/chapter_11/simple_interface2.go)：\n\n接着练习 11.1 中的内容，创建第二个类型 `RSimple`，它也实现了接口 `Simpler`，写一个函数 `fi()`，使它可以区分 `Simple` 和 `RSimple` 类型的变量。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[类型断言：如何检测和转换接口变量的类型](11.3.md)\n- 下一节：[测试一个值是否实现了某个接口](11.5.md)\n"
  },
  {
    "path": "eBook/11.5.md",
    "content": "# 11.5 测试一个值是否实现了某个接口\n\n这是 11.3 类型断言中的一个特例：假定 `v` 是一个值，然后我们想测试它是否实现了 `Stringer` 接口，可以这样做：\n\n```go\ntype Stringer interface {\n    String() string\n}\n\nif sv, ok := v.(Stringer); ok {\n    fmt.Printf(\"v implements String(): %s\\n\", sv.String()) // note: sv, not v\n}\n```\n\n`Print()` 函数就是如此检测类型是否可以打印自身的。\n\n接口是一种契约，实现类型必须满足它，它描述了类型的行为，规定类型可以做什么。接口彻底将类型能做什么，以及如何做分离开来，使得相同接口的变量在不同的时刻表现出不同的行为，这就是多态的本质。\n\n编写参数是接口变量的函数，这使得它们更具有一般性。\n\n**使用接口使代码更具有普适性。**\n\n标准库里到处都使用了这个原则，如果对接口概念没有良好的把握，是不可能理解它是如何构建的。\n\n在接下来的章节中，我们会讨论两个重要的例子，试着去深入理解它们，这样你就可以更好的应用上面的原则。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[类型判断：type-switch](11.4.md)\n- 下一节：[使用方法集与接口](11.6.md)\n"
  },
  {
    "path": "eBook/11.6.md",
    "content": "# 11.6 使用方法集与接口\n\n在[第 10.6.3 节](10.6.md)及例子 [methodset1.go](examples\\chapter_10\\methodset1.go) 中我们看到，作用于变量上的方法实际上是不区分变量到底是指针还是值的。当碰到接口类型值时，这会变得有点复杂，原因是接口变量中存储的具体值是不可寻址的，幸运的是，如果使用不当编译器会给出错误。考虑下面的程序：\n\n示例 11.5 [methodset2.go](examples/chapter_11/methodset2.go)：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\ntype List []int\n\nfunc (l List) Len() int {\n\treturn len(l)\n}\n\nfunc (l *List) Append(val int) {\n\t*l = append(*l, val)\n}\n\ntype Appender interface {\n\tAppend(int)\n}\n\nfunc CountInto(a Appender, start, end int) {\n\tfor i := start; i <= end; i++ {\n\t\ta.Append(i)\n\t}\n}\n\ntype Lener interface {\n\tLen() int\n}\n\nfunc LongEnough(l Lener) bool {\n\treturn l.Len()*10 > 42\n}\n\nfunc main() {\n\t// A bare value\n\tvar lst List\n\t// compiler error:\n\t// cannot use lst (type List) as type Appender in argument to CountInto:\n\t//       List does not implement Appender (Append method has pointer receiver)\n\t// CountInto(lst, 1, 10)\n\tif LongEnough(lst) { // VALID: Identical receiver type\n\t\tfmt.Printf(\"- lst is long enough\\n\")\n\t}\n\n\t// A pointer value\n\tplst := new(List)\n\tCountInto(plst, 1, 10) // VALID: Identical receiver type\n\tif LongEnough(plst) {\n\t\t// VALID: a *List can be dereferenced for the receiver\n\t\tfmt.Printf(\"- plst is long enough\\n\")\n\t}\n}\n```\n\n**讨论**\n\n在 `lst` 上调用 `CountInto` 时会导致一个编译器错误，因为 `CountInto` 需要一个 `Appender`，而它的方法 `Append` 只定义在指针上。 在 `lst` 上调用 `LongEnough` 是可以的，因为 `Len` 定义在值上。\n\n在 `plst` 上调用 `CountInto` 是可以的，因为 `CountInto` 需要一个 `Appender`，并且它的方法 `Append` 定义在指针上。 在 `plst` 上调用 `LongEnough` 也是可以的，因为指针会被自动解引用。\n\n**总结**\n\n在接口上调用方法时，必须有和方法定义时相同的接收者类型或者是可以根据具体类型 `P` 直接辨识的：\n\n- 指针方法可以通过指针调用\n- 值方法可以通过值调用\n- 接收者是值的方法可以通过指针调用，因为指针会首先被解引用\n- 接收者是指针的方法不可以通过值调用，因为存储在接口中的值没有地址\n\n将一个值赋值给一个接口时，编译器会确保所有可能的接口方法都可以在此值上被调用，因此不正确的赋值在编译期就会失败。\n\n**译注**\n\nGo 语言规范定义了接口方法集的调用规则：\n\n- 类型 `*T` 的可调用方法集包含接受者为 `*T` 或 `T` 的所有方法集\n- 类型 `T` 的可调用方法集包含接受者为 `T` 的所有方法\n- 类型 `T` 的可调用方法集**不**包含接受者为 `*T` 的方法\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[测试一个值是否实现了某个接口](11.5.md)\n- 下一节：[第一个例子：使用 Sorter 接口排序](11.7.md)\n"
  },
  {
    "path": "eBook/11.7.md",
    "content": "# 11.7 第一个例子：使用 Sorter 接口排序\n\n一个很好的例子是来自标准库的 `sort` 包，要对一组数字或字符串排序，只需要实现三个方法：反映元素个数的 `Len()` 方法、比较第 `i` 和 `j` 个元素的 `Less(i, j)` 方法以及交换第 `i` 和 `j` 个元素的 `Swap(i, j)` 方法。\n\n排序函数的算法只会使用到这三个方法（可以使用任何排序算法来实现，此处我们使用冒泡排序）：\n\n```go\nfunc Sort(data Sorter) {\n    for pass := 1; pass < data.Len(); pass++ {\n        for i := 0;i < data.Len() - pass; i++ {\n            if data.Less(i+1, i) {\n                data.Swap(i, i + 1)\n            }\n        }\n    }\n}\n```\n\n`Sort` 函数接收一个接口类型的参数：`Sorter` ，它声明了这些方法：\n\n```go\ntype Sorter interface {\n    Len() int\n    Less(i, j int) bool\n    Swap(i, j int)\n}\n```\n\n参数中的 `int` 是待排序序列长度的类型，而不是说要排序的对象一定要是一组 `int`。`i` 和 `j` 表示元素的整型索引，长度也是整型的。\n\n现在如果我们想对一个 `int` 数组进行排序，所有必须做的事情就是：为数组定一个类型并在它上面实现 `Sorter` 接口的方法：\n\n```go\ntype IntArray []int\nfunc (p IntArray) Len() int           { return len(p) }\nfunc (p IntArray) Less(i, j int) bool { return p[i] < p[j] }\nfunc (p IntArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }\n```\n\n下面是调用排序函数的一个具体例子：\n\n```go\ndata := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}\na := sort.IntArray(data) //conversion to type IntArray from package sort\nsort.Sort(a)\n```\n\n完整的、可运行的代码可以在 [sort.go](examples/chapter_11/sort/sort.go) 和 [sortmain.go](examples/chapter_11/sortmain.go) 里找到。\n\n同样的原理，排序函数可以用于一个浮点型数组，一个字符串数组，或者一个表示每周各天的结构体 `dayArray`。\n\n示例 11.6 [sort.go](examples/chapter_11/sort/sort.go)：\n\n```go\npackage sort\n\ntype Sorter interface {\n\tLen() int\n\tLess(i, j int) bool\n\tSwap(i, j int)\n}\n\nfunc Sort(data Sorter) {\n\tfor pass := 1; pass < data.Len(); pass++ {\n\t\tfor i := 0; i < data.Len()-pass; i++ {\n\t\t\tif data.Less(i+1, i) {\n\t\t\t\tdata.Swap(i, i+1)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc IsSorted(data Sorter) bool {\n\tn := data.Len()\n\tfor i := n - 1; i > 0; i-- {\n\t\tif data.Less(i, i-1) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// Convenience types for common cases\ntype IntArray []int\n\nfunc (p IntArray) Len() int           { return len(p) }\nfunc (p IntArray) Less(i, j int) bool { return p[i] < p[j] }\nfunc (p IntArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }\n\ntype StringArray []string\n\nfunc (p StringArray) Len() int           { return len(p) }\nfunc (p StringArray) Less(i, j int) bool { return p[i] < p[j] }\nfunc (p StringArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }\n\n// Convenience wrappers for common cases\nfunc SortInts(a []int)       { Sort(IntArray(a)) }\nfunc SortStrings(a []string) { Sort(StringArray(a)) }\n\nfunc IntsAreSorted(a []int) bool       { return IsSorted(IntArray(a)) }\nfunc StringsAreSorted(a []string) bool { return IsSorted(StringArray(a)) }\n```\n\n示例 11.7 [sortmain.go](examples/chapter_11/sortmain.go)：\n\n```go\npackage main\n\nimport (\n\t\"./sort\"\n\t\"fmt\"\n)\n\nfunc ints() {\n\tdata := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}\n\ta := sort.IntArray(data) //conversion to type IntArray\n\tsort.Sort(a)\n\tif !sort.IsSorted(a) {\n\t\tpanic(\"fails\")\n\t}\n\tfmt.Printf(\"The sorted array is: %v\\n\", a)\n}\n\nfunc strings() {\n\tdata := []string{\"monday\", \"friday\", \"tuesday\", \"wednesday\", \"sunday\", \"thursday\", \"\", \"saturday\"}\n\ta := sort.StringArray(data)\n\tsort.Sort(a)\n\tif !sort.IsSorted(a) {\n\t\tpanic(\"fail\")\n\t}\n\tfmt.Printf(\"The sorted array is: %v\\n\", a)\n}\n\ntype day struct {\n\tnum       int\n\tshortName string\n\tlongName  string\n}\n\ntype dayArray struct {\n\tdata []*day\n}\n\nfunc (p *dayArray) Len() int           { return len(p.data) }\nfunc (p *dayArray) Less(i, j int) bool { return p.data[i].num < p.data[j].num }\nfunc (p *dayArray) Swap(i, j int)      { p.data[i], p.data[j] = p.data[j], p.data[i] }\n\nfunc days() {\n\tSunday    := day{0, \"SUN\", \"Sunday\"}\n\tMonday    := day{1, \"MON\", \"Monday\"}\n\tTuesday   := day{2, \"TUE\", \"Tuesday\"}\n\tWednesday := day{3, \"WED\", \"Wednesday\"}\n\tThursday  := day{4, \"THU\", \"Thursday\"}\n\tFriday    := day{5, \"FRI\", \"Friday\"}\n\tSaturday  := day{6, \"SAT\", \"Saturday\"}\n\tdata := []*day{&Tuesday, &Thursday, &Wednesday, &Sunday, &Monday, &Friday, &Saturday}\n\ta := dayArray{data}\n\tsort.Sort(&a)\n\tif !sort.IsSorted(&a) {\n\t\tpanic(\"fail\")\n\t}\n\tfor _, d := range data {\n\t\tfmt.Printf(\"%s \", d.longName)\n\t}\n\tfmt.Printf(\"\\n\")\n}\n\nfunc main() {\n\tints()\n\tstrings()\n\tdays()\n}\n```\n\n输出：\n\n    The sorted array is: [-5467984 -784 0 0 42 59 74 238 905 959 7586 7586 9845]\n    The sorted array is: [ friday monday saturday sunday thursday tuesday wednesday]\n    Sunday Monday Tuesday Wednesday Thursday Friday Saturday \n\n**备注**：\n\n`panic(\"fail\")` 用于停止处于在非正常情况下的程序（详细请参考[第 13 章](13.0.md)），当然也可以先打印一条信息，然后调用 `os.Exit(1)` 来停止程序。\n\n上面的例子帮助我们进一步了解了接口的意义和使用方式。对于基本类型的排序，标准库已经提供了相关的排序函数，所以不需要我们再重复造轮子了。对于一般性的排序，`sort` 包定义了一个接口：\n\n```go\ntype Interface interface {\n\tLen() int\n\tLess(i, j int) bool\n\tSwap(i, j int)\n}\n```\n\n这个接口总结了需要用于排序的抽象方法，函数 `Sort(data Interface)` 用来对此类对象进行排序，可以用它们来实现对其他类型的数据（非基本类型）进行排序。在上面的例子中，我们也是这么做的，不仅可以对 `int` 和 `string` 序列进行排序，也可以对用户自定义类型 `dayArray` 进行排序。\n\n**练习 11.5** [interfaces_ext.go](exercises/chapter_11/interfaces_ext.go)：\n\na). 继续扩展程序，定义类型 `Triangle`，让它实现 `AreaInterface` 接口。通过计算一个特定三角形的面积来进行测试（三角形面积=0.5 * (底 * 高)）\n\nb). 定义一个新接口 `PeriInterface`，它有一个 `Perimeter` 方法。让 `Square` 实现这个接口，并通过一个 `Square` 示例来测试它。\n\n**练习 11.6** [point_interfaces.go](exercises/chapter_11/point_interfaces.go)：\n\n继续 [10.3](10.3.md) 中的练习 [point_methods.go](exercises/chapter_10/point_methods.go)，定义接口 `Magnitude`，它有一个方法 `Abs()`。让 `Point`、`Point3` 及 `Polar` 实现此接口。通过接口类型变量使用方法做 point.go 中同样的事情。\n\n**练习 11.7** [float_sort.go](exercises/chapter_11/float_sort.go) / [float_sortmain.go](exercises/chapter_11/float_sortmain.go)：\n\n类似 11.7 和示例 11.3/4，定义一个包 `float64`，并在包里定义类型 `Float64Array`，然后让它实现 `Sorter` 接口用来对 `float64` 数组进行排序。\n\n另外提供如下方法：\n\n- `NewFloat64Array()`：创建一个包含 25 个元素的数组变量（参考 [10.2](10.2.md) ）\n- `List()`：返回数组格式化后的字符串，并在 `String()` 方法中调用它，这样就不用显式地调用 `List()` 来打印数组（参考 [10.7](10.7.md)）\n- `Fill()`：创建一个包含 10 个随机浮点数的数组（参考 [4.5.2.6](04.5.md)）\n\n在主程序中新建一个此类型的变量，然后对它排序并进行测试。\n\n**练习 11.8** [sort.go](exercises/chapter_11/sort/sort.go) / [sort_persons.go](exercises/chapter_11/sort_persons.go)：\n\n定义一个结构体 `Person`，它有两个字段：`firstName` 和 `lastName`，为 `[]Person` 定义类型 `Persons` 。让 `Persons` 实现 `Sorter` 接口并进行测试。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[使用方法集与接口](11.6.md)\n- 下一节：[第二个例子：读和写](11.8.md)\n"
  },
  {
    "path": "eBook/11.8.md",
    "content": "# 11.8 第二个例子：读和写\n\n读和写是软件中很普遍的行为，提起它们会立即想到读写文件、缓存（比如字节或字符串切片）、标准输入输出、标准错误以及网络连接、管道等等，或者读写我们的自定义类型。为了让代码尽可能通用，Go 采取了一致的方式来读写数据。\n\n`io` 包提供了用于读和写的接口 `io.Reader` 和 `io.Writer`：\n\n```go\ntype Reader interface {\n    Read(p []byte) (n int, err error)\n}\n\ntype Writer interface {\n    Write(p []byte) (n int, err error)\n}\n```\n\n只要类型实现了读写接口，提供 `Read` 和 `Write` 方法，就可以从它读取数据，或向它写入数据。一个对象要是可读的，它必须实现 `io.Reader` 接口，这个接口只有一个签名是 `Read(p []byte) (n int, err error)` 的方法，它从调用它的对象上读取数据，并把读到的数据放入参数中的字节切片中，然后返回读取的字节数和一个 `error` 对象，如果没有错误发生返回 `nil`，如果已经到达输入的尾端，会返回 `io.EOF(\"EOF\")`，如果读取的过程中发生了错误，就会返回具体的错误信息。类似地，一个对象要是可写的，它必须实现 `io.Writer` 接口，这个接口也只有一个签名是 `Write(p []byte) (n int, err error)` 的方法，它将指定字节切片中的数据写入调用它的对象里，然后返回实际写入的字节数和一个 `error` 对象（如果没有错误发生就是 `nil`）。\n\n`io` 包里的 `Readers` 和 `Writers` 都是不带缓冲的，`bufio` 包里提供了对应的带缓冲的操作，在读写 `UTF-8` 编码的文本文件时它们尤其有用。在[第 12 章](12.0.md)我们会看到很多在实战中使用它们的例子。\n\n在实际编程中尽可能的使用这些接口，会使程序变得更通用，可以在任何实现了这些接口的类型上使用读写方法。\n\n例如一个 `JPEG` 图形解码器，通过一个 `Reader` 参数，它可以解码来自磁盘、网络连接或以 `gzip` 压缩的 `HTTP` 流中的 `JPEG` 图形数据，或者其他任何实现了 `Reader` 接口的对象。 \n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[第一个例子：使用Sorter接口排序](11.7.md)\n- 下一节：[空接口](11.9.md)\n"
  },
  {
    "path": "eBook/11.9.md",
    "content": "# 11.9 空接口\r\n\r\n## 11.9.1 概念\r\n\r\n**空接口或者最小接口** 不包含任何方法，它对实现不做任何要求：\r\n\r\n```go\r\ntype Any interface {}\r\n```\r\n\r\n任何其他类型都实现了空接口（它不仅仅像 `Java/C#` 中 `Object` 引用类型），`any` 或 `Any` 是空接口一个很好的别名或缩写。\r\n\r\n空接口类似 `Java/C#` 中所有类的基类： `Object` 类，二者的目标也很相近。\r\n\r\n可以给一个空接口类型的变量 `var val interface {}` 赋任何类型的值。\r\n\r\n示例 11.8 [empty_interface.go](examples/chapter_11/empty_interface.go)：\r\n\r\n```go\r\npackage main\r\nimport \"fmt\"\r\n\r\nvar i = 5\r\nvar str = \"ABC\"\r\n\r\ntype Person struct {\r\n\tname string\r\n\tage  int\r\n}\r\n\r\ntype Any interface{}\r\n\r\nfunc main() {\r\n\tvar val Any\r\n\tval = 5\r\n\tfmt.Printf(\"val has the value: %v\\n\", val)\r\n\tval = str\r\n\tfmt.Printf(\"val has the value: %v\\n\", val)\r\n\tpers1 := new(Person)\r\n\tpers1.name = \"Rob Pike\"\r\n\tpers1.age = 55\r\n\tval = pers1\r\n\tfmt.Printf(\"val has the value: %v\\n\", val)\r\n\tswitch t := val.(type) {\r\n\tcase int:\r\n\t\tfmt.Printf(\"Type int %T\\n\", t)\r\n\tcase string:\r\n\t\tfmt.Printf(\"Type string %T\\n\", t)\r\n\tcase bool:\r\n\t\tfmt.Printf(\"Type boolean %T\\n\", t)\r\n\tcase *Person:\r\n\t\tfmt.Printf(\"Type pointer to Person %T\\n\", t)\r\n\tdefault:\r\n\t\tfmt.Printf(\"Unexpected type %T\", t)\r\n\t}\r\n}\r\n```\r\n\r\n输出：\r\n\r\n    val has the value: 5\r\n    val has the value: ABC\r\n    val has the value: &{Rob Pike 55}\r\n    Type pointer to Person *main.Person\r\n\r\n在上面的例子中，接口变量 `val` 被依次赋予一个 `int`，`string` 和 `Person` 实例的值，然后使用 `type-switch` 来测试它的实际类型。每个 `interface {}` 变量在内存中占据两个字长：一个用来存储它包含的类型，另一个用来存储它包含的数据或者指向数据的指针。\r\n\r\n示例 [emptyint_switch.go](examples/chapter_11/emptyint_switch.go) 说明了空接口在 `type-switch` 中联合 `lambda` 函数的用法：\r\n\r\n```go\r\npackage main\r\n\r\nimport \"fmt\"\r\n\r\ntype specialString string\r\n\r\nvar whatIsThis specialString = \"hello\"\r\n\r\nfunc TypeSwitch() {\r\n\ttestFunc := func(any interface{}) {\r\n\t\tswitch v := any.(type) {\r\n\t\tcase bool:\r\n\t\t\tfmt.Printf(\"any %v is a bool type\", v)\r\n\t\tcase int:\r\n\t\t\tfmt.Printf(\"any %v is an int type\", v)\r\n\t\tcase float32:\r\n\t\t\tfmt.Printf(\"any %v is a float32 type\", v)\r\n\t\tcase string:\r\n\t\t\tfmt.Printf(\"any %v is a string type\", v)\r\n\t\tcase specialString:\r\n\t\t\tfmt.Printf(\"any %v is a special String!\", v)\r\n\t\tdefault:\r\n\t\t\tfmt.Println(\"unknown type!\")\r\n\t\t}\r\n\t}\r\n\ttestFunc(whatIsThis)\r\n}\r\n\r\nfunc main() {\r\n\tTypeSwitch()\r\n}\r\n```\r\n\r\n输出：\r\n\r\n    any hello is a special String!\r\n\r\n**练习 11.9** [simple_interface3.go](exercises/chapter_11/simple_interface3.go)：\r\n\r\n继续练习 11.2，在它中添加一个 `gI()` 函数，它不再接受 `Simpler` 类型的参数，而是接受一个空接口参数。然后通过类型断言判断参数是否是 `Simpler` 类型。最后在 `main` 使用 `gI()` 取代 `fI()` 函数并调用它。确保你的代码足够安全。\r\n\r\n## 11.9.2 构建通用类型或包含不同类型变量的数组\r\n\r\n在 [7.6.6](07.6.md) 中我们看到了能被搜索和排序的 `int` 数组、`float` 数组以及 `string` 数组，那么对于其他类型的数组呢，是不是我们必须得自己编程实现它们？\r\n\r\n现在我们知道该怎么做了，就是通过使用空接口。让我们给空接口定一个别名类型 `Element`：`type Element interface{}`\r\n\r\n然后定义一个容器类型的结构体 `Vector`，它包含一个 `Element` 类型元素的切片：\r\n\r\n```go\r\ntype Vector struct {\r\n\ta []Element\r\n}\r\n```\r\n\r\n`Vector` 里能放任何类型的变量，因为任何类型都实现了空接口，实际上 `Vector` 里放的每个元素可以是不同类型的变量。我们为它定义一个 `At()` 方法用于返回第 `i` 个元素：\r\n\r\n```go\r\nfunc (p *Vector) At(i int) Element {\r\n\treturn p.a[i]\r\n}\r\n```\r\n\r\n再定一个 `Set()` 方法用于设置第 `i` 个元素的值：\r\n\r\n```go\r\nfunc (p *Vector) Set(i int, e Element) {\r\n\tp.a[i] = e\r\n}\r\n```\r\n\r\n`Vector` 中存储的所有元素都是 `Element` 类型，要得到它们的原始类型（unboxing：拆箱）需要用到类型断言。TODO：The compiler rejects assertions guaranteed to fail，类型断言总是在运行时才执行，因此它会产生运行时错误。\r\n\r\n**练习 11.10** [min_interface.go](exercises/chapter_11/min_interface.go) / [minmain.go](exercises/chapter_11/minmain.go)：\r\n\r\n仿照 11.7 中开发的 `Sorter` 接口，创建一个 `Miner` 接口并实现一些必要的操作。函数 `Min()` 接受一个 `Miner` 类型变量的集合，然后计算并返回集合中最小的元素。\r\n\r\n## 11.9.3 复制数据切片至空接口切片\r\n\r\n假设你有一个 `myType` 类型的数据切片，你想将切片中的数据复制到一个空接口切片中，类似：\r\n\r\n```go\r\nvar dataSlice []myType = FuncReturnSlice()\r\nvar interfaceSlice []interface{} = dataSlice\r\n```\r\n\r\n可惜不能这么做，编译时会出错：`cannot use dataSlice (type []myType) as type []interface { } in assignment`。\r\n\r\n原因是它们俩在内存中的布局是不一样的（参考 [Go wiki](https://github.com/golang/go/wiki/InterfaceSlice)）。\r\n\r\n必须使用 `for-range` 语句来一个一个显式地赋值：\r\n\r\n```go\r\nvar dataSlice []myType = FuncReturnSlice()\r\nvar interfaceSlice []interface{} = make([]interface{}, len(dataSlice))\r\nfor i, d := range dataSlice {\r\n    interfaceSlice[i] = d\r\n}\r\n```\r\n\r\n## 11.9.4 通用类型的节点数据结构\r\n\r\n在 [10.1](10.1.md) 中我们遇到了诸如列表和树这样的数据结构，在它们的定义中使用了一种叫节点的递归结构体类型，节点包含一个某种类型的数据字段。现在可以使用空接口作为数据字段的类型，这样我们就能写出通用的代码。下面是实现一个二叉树的部分代码：通用定义、用于创建空节点的 `NewNode` 方法，及设置数据的 `SetData` 方法。\r\n\r\n示例 11.10 [node_structures.go](examples/chapter_11/node_structures.go)：\r\n\r\n```go\r\npackage main\r\n\r\nimport \"fmt\"\r\n\r\ntype Node struct {\r\n\tle   *Node\r\n\tdata interface{}\r\n\tri   *Node\r\n}\r\n\r\nfunc NewNode(left, right *Node) *Node {\r\n\treturn &Node{left, nil, right}\r\n}\r\n\r\nfunc (n *Node) SetData(data interface{}) {\r\n\tn.data = data\r\n}\r\n\r\nfunc main() {\r\n\troot := NewNode(nil, nil)\r\n\troot.SetData(\"root node\")\r\n\t// make child (leaf) nodes:\r\n\ta := NewNode(nil, nil)\r\n\ta.SetData(\"left node\")\r\n\tb := NewNode(nil, nil)\r\n\tb.SetData(\"right node\")\r\n\troot.le = a\r\n\troot.ri = b\r\n\tfmt.Printf(\"%v\\n\", root) // Output: &{0x125275f0 root node 0x125275e0}\r\n}\r\n```\r\n\r\n## 11.9.5 接口到接口\r\n\r\n一个接口的值可以赋值给另一个接口变量，只要底层类型实现了必要的方法。这个转换是在运行时进行检查的，转换失败会导致一个运行时错误：这是 `Go` 语言动态的一面，可以拿它和 `Ruby` 和 `Python` 这些动态语言相比较。\r\n\r\n假定：\r\n\r\n```go\r\nvar ai AbsInterface // declares method Abs()\r\ntype SqrInterface interface {\r\n    Sqr() float\r\n}\r\nvar si SqrInterface\r\npp := new(Point) // say *Point implements Abs, Sqr\r\nvar empty interface{}\r\n```\r\n\r\n那么下面的语句和类型断言是合法的：\r\n\r\n```go\r\nempty = pp                // everything satisfies empty\r\nai = empty.(AbsInterface) // underlying value pp implements Abs()\r\n// (runtime failure otherwise)\r\nsi = ai.(SqrInterface) // *Point has Sqr() even though AbsInterface doesn’t\r\nempty = si             // *Point implements empty set\r\n// Note: statically checkable so type assertion not necessary.\r\n```\r\n\r\n下面是函数调用的一个例子：\r\n\r\n```go\r\ntype myPrintInterface interface {\r\n\tprint()\r\n}\r\n\r\nfunc f3(x myInterface) {\r\n\tx.(myPrintInterface).print() // type assertion to myPrintInterface\r\n}\r\n```\r\n\r\n`x` 转换为 `myPrintInterface` 类型是完全动态的：只要 `x` 的底层类型（动态类型）定义了 `print` 方法这个调用就可以正常运行（译注：若 `x` 的底层类型未定义 `print` 方法，此处类型断言会导致 `panic`，最佳实践应该为 `if mpi, ok := x.(myPrintInterface); ok { mpi.print() }`，参考 [11.3](11.3.md) 章节）。\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[第二个例子：读和写](11.8.md)\r\n- 下一节：[反射包](11.10.md)\r\n"
  },
  {
    "path": "eBook/12.0.md",
    "content": "# 12.0 读写数据\n\n除了 `fmt` 和 `os` 包，我们还需要用到 `bufio` 包来处理缓冲的输入和输出。\n\n## 链接\n\n- [目录](directory.md)\n- 上一章：[结构体、集合和高阶函数](11.14.md)\n- 下一节：[读取用户的输入](12.1.md)\n"
  },
  {
    "path": "eBook/12.1.md",
    "content": "# 12.1 读取用户的输入\n\n我们如何读取用户的键盘（控制台）输入呢？从键盘和标准输入 `os.Stdin` 读取输入，最简单的办法是使用 `fmt` 包提供的 `Scan...` 和 `Sscan...` 开头的函数。请看以下程序：\n\n示例 12.1 [readinput1.go](examples/chapter_12/readinput1.go)：\n\n```go\n// 从控制台读取输入:\npackage main\nimport \"fmt\"\n\nvar (\n   firstName, lastName, s string\n   i int\n   f float32\n   input = \"56.12 / 5212 / Go\"\n   format = \"%f / %d / %s\"\n)\n\nfunc main() {\n   fmt.Println(\"Please enter your full name: \")\n   fmt.Scanln(&firstName, &lastName)\n   // fmt.Scanf(\"%s %s\", &firstName, &lastName)\n   fmt.Printf(\"Hi %s %s!\\n\", firstName, lastName) // Hi Chris Naegels\n   fmt.Sscanf(input, format, &f, &i, &s)\n   fmt.Println(\"From the string we read: \", f, i, s)\n    // 输出结果: From the string we read: 56.12 5212 Go\n}\n```\n\n`Scanln()` 扫描来自标准输入的文本，将空格分隔的值依次存放到后续的参数内，直到碰到换行。`Scanf()` 与其类似，除了 `Scanf()` 的第一个参数用作格式字符串，用来决定如何读取。`Sscan...` 和以 `Sscan...` 开头的函数则是从字符串读取，除此之外，与 `Scanf()` 相同。如果这些函数读取到的结果与您预想的不同，您可以检查成功读入数据的个数和返回的错误。\n\n您也可以使用 `bufio` 包提供的缓冲读取器 (buffered reader) 来读取数据，正如以下例子所示：\n\n示例 12.2 [readinput2.go](examples/chapter_12/readinput2.go)：\n\n```go\npackage main\nimport (\n    \"fmt\"\n    \"bufio\"\n    \"os\"\n)\n\nvar inputReader *bufio.Reader\nvar input string\nvar err error\n\nfunc main() {\n    inputReader = bufio.NewReader(os.Stdin)\n    fmt.Println(\"Please enter some input: \")\n    input, err = inputReader.ReadString('\\n')\n    if err == nil {\n        fmt.Printf(\"The input was: %s\\n\", input)\n    }\n}\n```\n\n`inputReader` 是一个指向 `bufio.Reader` 的指针。`inputReader := bufio.NewReader(os.Stdin)` 这行代码，将会创建一个读取器，并将其与标准输入绑定。\n\n`bufio.NewReader()` 构造函数的签名为：`func NewReader(rd io.Reader) *Reader`\n\n该函数的实参可以是满足 `io.Reader` 接口的任意对象（任意包含有适当的 `Read()` 方法的对象，请参考[章节 11.8](11.8.md)），函数返回一个新的带缓冲的 `io.Reader` 对象，它将从指定读取器（例如 `os.Stdin`）读取内容。\n\n返回的读取器对象提供一个方法 `ReadString(delim byte)`，该方法从输入中读取内容，直到碰到 `delim` 指定的字符，然后将读取到的内容连同 `delim` 字符一起放到缓冲区。\n\n`ReadString` 返回读取到的字符串，如果碰到错误则返回 `nil`。如果它一直读到文件结束，则返回读取到的字符串和 `io.EOF`。如果读取过程中没有碰到 `delim` 字符，将返回错误 `err != nil`。\n\n在上面的例子中，我们会读取键盘输入，直到回车键 (`\\n`) 被按下。\n\n屏幕是标准输出 `os.Stdout`；`os.Stderr` 用于显示错误信息，大多数情况下等同于 `os.Stdout`。\n\n一般情况下，我们会省略变量声明，而使用 `:=`，例如：\n\n```go\ninputReader := bufio.NewReader(os.Stdin)\ninput, err := inputReader.ReadString('\\n')\n```\n\n我们将从现在开始使用这种写法。\n\n第二个例子从键盘读取输入，使用了 `switch` 语句：\n\n示例 12.3 [switch_input.go](examples/chapter_12/switch_input.go)：\n\n```go\npackage main\nimport (\n    \"fmt\"\n    \"os\"\n    \"bufio\"\n)\n\nfunc main() {\n    inputReader := bufio.NewReader(os.Stdin)\n    fmt.Println(\"Please enter your name:\")\n    input, err := inputReader.ReadString('\\n')\n\n    if err != nil {\n        fmt.Println(\"There were errors reading, exiting program.\")\n        return\n    }\n\n    fmt.Printf(\"Your name is %s\", input)\n    // For Unix: test with delimiter \"\\n\", for Windows: test with \"\\r\\n\"\n    switch input {\n    case \"Philip\\r\\n\":  fmt.Println(\"Welcome Philip!\")\n    case \"Chris\\r\\n\":   fmt.Println(\"Welcome Chris!\")\n    case \"Ivo\\r\\n\":     fmt.Println(\"Welcome Ivo!\")\n    default: fmt.Printf(\"You are not welcome here! Goodbye!\")\n    }\n\n    // version 2:   \n    switch input {\n    case \"Philip\\r\\n\":  fallthrough\n    case \"Ivo\\r\\n\":     fallthrough\n    case \"Chris\\r\\n\":   fmt.Printf(\"Welcome %s\\n\", input)\n    default: fmt.Printf(\"You are not welcome here! Goodbye!\\n\")\n    }\n\n    // version 3:\n    switch input {\n    case \"Philip\\r\\n\", \"Ivo\\r\\n\":   fmt.Printf(\"Welcome %s\\n\", input)\n    default: fmt.Printf(\"You are not welcome here! Goodbye!\\n\")\n    }\n}\n```\n\n注意：Unix 和 Windows 的行结束符是不同的！\n\n**练习**\n\n**练习 12.1:** [word_letter_count.go](exercises/chapter_12/word_letter_count.go)\n\n编写一个程序，从键盘读取输入。当用户输入 'S' 的时候表示输入结束，这时程序输出 3 个数字：  \ni) 输入的字符的个数，包括空格，但不包括 `'\\r'` 和 `'\\n'`  \nii) 输入的单词的个数  \niii) 输入的行数\n\n**练习 12.2:** [calculator.go](exercises/chapter_12/calculator.go)\n\n编写一个简单的逆波兰式计算器，它接受用户输入的整型数（最大值 999999）和运算符 +、-、\\*、/。  \n输入的格式为：`number1 ENTER number2 ENTER operator ENTER --> 显示结果`  \n当用户输入字符 `'q'` 时，程序结束。请使用您在[练习 11.13](11.12.md) 中开发的 `stack` 包。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[读写数据](12.0.md)\n- 下一节：[文件读写](12.2.md)\n"
  },
  {
    "path": "eBook/12.10.md",
    "content": "# 12.10 XML 数据格式\n\n下面是与 [12.9 节](12.9.md) JSON 例子等价的 XML 版本：\n\n```xml\n<Person>\n    <FirstName>Laura</FirstName>\n    <LastName>Lynn</LastName>\n</Person>\n```\n\n如同 `json` 包一样，也有 `xml.Marshal()` 和 `xml.Unmarshal()` 从 XML 中编码和解码数据；但这个更通用，可以从文件中读取和写入（或者任何实现了 `io.Reader` 和 `io.Writer` 接口的类型）\n\n和 JSON 的方式一样，XML 数据可以序列化为结构，或者从结构反序列化为 XML 数据；这些可以在例子 15.8（[twitter_status.go](examples/chapter_15/twitter_status.go)）中看到。\n\n`encoding`/`xml` 包实现了一个简单的 XML 解析器（SAX），用来解析 XML 数据内容。下面的例子说明如何使用解析器：\n\n示例 12.17 [xml.go](examples/chapter_12/xml.go)：\n\n```go\n// xml.go\npackage main\n\nimport (\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"strings\"\n)\n\nvar t, token xml.Token\nvar err error\n\nfunc main() {\n\tinput := \"<Person><FirstName>Laura</FirstName><LastName>Lynn</LastName></Person>\"\n\tinputReader := strings.NewReader(input)\n\tp := xml.NewDecoder(inputReader)\n\n\tfor t, err = p.Token(); err == nil; t, err = p.Token() {\n\t\tswitch token := t.(type) {\n\t\tcase xml.StartElement:\n\t\t\tname := token.Name.Local\n\t\t\tfmt.Printf(\"Token name: %s\\n\", name)\n\t\t\tfor _, attr := range token.Attr {\n\t\t\t\tattrName := attr.Name.Local\n\t\t\t\tattrValue := attr.Value\n\t\t\t\tfmt.Printf(\"An attribute is: %s %s\\n\", attrName, attrValue)\n\t\t\t\t// ...\n\t\t\t}\n\t\tcase xml.EndElement:\n\t\t\tfmt.Println(\"End of token\")\n\t\tcase xml.CharData:\n\t\t\tcontent := string([]byte(token))\n\t\t\tfmt.Printf(\"This is the content: %v\\n\", content)\n\t\t\t// ...\n\t\tdefault:\n\t\t\t// ...\n\t\t}\n\t}\n}\n```\n\n输出：\n\n```\nToken name: Person\nToken name: FirstName\nThis is the content: Laura\nEnd of token\nToken name: LastName\nThis is the content: Lynn\nEnd of token\nEnd of token\n```\n\n包中定义了若干 XML 标签类型：StartElement，Chardata（这是从开始标签到结束标签之间的实际文本），EndElement，Comment，Directive 或 ProcInst。\n\n包中同样定义了一个结构解析器：`NewParser()` 方法持有一个 `io.Reader`（这里具体类型是 `strings.NewReader`）并生成一个解析器类型的对象。还有一个 `Token()` 方法返回输入流里的下一个 XML token。在输入流的结尾处，会返回 (`nil`,`io.EOF`)\n\nXML 文本被循环处理直到 `Token()` 返回一个错误，因为已经到达文件尾部，再没有内容可供处理了。通过一个 type-switch 可以根据一些 XML 标签进一步处理。Chardata 中的内容只是一个 `[]byte`，通过字符串转换让其变得可读性强一些。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[Json 数据格式](12.9.md)\n- 下一节：[用 Gob 传输数据](12.11.md)\n"
  },
  {
    "path": "eBook/12.11.md",
    "content": "# 12.11 用 Gob 传输数据\n\nGob 是 Go 自己的以二进制形式序列化和反序列化程序数据的格式；可以在 `encoding` 包中找到。这种格式的数据简称为 Gob （即 Go binary 的缩写）。类似于 Python 的 \"pickle\" 和 Java 的 \"Serialization\"。\n\nGob 通常用于远程方法调用（RPCs，参见 [15.9 节](15.9.md)的 `rpc` 包）参数和结果的传输，以及应用程序和机器之间的数据传输。\n它和 JSON 或 XML 有什么不同呢？Gob 特定地用于纯 Go 的环境中，例如，两个用 Go 写的服务之间的通信。这样的话服务可以被实现得更加高效和优化。\nGob 不是可外部定义，语言无关的编码方式。因此它的首选格式是二进制，而不是像 JSON 和 XML 那样的文本格式。\nGob 并不是一种不同于 Go 的语言，而是在编码和解码过程中用到了 Go 的反射。\n\nGob 文件或流是完全自描述的：里面包含的所有类型都有一个对应的描述，并且总是可以用 Go 解码，而不需要了解文件的内容。\n\n只有可导出的字段会被编码，零值会被忽略。在解码结构体的时候，只有同时匹配名称和可兼容类型的字段才会被解码。当源数据类型增加新字段后，Gob 解码客户端仍然可以以这种方式正常工作：解码客户端会继续识别以前存在的字段。并且还提供了很大的灵活性，比如在发送者看来，整数被编码成没有固定长度的可变长度，而忽略具体的 Go 类型。\n\n假如在发送者这边有一个有结构 T：\n\n```go\ntype T struct { X, Y, Z int }\nvar t = T{X: 7, Y: 0, Z: 8}\n```\n\n而在接收者这边可以用一个结构体 U 类型的变量 u 来接收这个值：\n\n```go\ntype U struct { X, Y *int8 }\nvar u U\n```\n\n在接收者中，`X` 的值是 `7`，`Y` 的值是 `0`（`Y` 的值并没有从 `t` 中传递过来，因为它是零值）\n\n\n和 JSON 的使用方式一样，Gob 使用通用的 `io.Writer` 接口，通过 `NewEncoder()` 函数创建 `Encoder` 对象并调用 `Encode()`；相反的过程使用通用的 `io.Reader` 接口，通过 `NewDecoder()` 函数创建 `Decoder` 对象并调用 `Decode()`。\n\n\n我们把示例 12.12 的信息写进名为 vcard.gob 的文件作为例子。这会产生一个文本可读数据和二进制数据的混合，当你试着在文本编辑中打开的时候会看到。\n\n在示例 12.18 中你会看到一个编解码，并且以字节缓冲模拟网络传输的简单例子：\n\n示例 12.18 [gob1.go](examples/chapter_12/gob1.go)：\n\n```go\n// gob1.go\npackage main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"encoding/gob\"\n\t\"log\"\n)\n\ntype P struct {\n\tX, Y, Z int\n\tName    string\n}\n\ntype Q struct {\n\tX, Y *int32\n\tName string\n}\n\nfunc main() {\n\t// Initialize the encoder and decoder.  Normally enc and dec would be      \n\t// bound to network connections and the encoder and decoder would      \n\t// run in different processes.      \n\tvar network bytes.Buffer   // Stand-in for a network connection      \n\tenc := gob.NewEncoder(&network) // Will write to network.      \n\tdec := gob.NewDecoder(&network)\t// Will read from network.      \n\t// Encode (send) the value.      \n\terr := enc.Encode(P{3, 4, 5, \"Pythagoras\"})\n\tif err != nil {\n\t\tlog.Fatal(\"encode error:\", err)\n\t}\n\t// Decode (receive) the value.      \n\tvar q Q\n\terr = dec.Decode(&q)\n\tif err != nil {\n\t\tlog.Fatal(\"decode error:\", err)\n\t}\n\tfmt.Printf(\"%q: {%d,%d}\\n\", q.Name, q.X, q.Y)\n}\n// Output:   \"Pythagoras\": {3,4}\n```\n\n示例 12.19 [gob2.go](examples/chapter_12/gob2.go) 编码到文件：\n\n```go\n// gob2.go\npackage main\n\nimport (\n\t\"encoding/gob\"\n\t\"log\"\n\t\"os\"\n)\n\ntype Address struct {\n\tType             string\n\tCity             string\n\tCountry          string\n}\n\ntype VCard struct {\n\tFirstName\tstring\n\tLastName\tstring\n\tAddresses\t[]*Address\n\tRemark\t\tstring\n}\n\nvar content\tstring\n\nfunc main() {\n\tpa := &Address{\"private\", \"Aartselaar\",\"Belgium\"}\n\twa := &Address{\"work\", \"Boom\", \"Belgium\"}\n\tvc := VCard{\"Jan\", \"Kersschot\", []*Address{pa,wa}, \"none\"}\n\t// fmt.Printf(\"%v: \\n\", vc) // {Jan Kersschot [0x126d2b80 0x126d2be0] none}:\n\t// using an encoder:\n\tfile, _ := os.OpenFile(\"vcard.gob\", os.O_CREATE|os.O_WRONLY, 0666)\n\tdefer file.Close()\n\tenc := gob.NewEncoder(file)\n\terr := enc.Encode(vc)\n\tif err != nil {\n\t\tlog.Println(\"Error in encoding gob\")\n\t}\n}\n```\n\n**练习 12.8**：[degob.go](exercises/chapter_12/degob.go)：\n\n写一个程序读取 vcard.gob 文件，解码并打印它的内容。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[XML 数据格式](12.10.md)\n- 下一节：[Go 中的密码学](12.12.md)\n"
  },
  {
    "path": "eBook/12.12.md",
    "content": "# 12.12 Go 中的密码学\n\n通过网络传输的数据必须加密，以防止被 hacker（黑客）读取或篡改，并且保证发出的数据和收到的数据检验和一致。\n鉴于 Go 母公司的业务，我们毫不惊讶地看到 Go 的标准库为该领域提供了超过 30 个的包：\n\n- `hash` 包：实现了 `adler32`、`crc32`、`crc64` 和 `fnv` 校验；\n- `crypto` 包：实现了其它的 hash 算法，比如 `md4`、`md5`、`sha1` 等。以及完整地实现了 `aes`、`blowfish`、`rc4`、`rsa`、`xtea` 等加密算法。\n\n下面的示例用 `sha1` 和 `md5` 计算并输出了一些校验值。\n\n示例 12.20 [hash_sha1.go](examples/chapter_12/hash_sha1.go)：\n\n```go\n// hash_sha1.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"crypto/sha1\"\n\t\"io\"\n\t\"log\"\n)\n\nfunc main() {\n\thasher := sha1.New()\n\tio.WriteString(hasher, \"test\")\n\tb := []byte{}\n\tfmt.Printf(\"Result: %x\\n\", hasher.Sum(b))\n\tfmt.Printf(\"Result: %d\\n\", hasher.Sum(b))\n\t//\n\thasher.Reset()\n\tdata := []byte(\"We shall overcome!\")\n\tn, err := hasher.Write(data)\n\tif n!=len(data) || err!=nil {\n\t\tlog.Printf(\"Hash write error: %v / %v\", n, err)\n\t}\n\tchecksum := hasher.Sum(b)\n\tfmt.Printf(\"Result: %x\\n\", checksum)\n}\n```\n\n输出：\n\n```\nResult: a94a8fe5ccb19ba61c4c0873d391e987982fbbd3\nResult: [169 74 143 229 204 177 155 166 28 76 8 115 211 145 233 135 152 47 187 211]\nResult: e2222bfc59850bbb00a722e764a555603bb59b2a\n```\n\n通过调用 `sha1.New()` 创建了一个新的 `hash.Hash` 对象，用来计算 SHA1 校验值。`Hash` 类型实际上是一个接口，它实现了 `io.Writer` 接口：\n\n```go\ntype Hash interface {\n\t// Write (via the embedded io.Writer interface) adds more data to the running hash.\n\t// It never returns an error.\n\tio.Writer\n\n\t// Sum appends the current hash to b and returns the resulting slice.\n\t// It does not change the underlying hash state.\n\tSum(b []byte) []byte\n\n\t// Reset resets the Hash to its initial state.\n\tReset()\n\n\t// Size returns the number of bytes Sum will return.\n\tSize() int\n\n\t// BlockSize returns the hash's underlying block size.\n\t// The Write method must be able to accept any amount\n\t// of data, but it may operate more efficiently if all writes\n\t// are a multiple of the block size.\n\tBlockSize() int\n}\n```\n\n通过 `io.WriteString` 或 `hasher.Write` 将给定的 `[]byte` 附加到当前的 `hash.Hash` 对象中。\n\n**练习 12.9**：[hash_md5.go](exercises/chapter_12/hash_md5.go)：\n\n在示例 12.20 中检验 md5 算法。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[用 Gob 传输数据](12.11.md)\n- 下一章：[错误处理与测试](13.0.md)\n"
  },
  {
    "path": "eBook/12.2.md",
    "content": "# 12.2 文件读写\n\n## 12.2.1 读文件\n\n在 Go 语言中，文件使用指向 `os.File` 类型的指针来表示的，也叫做文件句柄。我们在前面章节使用到过标准输入 `os.Stdin` 和标准输出 `os.Stdout`，他们的类型都是 `*os.File`。让我们来看看下面这个程序：\n\n示例 12.4 [fileinput.go](examples/chapter_12/fileinput.go)：\n\n```go\npackage main\n\nimport (\n    \"bufio\"\n    \"fmt\"\n    \"io\"\n    \"os\"\n)\n\nfunc main() {\n    inputFile, inputError := os.Open(\"input.dat\")\n    if inputError != nil {\n        fmt.Printf(\"An error occurred on opening the inputfile\\n\" +\n            \"Does the file exist?\\n\" +\n            \"Have you got access to it?\\n\")\n        return // exit the function on error\n    }\n    defer inputFile.Close()\n\n    inputReader := bufio.NewReader(inputFile)\n    for {\n        inputString, readerError := inputReader.ReadString('\\n')\n        fmt.Printf(\"The input was: %s\", inputString)\n        if readerError == io.EOF {\n            return\n        }      \n    }\n}\n```\n\n变量 `inputFile` 是 `*os.File` 类型的。该类型是一个结构，表示一个打开文件的描述符（文件句柄）。然后，使用 `os` 包里的 `Open()` 函数来打开一个文件。该函数的参数是文件名，类型为 `string`。在上面的程序中，我们以只读模式打开 `input.dat` 文件。\n\n如果文件不存在或者程序没有足够的权限打开这个文件，Open函数会返回一个错误：`inputFile, inputError = os.Open(\"input.dat\")`。如果文件打开正常，我们就使用 `defer inputFile.Close()` 语句确保在程序退出前关闭该文件。然后，我们使用 `bufio.NewReader()` 来获得一个读取器变量。\n\n通过使用 `bufio` 包提供的读取器（写入器也类似），如上面程序所示，我们可以很方便的操作相对高层的 `string` 对象，而避免了去操作比较底层的字节。\n\n接着，我们在一个无限循环中使用 `ReadString('\\n')` 或 `ReadBytes('\\n')` 将文件的内容逐行（行结束符 `'\\n'`）读取出来。\n\n**注意：** 在之前的例子中，我们看到，Unix 和 Linux 的行结束符是 `\\n`，而 Windows 的行结束符是 `\\r\\n`。在使用 `ReadString` 和 `ReadBytes` 方法的时候，我们不需要关心操作系统的类型，直接使用 `\\n` 就可以了。另外，我们也可以使用 `ReadLine()` 方法来实现相同的功能。\n\n一旦读取到文件末尾，变量 `readerError` 的值将变成非空（事实上，其值为常量 `io.EOF`），我们就会执行 `return` 语句从而退出循环。\n\n**其他类似函数：**\n\n**1) 将整个文件的内容读到一个字符串里：**\n\n如果您想这么做，可以使用 `io/ioutil` 包里的 `ioutil.ReadFile()` 方法，该方法第一个返回值的类型是 `[]byte`，里面存放读取到的内容，第二个返回值是错误，如果没有错误发生，第二个返回值为 `nil`。请看示例 12.5。类似的，函数 `WriteFile()` 可以将 `[]byte` 的值写入文件。\n\n示例 12.5 [read_write_file1.go](examples/chapter_12/read_write_file1.go)：\n\n```go\npackage main\nimport (\n    \"fmt\"\n    \"io/ioutil\"\n    \"os\"\n)\n\nfunc main() {\n    inputFile := \"products.txt\"\n    outputFile := \"products_copy.txt\"\n    buf, err := ioutil.ReadFile(inputFile)\n    if err != nil {\n        fmt.Fprintf(os.Stderr, \"File Error: %s\\n\", err)\n        // panic(err.Error())\n    }\n    fmt.Printf(\"%s\\n\", string(buf))\n    err = ioutil.WriteFile(outputFile, buf, 0644) // oct, not hex\n    if err != nil {\n        panic(err.Error())\n    }\n}\n```\n\n**2) 带缓冲的读取**\n\n在很多情况下，文件的内容是不按行划分的，或者干脆就是一个二进制文件。在这种情况下，`ReadString()` 就无法使用了，我们可以使用 `bufio.Reader` 的 `Read()`，它只接收一个参数：\n\n```go\nbuf := make([]byte, 1024)\n...\nn, err := inputReader.Read(buf)\nif (n == 0) { break}\n```\n\n变量 `n` 的值表示读取到的字节数.\n\n**3) 按列读取文件中的数据**\n\n如果数据是按列排列并用空格分隔的，你可以使用 `fmt` 包提供的以 `FScan...` 开头的一系列函数来读取他们。请看以下程序，我们将 3 列的数据分别读入变量 `v1`、`v2` 和 `v3` 内，然后分别把他们添加到切片的尾部。\n\n示例 12.6 [read_file2.go](examples/chapter_12/read_file2.go)：\n\n```go\npackage main\nimport (\n    \"fmt\"\n    \"os\"\n)\n\nfunc main() {\n    file, err := os.Open(\"products2.txt\")\n    if err != nil {\n        panic(err)\n    }\n    defer file.Close()\n\n    var col1, col2, col3 []string\n    for {\n        var v1, v2, v3 string\n        _, err := fmt.Fscanln(file, &v1, &v2, &v3)\n        // scans until newline\n        if err != nil {\n            break\n        }\n        col1 = append(col1, v1)\n        col2 = append(col2, v2)\n        col3 = append(col3, v3)\n    }\n\n    fmt.Println(col1)\n    fmt.Println(col2)\n    fmt.Println(col3)\n}\n```\n\n输出结果：\n\n```\n[ABC FUNC GO]\n[40 56 45]\n[150 280 356]\n```\n\n**注意：** `path` 包里包含一个子包叫 `filepath`，这个子包提供了跨平台的函数，用于处理文件名和路径。例如 `Base()` 函数用于获得路径中的最后一个元素（不包含后面的分隔符）：\n\n```go\nimport \"path/filepath\"\nfilename := filepath.Base(path)\n```\n\n**练习 12.3**：[read_csv.go](exercises/chapter_12/read_csv.go)\n\n文件 products.txt 的内容如下：\n\n```\n\"The ABC of Go\";25.5;1500\n\"Functional Programming with Go\";56;280\n\"Go for It\";45.9;356\n\"The Go Way\";55;500\n```\n每行的第一个字段为标题，第二个字段为价格，第三个字段为数量。内容的格式基本与 示例 12.3c 的相同，除了分隔符改成了分号。请读取出文件的内容，创建一个结构用于存取一行的数据，然后使用结构的切片，并把数据打印出来。\n\n关于解析 CSV 文件，`encoding/csv` 包提供了相应的功能。具体请参考 [http://golang.org/pkg/encoding/csv/](http://golang.org/pkg/encoding/csv/) 。\n\n## 12.2.2 compress 包：读取压缩文件\n\n`compress` 包提供了读取压缩文件的功能，支持的压缩文件格式为：bzip2、flate、gzip、lzw 和 zlib。\n\n下面的程序展示了如何读取一个 gzip 文件。\n\n示例 12.7 [gzipped.go](examples/chapter_12/gzipped.go)：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"bufio\"\n\t\"os\"\n\t\"compress/gzip\"\n)\n\nfunc main() {\n\tfName := \"MyFile.gz\"\n\tvar r *bufio.Reader\n\tfi, err := os.Open(fName)\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"%v, Can't open %s: error: %s\\n\", os.Args[0], fName,\n\t\t\terr)\n\t\tos.Exit(1)\n\t}\n\tdefer fi.Close()\n\tfz, err := gzip.NewReader(fi)\n\tif err != nil {\n\t\tr = bufio.NewReader(fi)\n\t} else {\n\t\tr = bufio.NewReader(fz)\n\t}\n\n\tfor {\n\t\tline, err := r.ReadString('\\n')\n\t\tif err != nil {\n\t\t\tfmt.Println(\"Done reading file\")\n\t\t\tos.Exit(0)\n\t\t}\n\t\tfmt.Println(line)\n\t}\n}\n```\n\n## 12.2.3 写文件\n\n请看以下程序：\n\n示例 12.8 [fileoutput.go](examples/chapter_12/fileoutput.go)：\n\n```go\npackage main\n\nimport (\n\t\"os\"\n\t\"bufio\"\n\t\"fmt\"\n)\n\nfunc main () {\n\t// var outputWriter *bufio.Writer\n\t// var outputFile *os.File\n\t// var outputError os.Error\n\t// var outputString string\n\toutputFile, outputError := os.OpenFile(\"output.dat\", os.O_WRONLY|os.O_CREATE, 0666)\n\tif outputError != nil {\n\t\tfmt.Printf(\"An error occurred with file opening or creation\\n\")\n\t\treturn  \n\t}\n\tdefer outputFile.Close()\n\n\toutputWriter := bufio.NewWriter(outputFile)\n\toutputString := \"hello world!\\n\"\n\n\tfor i:=0; i<10; i++ {\n\t\toutputWriter.WriteString(outputString)\n\t}\n\toutputWriter.Flush()\n}\n```\n\n除了文件句柄，我们还需要 `bufio` 的 `Writer`。我们以只写模式打开文件 `output.dat`，如果文件不存在则自动创建：\n\n```go\noutputFile, outputError := os.OpenFile(\"output.dat\", os.O_WRONLY|os.O_CREATE, 0666)\n```\n\n可以看到，`OpenFile` 函数有三个参数：文件名、一个或多个标志（使用逻辑运算符 `|` 连接），使用的文件权限。\n\n我们通常会用到以下标志：\n\n- `os.O_RDONLY`：只读  \n- `os.O_WRONLY`：只写  \n- `os.O_CREATE`：创建：如果指定文件不存在，就创建该文件。  \n- `os.O_TRUNC`：截断：如果指定文件已存在，就将该文件的长度截为 0 。\n\n在读文件的时候，文件的权限是被忽略的，所以在使用 `OpenFile()` 时传入的第三个参数可以用 0 。而在写文件时，不管是 Unix 还是 Windows，都需要使用 `0666`。\n\n然后，我们创建一个写入器（缓冲区）对象：\n\n```go\noutputWriter := bufio.NewWriter(outputFile)\n```\n\n接着，使用一个 `for` 循环，将字符串写入缓冲区，写 10 次：`outputWriter.WriteString(outputString)`\n\n缓冲区的内容紧接着被完全写入文件：`outputWriter.Flush()`\n\n如果写入的东西很简单，我们可以使用 `fmt.Fprintf(outputFile, \"Some test data.\\n\")` 直接将内容写入文件。`fmt` 包里的 `F...` 开头的 `Print()` 函数可以直接写入任何 `io.Writer`，包括文件（请参考[章节 12.8](12.8.md))。\n\n程序 `filewrite.go` 展示了不使用 `fmt.FPrintf()` 函数，使用其他函数如何写文件：\n\n示例 12.8 [filewrite.go](examples/chapter_12/filewrite.go)：\n\n```go\npackage main\n\nimport \"os\"\n\nfunc main() {\n\tos.Stdout.WriteString(\"hello, world\\n\")\n\tf, _ := os.OpenFile(\"test\", os.O_CREATE|os.O_WRONLY, 0666)\n\tdefer f.Close()\n\tf.WriteString(\"hello, world in a file\\n\")\n}\n```\n\n使用 `os.Stdout.WriteString(\"hello, world\\n\")`，我们可以输出到屏幕。\n\n我们以只写模式创建或打开文件 `\"test\"` ，并且忽略了可能发生的错误：`f, _ := os.OpenFile(\"test\", os.O_CREATE|os.O_WRONLY, 0666)`\n\n我们不使用缓冲区，直接将内容写入文件：`f.WriteString()`\n\n**练习 12.4**：[wiki_part1.go](exercises/chapter_12/wiki_part1.go)\n\n（这是一个独立的练习，但是同时也是为[章节 15.4](15.4.md) 做准备）\n\n程序中的数据结构如下，是一个包含以下字段的结构:\n\n```go\ntype Page struct {\n    Title string\n    Body  []byte\n}\n```\n\n请给这个结构编写一个 `save()` 方法，将 Title 作为文件名、Body 作为文件内容，写入到文本文件中。\n\n再编写一个 `load()` 函数，接收的参数是字符串 `title`，该函数读取出与 `title` 对应的文本文件。请使用 `*Page` 做为参数，因为这个结构可能相当巨大，我们不想在内存中拷贝它。请使用 `ioutil` 包里的函数（参考[章节 12.2.1](12.2.md)）。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[读取用户的输入](12.1.md)\n- 下一节：[文件拷贝](12.3.md)\n"
  },
  {
    "path": "eBook/12.3.md",
    "content": "# 12.3 文件拷贝\r\n\r\n如何拷贝一个文件到另一个文件？最简单的方式就是使用 `io` 包：\r\n\r\n示例 12.10 [filecopy.go](examples/chapter_12/filecopy.go)：\r\n\r\n```go\r\n// filecopy.go\r\npackage main\r\n\r\nimport (\r\n\t\"fmt\"\r\n\t\"io\"\r\n\t\"os\"\r\n)\r\n\r\nfunc main() {\r\n\tCopyFile(\"target.txt\", \"source.txt\")\r\n\tfmt.Println(\"Copy done!\")\r\n}\r\n\r\nfunc CopyFile(dstName, srcName string) (written int64, err error) {\r\n\tsrc, err := os.Open(srcName)\r\n\tif err != nil {\r\n\t\treturn\r\n\t}\r\n\tdefer src.Close()\r\n\r\n\tdst, err := os.Create(dstName)\r\n\tif err != nil {\r\n\t\treturn\r\n\t}\r\n\tdefer dst.Close()\r\n\r\n\treturn io.Copy(dst, src)\r\n}\r\n```\r\n\r\n注意 `defer` 的使用：当打开 `dst` 文件时发生了错误，那么 `defer` 仍然能够确保 `src.Close()` 执行。如果不这么做，`src` 文件会一直保持打开状态并占用资源。\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[文件读写](12.2.md)\r\n- 下一节：[从命令行读取参数](12.4.md)\r\n"
  },
  {
    "path": "eBook/12.4.md",
    "content": "# 12.4 从命令行读取参数\n\n## 12.4.1 os 包\n\n`os` 包中有一个 `string` 类型的切片变量 `os.Args`，用来处理一些基本的命令行参数，它在程序启动后读取命令行输入的参数。来看下面的打招呼程序：\n\n示例 12.11 [os_args.go](examples/chapter_12/os_args.go)：\n\n```go\n// os_args.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n)\n\nfunc main() {\n\twho := \"Alice \"\n\tif len(os.Args) > 1 {\n\t\twho += strings.Join(os.Args[1:], \" \")\n\t}\n\tfmt.Println(\"Good Morning\", who)\n}\n```\n\n我们在 IDE 或编辑器中直接运行这个程序输出：`Good Morning Alice`\n\n我们在命令行运行 `os_args` 或 `./os_args` 会得到同样的结果。\n\n但是我们在命令行加入参数，像这样：`os_args John Bill Marc Luke`，将得到这样的输出：`Good Morning Alice John Bill Marc Luke`\n\n这个命令行参数会放置在切片 `os.Args[]` 中（以空格分隔），从索引 1 开始（`os.Args[0]` 放的是程序本身的名字，在本例中是 `os_args`）。函数 `strings.Join` 以空格为间隔连接这些参数。\n\n**练习 12.5**：[hello_who.go](exercises/chapter_12/hello_who.go)\n\n写一个“Hello World”的变种程序：把人的名字作为程序命令行执行的一个参数，比如： `hello_who Evan Michael Laura` 那么会输出 `Hello Evan Michael Laura!`\n\n## 12.4.2 flag 包\n\n`flag` 包有一个扩展功能用来解析命令行选项。但是通常被用来替换基本常量，例如，在某些情况下我们希望在命令行给常量一些不一样的值。（参看 [19 章](19.0.md)的项目）\n\n在 `flag` 包中有一个 `Flag` 是被定义成一个含有如下字段的结构体：\n\n```go\ntype Flag struct {\n\tName     string // name as it appears on command line\n\tUsage    string // help message\n\tValue    Value  // value as set\n\tDefValue string // default value (as text); for usage message\n}\n```\n\n下面的程序 [echo.go](examples/chapter_12/echo.go) 模拟了 Unix 的 echo 功能：\n\n```go\npackage main\n\nimport (\n\t\"flag\" // command line option parser\n\t\"os\"\n)\n\nvar NewLine = flag.Bool(\"n\", false, \"print newline\") // echo -n flag, of type *bool\n\nconst (\n\tSpace   = \" \"\n\tNewline = \"\\n\"\n)\n\nfunc main() {\n\tflag.PrintDefaults()\n\tflag.Parse() // Scans the arg list and sets up flags\n\tvar s string = \"\"\n\tfor i := 0; i < flag.NArg(); i++ {\n\t\tif i > 0 {\n\t\t\ts += \" \"\n\t\t\tif *NewLine { // -n is parsed, flag becomes true\n\t\t\t\ts += Newline\n\t\t\t}\n\t\t}\n\t\ts += flag.Arg(i)\n\t}\n\tos.Stdout.WriteString(s)\n}\n```\n\n`flag.Parse()` 扫描参数列表（或者常量列表）并设置 flag, `flag.Arg(i)` 表示第 i 个参数。`Parse()` 之后 `flag.Arg(i)` 全部可用，`flag.Arg(0)` 就是第一个真实的 flag，而不是像 `os.Args(0)` 放置程序的名字。\n\n`flag.Narg()` 返回参数的数量。解析后 flag 或常量就可用了。\n`flag.Bool()` 定义了一个默认值是 `false` 的 flag：当在命令行出现了第一个参数（这里是 `'n'`），flag 被设置成 `true`（`NewLine` 是 `*bool` 类型）。flag 被解引用到 `*NewLine`，所以当值是 `true` 时将添加一个 `Newline(\"\\n\")`。\n\n`flag.PrintDefaults()` 打印 flag 的使用帮助信息，本例中打印的是：\n\n```go\n-n=false: print newline\n```\n\n`flag.VisitAll(fn func(*Flag))` 是另一个有用的功能：按照字典顺序遍历 flag，并且对每个标签调用 fn （参考 [15.8 章](15.8.md)的例子）\n\n当在命令行 (Windows) 中执行：`echo.exe A B C`，将输出：`A B C`；执行 `echo.exe -n A B C`，将输出：\n\n```\nA\nB\nC\n```\n\n每个字符的输出都新起一行，每次都在输出的数据前面打印使用帮助信息：`-n=false: print newline`。\n\n对于 `flag.Bool` 你可以设置布尔型 flag 来测试你的代码，例如定义一个 flag `processedFlag`:\n\n```go\nvar processedFlag = flag.Bool(\"proc\", false, \"nothing processed yet\")\n```\n\n在后面用如下代码来测试：\n\n```go\nif *processedFlag { // found flag -proc\n\tr = process()\n}\n```\n\n要给 flag 定义其它类型，可以使用 `flag.Int()`，`flag.Float64()`，`flag.String()`。\n\n在[第 15.8 章](15.8.md)你将找到一个具体的例子。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[文件拷贝](12.3.md)\n- 下一节：[用 buffer 读取文件](12.5.md)\n"
  },
  {
    "path": "eBook/12.5.md",
    "content": "# 12.5 用 buffer 读取文件\n\n在下面的例子中，我们结合使用了缓冲读取文件和命令行 flag 解析这两项技术。如果不加参数，那么你输入什么屏幕就打印什么。\n\n参数被认为是文件名，如果文件存在的话就打印文件内容到屏幕。命令行执行 `cat test` 测试输出。\n\n示例 12.11 [cat.go](examples/chapter_12/cat.go)：\n\n```go\npackage main\n\nimport (\n\t\"bufio\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n)\n\nfunc cat(r *bufio.Reader) {\n\tfor {\n\t\tbuf, err := r.ReadBytes('\\n')\n\t\tfmt.Fprintf(os.Stdout, \"%s\", buf)\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn\n}\n\nfunc main() {\n\tflag.Parse()\n\tif flag.NArg() == 0 {\n\t\tcat(bufio.NewReader(os.Stdin))\n\t}\n\tfor i := 0; i < flag.NArg(); i++ {\n\t\tf, err := os.Open(flag.Arg(i))\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"%s:error reading from %s: %s\\n\", os.Args[0], flag.Arg(i), err.Error())\n\t\t\tcontinue\n\t\t}\n\t\tcat(bufio.NewReader(f))\n\t\tf.Close()\n\t}\n}\n```\n\n在 [12.6 章节](12.6.md)，我们将看到如何使用缓冲写入。\n\n**练习 12.6**：[cat_numbered.go](exercises/chapter_12/cat_numbered.go)\n\n扩展 [cat.go](exercises/chapter_12/cat.go) 例子，使用 flag 添加一个选项，目的是为每一行头部加入一个行号。使用 `cat -n test` 测试输出。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[从命令行读取参数](12.4.md)\n- 下一节：[用切片读写文件](12.6.md)\n"
  },
  {
    "path": "eBook/12.6.md",
    "content": "# 12.6 用切片读写文件\n\n切片提供了 Go 中处理 I/O 缓冲的标准方式，下面 `cat` 函数的第二版中，在一个切片缓冲内使用无限 `for` 循环（直到文件尾部 `EOF`）读取文件，并写入到标准输出（`os.Stdout`）。\n\n```go\nfunc cat(f *os.File) {\n\tconst NBUF = 512\n\tvar buf [NBUF]byte\n\tfor {\n\t\tswitch nr, err := f.Read(buf[:]);  {\n\t\tcase nr < 0:\n\t\t\tfmt.Fprintf(os.Stderr, \"cat: error reading: %s\\n\", err.Error())\n\t\t\tos.Exit(1)\n\t\tcase nr == 0: // EOF\n\t\t\treturn\n\t\tcase nr > 0:\n\t\t\tif nw, ew := os.Stdout.Write(buf[0:nr]); nw != nr {\n\t\t\t\tfmt.Fprintf(os.Stderr, \"cat: error writing: %s\\n\", ew.Error())\n\t\t\t}\n\t\t}\n\t}\n}\n```\n\n上面的代码来自于 [cat2.go](examples/chapter_12/cat2.go)，使用了 os 包中的 `os.File` 和 `Read` 方法；`cat2.go` 与 `cat.go` 具有同样的功能。\n\n示例 12.14 [cat2.go](examples/chapter_12/cat2.go)：\n\n```go\npackage main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n)\n\nfunc cat(f *os.File) {\n\tconst NBUF = 512\n\tvar buf [NBUF]byte\n\tfor {\n\t\tswitch nr, err := f.Read(buf[:]); true {\n\t\tcase nr < 0:\n\t\t\tfmt.Fprintf(os.Stderr, \"cat: error reading: %s\\n\", err.Error())\n\t\t\tos.Exit(1)\n\t\tcase nr == 0: // EOF\n\t\t\treturn\n\t\tcase nr > 0:\n\t\t\tif nw, ew := os.Stdout.Write(buf[0:nr]); nw != nr {\n\t\t\t\tfmt.Fprintf(os.Stderr, \"cat: error writing: %s\\n\", ew.Error())\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc main() {\n\tflag.Parse() // Scans the arg list and sets up flags\n\tif flag.NArg() == 0 {\n\t\tcat(os.Stdin)\n\t}\n\tfor i := 0; i < flag.NArg(); i++ {\n\t\tf, err := os.Open(flag.Arg(i))\n\t\tif f == nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cat: can't open %s: error %s\\n\", flag.Arg(i), err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tcat(f)\n\t\tf.Close()\n\t}\n}\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[用 buffer 读取文件](12.5.md)\n- 下一节：[用 defer 关闭文件](12.7.md)\n"
  },
  {
    "path": "eBook/12.7.md",
    "content": "# 12.7 用 defer 关闭文件\n\n`defer` 关键字（参看 [6.4](06.4.md)）对于在函数结束时关闭打开的文件非常有用，例如下面的代码片段：\n\n```go\nfunc data(name string) string {\n\tf, _ := os.OpenFile(name, os.O_RDONLY, 0)\n\tdefer f.Close() // idiomatic Go code!\n\tcontents, _ := ioutil.ReadAll(f)\n\treturn string(contents)\n}\n```\n\n在函数 `return` 后执行了 `f.Close()`\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[用切片读写文件](12.6.md)\n- 下一节：[使用接口的实际例子：fmt.Fprintf](12.8.md)\n"
  },
  {
    "path": "eBook/12.8.md",
    "content": "# 12.8 使用接口的实际例子：fmt.Fprintf\r\n\r\n例子程序 `io_interfaces.go` 很好的阐述了 `io` 包中的接口概念。\r\n\r\n示例 12.15 [io_interfaces.go](examples/chapter_12/io_interfaces.go)：\r\n\r\n```go\r\n// interfaces being used in the GO-package fmt\r\npackage main\r\n\r\nimport (\r\n\t\"bufio\"\r\n\t\"fmt\"\r\n\t\"os\"\r\n)\r\n\r\nfunc main() {\r\n\t// unbuffered\r\n\tfmt.Fprintf(os.Stdout, \"%s\\n\", \"hello world! - unbuffered\")\r\n\t// buffered: os.Stdout implements io.Writer\r\n\tbuf := bufio.NewWriter(os.Stdout)\r\n\t// and now so does buf.\r\n\tfmt.Fprintf(buf, \"%s\\n\", \"hello world! - buffered\")\r\n\tbuf.Flush()\r\n}\r\n```\r\n\r\n输出：\r\n\r\n```\r\nhello world! - unbuffered\r\nhello world! - buffered\r\n```\r\n\r\n下面是 `fmt.Fprintf()` 函数的实际签名\r\n\r\n```go\r\nfunc Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)\r\n```\r\n\r\n不是写入一个文件，而是写入一个 `io.Writer` 接口类型的变量，下面是 `Writer` 接口在 `io` 包中的定义：\r\n\r\n```go\r\ntype Writer interface {\r\n\tWrite(p []byte) (n int, err error)\r\n}\r\n```\r\n\r\n`fmt.Fprintf()` 依据指定的格式向第一个参数内写入字符串，第一个参数必须实现了 `io.Writer` 接口。`Fprintf()` 能够写入任何类型，只要其实现了 `Write` 方法，包括 `os.Stdout`，文件（例如 `os.File`），管道，网络连接，通道等等。同样地，也可以使用 `bufio` 包中缓冲写入。`bufio` 包中定义了 `type Writer struct{...}` 。\r\n\r\n`bufio.Writer` 实现了 `Write()` 方法：\r\n\r\n```go\r\nfunc (b *Writer) Write(p []byte) (nn int, err error)\r\n```\r\n\r\n它还有一个工厂函数：传给它一个 `io.Writer` 类型的参数，它会返回一个带缓冲的 `bufio.Writer` 类型的 `io.Writer` ：\r\n\r\n```go\r\nfunc NewWriter(wr io.Writer) (b *Writer)\r\n```\r\n\r\n适合任何形式的缓冲写入。\r\n\r\n在缓冲写入的最后千万不要忘了使用 `Flush()`，否则最后的输出不会被写入。\r\n\r\n在 [15.2](15.2.md)-[15.8](15.8.md) 章节，我们将使用 `fmt.Fprint()` 函数向 `http.ResponseWriter` 写入，其同样实现了 `io.Writer` 接口。\r\n\r\n**练习 12.7**：[remove_3till5char.go](exercises/chapter_12/remove_3till5char.go)\r\n\r\n下面的代码有一个输入文件 `goprogram`，然后以每一行为单位读取，从读取的当前行中截取第 3 到第 5 的字节写入另一个文件。然而当你运行这个程序，输出的文件却是个空文件。找出程序逻辑中的 bug，修正它并测试。\r\n\r\n```go\r\npackage main\r\n\r\nimport (\r\n\t\"bufio\"\r\n\t\"fmt\"\r\n\t\"os\"\r\n\t\"io\"\r\n)\r\n\r\nfunc main() {\r\n\tinputFile, _ := os.Open(\"goprogram\")\r\n\toutputFile, _ := os.OpenFile(\"goprogramT\", os.O_WRONLY|os.O_CREATE, 0666)\r\n\tdefer inputFile.Close()\r\n\tdefer outputFile.Close()\r\n\tinputReader := bufio.NewReader(inputFile)\r\n\toutputWriter := bufio.NewWriter(outputFile)\r\n\tfor {\r\n\t\tinputString, _, readerError := inputReader.ReadLine()\r\n\t\tif readerError == io.EOF {\r\n\t\t\tfmt.Println(\"EOF\")\r\n\t\t\treturn\r\n\t\t}\r\n\t\toutputString := string(inputString[2:5]) + \"\\r\\n\"\r\n\t\t_, err := outputWriter.WriteString(outputString)\r\n\t\tif err != nil {\r\n\t\t\tfmt.Println(err)\r\n\t\t\treturn\r\n\t\t}\r\n\t}\r\n\tfmt.Println(\"Conversion done\")\r\n}\r\n```\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[用 defer 关闭文件](12.7.md)\r\n- 下一节：[格式化 JSON 数据](12.9.md)\r\n"
  },
  {
    "path": "eBook/12.9.md",
    "content": "# 12.9 JSON 数据格式\n\n数据结构要在网络中传输或保存到文件，就必须对其编码和解码；目前存在很多编码格式：JSON，XML，gob，Google 缓冲协议等等。Go 语言支持所有这些编码格式；在后面的章节，我们将讨论前三种格式。\n\n结构可能包含二进制数据，如果将其作为文本打印，那么可读性是很差的。另外结构内部可能包含匿名字段，而不清楚数据的用意。\n\n通过把数据转换成纯文本，使用命名的字段来标注，让其具有可读性。这样的数据格式可以通过网络传输，而且是与平台无关的，任何类型的应用都能够读取和输出，不与操作系统和编程语言的类型相关。\n\n下面是一些术语说明：\n\n- 数据结构 --> 指定格式 = **序列化** 或 **编码**（传输之前）\n- 指定格式 --> 数据结构 = **反序列化** 或 **解码**（传输之后）\n\n序列化是在内存中把数据转换成指定格式（数据 -> 字符串），反之亦然（字符串 -> 数据）。\n\n编码也是一样的，只是输出一个数据流（实现了 `io.Writer` 接口）；解码是从一个数据流（实现了 `io.Reader`）输出到一个数据结构。\n\n我们都比较熟悉 XML 格式(参阅 [12.10](12.9.md))；但有些时候 JSON（JavaScript Object Notation，参阅 [http://json.org](http://json.org)）被作为首选，主要是由于其格式上非常简洁。通常 JSON 被用于 web 后端和浏览器之间的通讯，但是在其它场景也同样的有用。\n\n这是一个简短的 JSON 片段：\n\n```javascript\n{\n    \"Person\": {\n        \"FirstName\": \"Laura\",\n        \"LastName\": \"Lynn\"\n    }\n}\n```\n\n尽管 XML 被广泛的应用，但是 JSON 更加简洁、轻量（占用更少的内存、磁盘及网络带宽）和更好的可读性，这也使它越来越受欢迎。\n\nGo 语言的 `json` 包可以让你在程序中方便的读取和写入 JSON 数据。\n\n我们将在下面的例子里使用 `json` 包，并使用练习 10.1 [vcard.go](exercises/chapter_10/vcard.go) 中一个简化版本的 `Address` 和 `VCard` 结构（为了简单起见，我们忽略了很多错误处理，不过在实际应用中你必须要合理的处理这些错误，参阅 [13 章](13.0.md)）。\n\n示例 12.16 [json.go](examples/chapter_12/json.go)：\n\n```go\n// json.go\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n)\n\ntype Address struct {\n\tType    string\n\tCity    string\n\tCountry string\n}\n\ntype VCard struct {\n\tFirstName string\n\tLastName  string\n\tAddresses []*Address\n\tRemark    string\n}\n\nfunc main() {\n\tpa := &Address{\"private\", \"Aartselaar\", \"Belgium\"}\n\twa := &Address{\"work\", \"Boom\", \"Belgium\"}\n\tvc := VCard{\"Jan\", \"Kersschot\", []*Address{pa, wa}, \"none\"}\n\t// fmt.Printf(\"%v: \\n\", vc) // {Jan Kersschot [0x126d2b80 0x126d2be0] none}:\n\t// JSON format:\n\tjs, _ := json.Marshal(vc)\n\tfmt.Printf(\"JSON format: %s\", js)\n\t// using an encoder:\n\tfile, _ := os.OpenFile(\"vcard.json\", os.O_CREATE|os.O_WRONLY, 0666)\n\tdefer file.Close()\n\tenc := json.NewEncoder(file)\n\terr := enc.Encode(vc)\n\tif err != nil {\n\t\tlog.Println(\"Error in encoding json\")\n\t}\n}\n```\n\n`json.Marshal()` 的函数签名是 `func Marshal(v interface{}) ([]byte, error)`，下面是数据编码后的 JSON 文本（实际上是一个 `[]byte`）：\n\n```javascript\n{\n    \"FirstName\": \"Jan\",\n    \"LastName\": \"Kersschot\",\n    \"Addresses\": [{\n        \"Type\": \"private\",\n        \"City\": \"Aartselaar\",\n        \"Country\": \"Belgium\"\n    }, {\n        \"Type\": \"work\",\n        \"City\": \"Boom\",\n        \"Country\": \"Belgium\"\n    }],\n    \"Remark\": \"none\"\n}\n```\n\n出于安全考虑，在 web 应用中最好使用 `json.MarshalforHTML()` 函数，其对数据执行 HTML 转码，所以文本可以被安全地嵌在 HTML `<script>` 标签中。\n\n`json.NewEncoder()` 的函数签名是 `func NewEncoder(w io.Writer) *Encoder`，返回的 `Encoder` 类型的指针可调用方法 `Encode(v interface{})`，将数据对象 `v` 的 json 编码写入 `io.Writer` `w` 中。\n\nJSON 与 Go 类型对应如下：\n\n- `bool` 对应 JSON 的 boolean\n- `float64` 对应 JSON 的 number\n- `string` 对应 JSON 的 string\n- `nil` 对应 JSON 的 null\n\n不是所有的数据都可以编码为 JSON 类型，只有验证通过的数据结构才能被编码：\n\n- JSON 对象只支持字符串类型的 key；要编码一个 Go `map` 类型，`map` 必须是 `map[string]T`（`T` 是 `json` 包中支持的任何类型）\n- Channel，复杂类型和函数类型不能被编码\n- 不支持循环数据结构；它将引起序列化进入一个无限循环\n- 指针可以被编码，实际上是对指针指向的值进行编码（或者指针是 `nil`）\n\n### 反序列化：\n\n`json.Unmarshal()` 的函数签名是 `func Unmarshal(data []byte, v interface{}) error` 把 JSON 解码为数据结构。\n\n示例 12.16 中对 `vc` 编码后的数据为 `js` ，对其解码时，我们首先创建结构 `VCard` 用来保存解码的数据：`var v VCard` 并调用 `json.Unmarshal(js, &v)`，解析 `[]byte` 中的 JSON 数据并将结果存入指针 `&v` 指向的值。\n\n虽然反射能够让 JSON 字段去尝试匹配目标结构字段；但是只有真正匹配上的字段才会填充数据。字段没有匹配不会报错，而是直接忽略掉。\n\n（练习 15.2b [twitter_status_json.go](exercises/chapter_15/twitter_status_json.go) 中用到了 `Unmarshal()`）\n\n### 解码任意的数据：\n\njson 包使用 `map[string]interface{}` 和 `[]interface{}` 储存任意的 JSON 对象和数组；其可以被反序列化为任何的 JSON blob 存储到接口值中。\n\n来看这个 JSON 数据，被存储在变量 `b` 中：\n\n```go\nb := []byte(`{\"Name\": \"Wednesday\", \"Age\": 6, \"Parents\": [\"Gomez\", \"Morticia\"]}`)\n```\n\n不用理解这个数据的结构，我们可以直接使用 `Unmarshal()` 把这个数据编码并保存在接口值中：\n\n```go\nvar f interface{}\nerr := json.Unmarshal(b, &f)\n```\n\nf 指向的值是一个 `map`，key 是一个字符串，value 是自身存储作为空接口类型的值：\n\n```go\nmap[string]interface{} {\n\t\"Name\": \"Wednesday\",\n\t\"Age\":  6,\n\t\"Parents\": []interface{} {\n\t\t\"Gomez\",\n\t\t\"Morticia\",\n\t},\n}\n```\n\n要访问这个数据，我们可以使用类型断言\n\n```go\nm := f.(map[string]interface{})\n```\n\n我们可以通过 for range 语法和 type switch 来访问其实际类型：\n\n```go\nfor k, v := range m {\n\tswitch vv := v.(type) {\n\tcase string:\n\t\tfmt.Println(k, \"is string\", vv)\n\tcase int:\n\t\tfmt.Println(k, \"is int\", vv)\n\n\tcase []interface{}:\n\t\tfmt.Println(k, \"is an array:\")\n\t\tfor i, u := range vv {\n\t\t\tfmt.Println(i, u)\n\t\t}\n\tdefault:\n\t\tfmt.Println(k, \"is of a type I don’t know how to handle\")\n\t}\n}\n```\n\n通过这种方式，你可以处理未知的 JSON 数据，同时可以确保类型安全。\n\n### 解码数据到结构\n\n如果我们事先知道 JSON 数据，我们可以定义一个适当的结构并对 JSON 数据反序列化。下面的例子中，我们将定义：\n\n```go\ntype FamilyMember struct {\n\tName    string\n\tAge     int\n\tParents []string\n}\n\n```\n\n并对其反序列化：\n\n```go\nvar m FamilyMember\nerr := json.Unmarshal(b, &m)\n```\n\n程序实际上是分配了一个新的切片。这是一个典型的反序列化引用类型（指针、切片和 `map`）的例子。\n\n### 编码和解码流\n\n`json` 包提供 `Decoder` 和 `Encoder` 类型来支持常用 JSON 数据流读写。`NewDecoder()` 和 `NewEncoder()` 函数分别封装了 `io.Reader` 和 `io.Writer` 接口。\n\n```go\nfunc NewDecoder(r io.Reader) *Decoder\nfunc NewEncoder(w io.Writer) *Encoder\n```\n\n要想把 JSON 直接写入文件，可以使用 `json.NewEncoder` 初始化文件（或者任何实现 `io.Writer` 的类型），并调用 `Encode()`；反过来与其对应的是使用 `json.NewDecoder` 和 `Decode()` 函数：\n\n```go\nfunc NewDecoder(r io.Reader) *Decoder\nfunc (dec *Decoder) Decode(v interface{}) error\n```\n\n来看下接口是如何对实现进行抽象的：数据结构可以是任何类型，只要其实现了某种接口，目标或源数据要能够被编码就必须实现 `io.Writer` 或 `io.Reader` 接口。由于 Go 语言中到处都实现了 Reader 和 Writer，因此 `Encoder` 和 `Decoder` 可被应用的场景非常广泛，例如读取或写入 HTTP 连接、websockets 或文件。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[使用接口的实际例子:fmt.Fprintf](12.8.md)\n- 下一节：[XML 数据格式](12.10.md)\n"
  },
  {
    "path": "eBook/13.0.md",
    "content": "# 13.0 错误处理与测试\n\nGo 没有像 Java 和 .NET 那样的 `try/catch` 异常机制：不能执行抛异常操作。但是有一套 `defer-panic-and-recover` 机制（参见 [13.2](13.2.md)-[13.3](13.3.md) 节）。\n\nGo 的设计者觉得 `try/catch` 机制的使用太泛滥了，而且从底层向更高的层级抛异常太耗费资源。他们给 Go 设计的机制也可以“捕捉”异常，但是更轻量，并且只应该作为（处理错误的）最后的手段。\n\nGo 是怎么处理普通错误的呢？通过在函数和方法中返回错误对象作为它们的唯一或最后一个返回值——如果返回 `nil`，则没有错误发生——并且主调 (calling) 函数总是应该检查收到的错误。\n\n**永远不要忽略错误，否则可能会导致程序崩溃！！**\n\n处理错误并且在函数发生错误的地方给用户返回错误信息：照这样处理就算真的出了问题，你的程序也能继续运行并且通知给用户。`panic()` 和 `recover()` 是用来处理真正的异常（无法预测的错误）而不是普通的错误。\n\n库函数通常必须返回某种错误提示给主调函数。\n\n在前面的章节中我们了解了 Go 检查和报告错误条件的惯有方式：\n\n- 产生错误的函数会返回两个变量，一个值和一个错误码；如果后者是 `nil` 就是成功，非 `nil` 就是发生了错误。\n\n- 为了防止发生错误时正在执行的函数（如果有必要的话甚至会是整个程序）被中止，在调用函数后必须检查错误。\n\n下面这段来自 `pack1` 包的代码 `Func1()` 测试了它的返回值：\n\n```go\nif value, err := pack1.Func1(param1); err != nil {\n\tfmt.Printf(\"Error %s in pack1.Func1 with parameter %v\", err.Error(), param1)\n\treturn    // or: return err\n} else {\n\t// Process(value)\n}\n```\n\n*为了更清晰的代码，应该总是使用包含错误值变量的 if 复合语句*\n\n上例除了 `fmt.Printf()` 还可以使用 `log` 中对应的方法（参见 [13.3](13.3.md) 节和 [15.2](15.2.md) 节），如果程序中止也没关系的话甚至可以使用 `panic()`（参见后面的章节）。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[Go 中的密码学](12.12.md)\n- 下一节：[错误处理](13.1.md)\n"
  },
  {
    "path": "eBook/13.1.md",
    "content": "# 13.1 错误处理\n\nGo 有一个预先定义的 error 接口类型\n\n```go\ntype error interface {\n\tError() string\n}\n```\n\n错误值用来表示异常状态；我们可以在 [5.2 节](05.2.md)中看到它的标准用法。处理文件操作的例子可以在 [12 章](12.0.md)找到；我们将在 [15 章](15.0.md)看到网络操作的例子。`errors` 包中有一个 `errorString` 结构体实现了 `error` 接口。当程序处于错误状态时可以用 `os.Exit(1)` 来中止运行。\n\n## 13.1.1 定义错误\n\n任何时候当你需要一个新的错误类型，都可以用 `errors` 包（必须先 `import`）的 `errors.New()` 函数接收合适的错误信息来创建，像下面这样：\n\n```go\nerr := errors.New(\"math - square root of negative number\")\n```\n\n在示例 13.1 中你可以看到一个简单的用例：\n\n示例 13.1 [errors.go](examples/chapter_13/errors.go)：\n\n```go\n// errors.go\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nvar errNotFound error = errors.New(\"Not found error\")\n\nfunc main() {\n\tfmt.Printf(\"error: %v\", errNotFound)\n}\n// error: Not found error\n```\n\n可以把它用于计算平方根函数的参数测试：\n\n```go\nfunc Sqrt(f float64) (float64, error) {\n\tif f < 0 {\n\t\treturn 0, errors.New (\"math - square root of negative number\")\n\t}\n   // implementation of Sqrt\n}\n```\n\n你可以像下面这样调用 `Sqrt()` 函数：\n\n```go\nif f, err := Sqrt(-1); err != nil {\n\tfmt.Printf(\"Error: %s\\n\", err)\n}\n```\n\n由于 `fmt.Printf` 会自动调用 `String()` 方法 （参见 [10.7 节](10.7.md)），所以错误信息 “`Error: math - square root of negative number`” 会打印出来。通常（错误信息）都会有像 `Error:...` 这样的前缀，所以你的错误信息不要以大写字母开头（注：英文只有句首单词首字母大写，这里应当是考虑到这一点）。\n\n在大部分情况下自定义错误结构类型很有意义的，可以包含除了（低层级的）错误信息以外的其它有用信息，例如，正在进行的操作（打开文件等），全路径或名字。看下面例子中 `os.Open()` 操作触发的 `PathError` 错误：\n\n```go\n// PathError records an error and the operation and file path that caused it.\ntype PathError struct {\n\tOp string    // \"open\", \"unlink\", etc.\n\tPath string  // The associated file.\n\tErr error  // Returned by the system call.\n}\n\nfunc (e *PathError) Error() string {\n\treturn e.Op + \" \" + e.Path + \": \"+ e.Err.Error()\n}\n```\n\n如果有不同错误条件可能发生，那么对实际的错误使用类型断言或类型判断（type-switch）是很有用的，并且可以根据错误场景做一些补救和恢复操作。\n\n```go\n//  err != nil\nif e, ok := err.(*os.PathError); ok {\n\t// remedy situation\n}\n```\n\n或：\n\n```go\nswitch err := err.(type) {\n\tcase ParseError:\n\t\tPrintParseError(err)\n\tcase PathError:\n\t\tPrintPathError(err)\n\t...\n\tdefault:\n\t\tfmt.Printf(\"Not a special error, just %s\\n\", err)\n}\n```\n\n作为第二个例子考虑用 `json` 包的情况。当 `json.Decode()` 在解析 JSON 文档发生语法错误时，指定返回一个 `SyntaxError` 类型的错误：\n\n```go\ntype SyntaxError struct {\n\tmsg    string // description of error\n// error occurred after reading Offset bytes, from which line and columnnr can be obtained\n\tOffset int64\n}\n\nfunc (e *SyntaxError) Error() string { return e.msg }\n```\n\n在调用代码中你可以像这样用类型断言测试错误是不是上面的类型：\n\n```go\nif serr, ok := err.(*json.SyntaxError); ok {\n\tline, col := findLine(f, serr.Offset)\n\treturn fmt.Errorf(\"%s:%d:%d: %v\", f.Name(), line, col, err)\n}\n```\n\n包也可以用额外的方法 (methods)定义特定的错误，比如 `net.Error`：\n\n```go\npackage net\ntype Error interface {\n\tTimeout() bool   // Is the error a timeout?\n\tTemporary() bool // Is the error temporary?\n}\n```\n\n在 [15.1 节](15.1.md) 我们可以看到怎么使用它。\n\n正如你所看到的一样，所有的例子都遵循同一种命名规范：错误类型以 `...Error` 结尾，错误变量以 `err...` 或 `Err...` 开头或者直接叫 `err` 或 `Err`。\n\n`syscall` 是低阶外部包，用来提供系统基本调用的原始接口。它们返回封装整数类型错误码的 `syscall.Errno`；类型 `syscall.Errno` 实现了 `Error` 接口。\n\n大部分 `syscall` 函数都返回一个结果和可能的错误，比如：\n\n```go\nr, err := syscall.Open(name, mode, perm)\nif err != nil {\n\tfmt.Println(err.Error())\n}\n```\n\n`os` 包也提供了一套像 `os.EINAL` 这样的标准错误，它们基于 `syscall` 错误：\n\n```go\nvar (\n\tEPERM\t\tError = Errno(syscall.EPERM)\n\tENOENT\t\tError = Errno(syscall.ENOENT)\n\tESRCH\t\tError = Errno(syscall.ESRCH)\n\tEINTR\t\tError = Errno(syscall.EINTR)\n\tEIO\t\t\tError = Errno(syscall.EIO)\n\t...\n)\n```\n\n## 13.1.2 用 fmt 创建错误对象\n\n通常你想要返回包含错误参数的更有信息量的字符串，例如：可以用 `fmt.Errorf()` 来实现：它和 `fmt.Printf()` 完全一样，接收一个或多个格式占位符的格式化字符串和相应数量的占位变量。和打印信息不同的是它用信息生成错误对象。\n\n比如在前面的平方根例子中使用：\n\n```go\nif f < 0 {\n\treturn 0, fmt.Errorf(\"math: square root of negative number %g\", f)\n}\n```\n\n第二个例子：从命令行读取输入时，如果加了 `--help` 或 `-h` 标志，我们可以用有用的信息产生一个错误：\n\n```go\nif len(os.Args) > 1 && (os.Args[1] == \"-h\" || os.Args[1] == \"--help\") {\n\terr = fmt.Errorf(\"usage: %s infile.txt outfile.txt\", filepath.Base(os.Args[0]))\n\treturn\n}\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[错误处理与测试](13.0.md)\n- 下一节：[运行时异常和 panic](13.2.md)\n"
  },
  {
    "path": "eBook/13.10.md",
    "content": "# 13.10 性能调试：分析并优化 Go 程序\n\n## 13.10.1 时间和内存消耗\n\n可以用这个便捷脚本 *xtime* 来测量：\n\n```sh\n#!/bin/sh\n/usr/bin/time -f '%Uu %Ss %er %MkB %C' \"$@\"\n```\n\n在 Unix 命令行中像这样使用 ```xtime goprogexec```，这里的 progexec 是一个 Go 可执行程序，这句命令行输出类似：56.63u 0.26s 56.92r 1642640kB progexec，分别对应用户时间，系统时间，实际时间和最大内存占用。\n\n## 13.10.2 用 go test 调试\n\n如果代码使用了 Go 中 `testing` 包的基准测试功能，我们可以用 gotest 标准的 `-cpuprofile` 和 `-memprofile` 标志向指定文件写入 CPU 或 内存使用情况报告。\n\n使用方式：```go test -x -v -cpuprofile=prof.out -file x_test.go```\n\n编译执行 x_test.go 中的测试，并向 prof.out 文件中写入 cpu 性能分析信息。\n\n## 13.10.3 用 pprof 调试\n\n你可以在单机程序 progexec 中引入 `runtime/pprof` 包；这个包以 pprof 可视化工具需要的格式写入运行时报告数据。对于 CPU 性能分析来说你需要添加一些代码：\n\n```go\nvar cpuprofile = flag.String(\"cpuprofile\", \"\", \"write cpu profile to file\")\n\nfunc main() {\n\tflag.Parse()\n\tif *cpuprofile != \"\" {\n\t\tf, err := os.Create(*cpuprofile)\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\tpprof.StartCPUProfile(f)\n\t\tdefer pprof.StopCPUProfile()\n\t}\n...\n```\n\n代码定义了一个名为 cpuprofile 的 flag，调用 Go flag 库来解析命令行 flag，如果命令行设置了 cpuprofile flag，则开始 CPU 性能分析并把结果重定向到那个文件（`os.Create` 用拿到的名字创建了用来写入分析数据的文件）。这个分析程序最后需要在程序退出之前调用 `StopCPUProfile()` 来刷新挂起的写操作到文件中；我们用 `defer` 来保证这一切会在 `main()` 返回时触发。\n\n现在用这个 flag 运行程序：```progexec -cpuprofile=progexec.prof```\n\n然后可以像这样用 gopprof 工具：```gopprof progexec progexec.prof```\n\ngopprof 程序是 Google pprofC++ 分析器的一个轻微变种；关于此工具更多的信息，参见[https://github.com/gperftools/gperftools](https://github.com/gperftools/gperftools) 。\n\n如果开启了 CPU 性能分析，Go 程序会以大约每秒 100 次的频率阻塞，并记录当前执行的 goroutine 栈上的程序计数器样本。\n\n此工具一些有趣的命令：\n\n1）`topN`\n\n用来展示分析结果中最开头的 N 份样本，例如：```top5```\n它会展示在程序运行期间调用最频繁的 5 个函数，输出如下：\n\n```\nTotal: 3099 samples\n626 20.2% 20.2% 626 20.2% scanblock\n309 10.0% 30.2% 2839 91.6% main.FindLoops\n...\n```\n\n第 5 列表示函数的调用频度。\n\n2）`web` 或 `web 函数名`\n\n该命令生成一份 SVG 格式的分析数据图表，并在网络浏览器中打开它（还有一个 gv 命令可以生成 PostScript 格式的数据，并在 GhostView 中打开，这个命令需要安装 graphviz）。函数被表示成不同的矩形（被调用越多，矩形越大），箭头指示函数调用链。\n\n3）`list 函数名` 或 `weblist 函数名`\n\n展示对应函数名的代码行列表，第 2 列表示当前行执行消耗的时间，这样就很好地指出了运行过程中消耗最大的代码。\n\n如果发现函数 `runtime.mallocgc`（分配内存并执行周期性的垃圾回收）调用频繁，那么是应该进行内存分析的时候了。找出垃圾回收频繁执行的原因，和内存大量分配的根源。\n\n为了做到这一点必须在合适的地方添加下面的代码：\n\n```go\nvar memprofile = flag.String(\"memprofile\", \"\", \"write memory profile to this file\")\n...\n\nCallToFunctionWhichAllocatesLotsOfMemory()\nif *memprofile != \"\" {\n\tf, err := os.Create(*memprofile)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tpprof.WriteHeapProfile(f)\n\tf.Close()\n\treturn\n}\n```\n\n用 `-memprofile flag` 运行这个程序：```progexec -memprofile=progexec.mprof```\n\n然后你可以像这样再次使用 gopprof 工具：```gopprof progexec progexec.mprof```\n\n`top5`，`list 函数名` 等命令同样适用，只不过现在是以 Mb 为单位测量内存分配情况，这是 top 命令输出的例子：\n\n```\nTotal: 118.3 MB\n\t66.1 55.8% 55.8% 103.7 87.7% main.FindLoops\n\t30.5 25.8% 81.6% 30.5 25.8% main.*LSG·NewLoop\n\t...\n```\n\n从第 1 列可以看出，最上面的函数占用了最多的内存。\n\n同样有一个报告内存分配计数的有趣工具：\n\n```sh\ngopprof --inuse_objects progexec progexec.mprof\n```\n\n对于 web 应用来说，有标准的 HTTP 接口可以分析数据。在 HTTP 服务中添加\n\n```go\nimport _ \"http/pprof\"\n```\n\n会为 /debug/pprof/ 下的一些 URL 安装处理器。然后你可以用一个唯一的参数——你服务中的分析数据的 URL 来执行 gopprof 命令——它会下载并执行在线分析。\n\n```sh\ngopprof http://localhost:6060/debug/pprof/profile # 30-second CPU profile\ngopprof http://localhost:6060/debug/pprof/heap # heap profile\n```\n\n在 Go-blog（引用 15）中有一篇很好的文章用具体的例子进行了分析：分析 Go 程序（2011年6月）。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[用（测试数据）表驱动测试](13.9.md)\n- 下一章：[协程（goroutine）与通道（channel）](14.0.md)\n"
  },
  {
    "path": "eBook/13.2.md",
    "content": "# 13.2 运行时异常和 panic\n\n当发生像数组下标越界或类型断言失败这样的运行错误时，Go 运行时会触发*运行时 panic*，伴随着程序的崩溃抛出一个 `runtime.Error` 接口类型的值。这个错误值有个 `RuntimeError()` 方法用于区别普通错误。\n\n`panic()` 可以直接从代码初始化：当错误条件（我们所测试的代码）很严苛且不可恢复，程序不能继续运行时，可以使用 `panic()` 函数产生一个中止程序的运行时错误。`panic()` 接收一个做任意类型的参数，通常是字符串，在程序死亡时被打印出来。Go 运行时负责中止程序并给出调试信息。在示例 13.2 [panic.go](examples/chapter_13/panic.go) 中阐明了它的工作方式：\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"Starting the program\")\n\tpanic(\"A severe error occurred: stopping the program!\")\n\tfmt.Println(\"Ending the program\")\n}\n```\n\n输出如下：\n\n```\nStarting the program\npanic: A severe error occurred: stopping the program!\npanic PC=0x4f3038\nruntime.panic+0x99 /go/src/pkg/runtime/proc.c:1032\n       runtime.panic(0x442938, 0x4f08e8)\nmain.main+0xa5 E:/Go/GoBoek/code examples/chapter 13/panic.go:8\n       main.main()\nruntime.mainstart+0xf 386/asm.s:84\n       runtime.mainstart()\nruntime.goexit /go/src/pkg/runtime/proc.c:148\n       runtime.goexit()\n---- Error run E:/Go/GoBoek/code examples/chapter 13/panic.exe with code Crashed\n---- Program exited with code -1073741783\n```\n\n一个检查程序是否被已知用户启动的具体例子：\n\n```go\nvar user = os.Getenv(\"USER\")\n\nfunc check() {\n\tif user == \"\" {\n\t\tpanic(\"Unknown user: no value for $USER\")\n\t}\n}\n```\n\n可以在导入包的 `init()` 函数中检查这些。\n\n当发生错误必须中止程序时，`panic()` 可以用于错误处理模式：\n\n```go\nif err != nil {\n\tpanic(\"ERROR occurred:\" + err.Error())\n}\n```\n\n<u>Go panicking</u>：\n\n在多层嵌套的函数调用中调用 `panic()`，可以马上中止当前函数的执行，所有的 `defer` 语句都会保证执行并把控制权交还给接收到 panic 的函数调用者。这样向上冒泡直到最顶层，并执行（每层的） `defer`，在栈顶处程序崩溃，并在命令行中用传给 `panic()` 的值报告错误情况：这个终止过程就是 *panicking*。\n\n标准库中有许多包含 `Must` 前缀的函数，像 `regexp.MustComplie()` 和 `template.Must()`；当正则表达式或模板中转入的转换字符串导致错误时，这些函数会 `panic()`。\n\n不能随意地用 `panic()` 中止程序，必须尽力补救错误让程序能继续执行。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[错误处理](13.1.md)\n- 下一节：[从 panic 中恢复 (recover)](13.3.md)\n"
  },
  {
    "path": "eBook/13.3.md",
    "content": "# 13.3 从 panic 中恢复 (recover)\n\n正如名字一样，这个 (`recover()`) 内建函数被用于从 panic 或错误场景中恢复：让程序可以从 panicking 重新获得控制权，停止终止过程进而恢复正常执行。\n\n`recover` 只能在 `defer` 修饰的函数（参见 [6.4 节](06.4.md)）中使用：用于取得 `panic()` 调用中传递过来的错误值，如果是正常执行，调用 `recover()` 会返回 `nil`，且没有其它效果。\n\n<u>总结</u>：`panic()` 会导致栈被展开直到 `defer` 修饰的 `recover()` 被调用或者程序中止。\n\n下面例子中的 `protect()` 函数调用函数参数 `g` 来保护调用者防止从 `g` 中抛出的运行时 panic，并展示 panic 中的信息：\n\n```go\nfunc protect(g func()) {\n\tdefer func() {\n\t\tlog.Println(\"done\")\n\t\t// Println executes normally even if there is a panic\n\t\tif err := recover(); err != nil {\n\t\t\tlog.Printf(\"run time panic: %v\", err)\n\t\t}\n\t}()\n\tlog.Println(\"start\")\n\tg() //   possible runtime-error\n}\n```\n\n这跟 Java 和 .NET 这样的语言中的 catch 块类似。\n\n`log` 包实现了简单的日志功能：默认的 log 对象向标准错误输出中写入并打印每条日志信息的日期和时间。除了 `Println` 和 `Printf` 函数，其它的致命性函数都会在写完日志信息后调用 `os.Exit(1)`，那些退出函数也是如此。而 Panic 效果的函数会在写完日志信息后调用 `panic()`；可以在程序必须中止或发生了临界错误时使用它们，就像当 web 服务器不能启动时那样（参见 [15.4 节](15.4.md) 中的例子）。\n\nlog 包用那些方法 (methods) 定义了一个 `Logger` 接口类型，如果你想自定义日志系统的话可以参考 [http://golang.org/pkg/log/#Logger](http://golang.org/pkg/log/#Logger) 。\n\n这是一个展示 `panic()`，`defer` 和 `recover()` 怎么结合使用的完整例子：\n\n示例 13.3 [panic_recover.go](examples/chapter_13/panic_recover.go)：\n\n```go\n// panic_recover.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc badCall() {\n\tpanic(\"bad end\")\n}\n\nfunc test() {\n\tdefer func() {\n\t\tif e := recover(); e != nil {\n\t\t\tfmt.Printf(\"Panicing %s\\r\\n\", e)\n\t\t}\n\t}()\n\tbadCall()\n\tfmt.Printf(\"After bad call\\r\\n\") // <-- would not reach\n}\n\nfunc main() {\n\tfmt.Printf(\"Calling test\\r\\n\")\n\ttest()\n\tfmt.Printf(\"Test completed\\r\\n\")\n}\n```\n\n输出：\n\n```\nCalling test\nPanicing bad end\nTest completed\n```\n\n`defer`-`panic()`-`recover()` 在某种意义上也是一种像 `if`，`for` 这样的控制流机制。\n\nGo 标准库中许多地方都用了这个机制，例如，`json` 包中的解码和 `regexp` 包中的 `Complie()` 函数。Go 库的原则是即使在包的内部使用了 `panic()`，在它的对外接口 (API) 中也必须用 `recover()` 处理成显式返回的错误。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[错运行时异常和 panic](13.2.md)\n- 下一节：[自定义包中的错误处理和 panicking](13.4.md)\n"
  },
  {
    "path": "eBook/13.4.md",
    "content": "# 13.4 自定义包中的错误处理和 panicking\n\n这是所有自定义包实现者应该遵守的最佳实践：\n\n1）*在包内部，总是应该从 panic 中 recover*：不允许显式的超出包范围的 `panic()`\n\n2）*向包的调用者返回错误值（而不是 panic）。*\n\n在包内部，特别是在非导出函数中有很深层次的嵌套调用时，将 panic 转换成 `error` 来告诉调用方为何出错，是很实用的（且提高了代码可读性）。\n\n下面的代码则很好地阐述了这一点。我们有一个简单的 `parse` 包（示例 13.4）用来把输入的字符串解析为整数切片；这个包有自己特殊的 `ParseError`。\n\n当没有东西需要转换或者转换成整数失败时，这个包会 `panic()`（在函数 `fields2numbers()` 中）。但是可导出的 `Parse()` 函数会从 `panic()` 中 `recover()` 并用所有这些信息返回一个错误给调用者。为了演示这个过程，在 [panic_recover.go](examples/chapter_13/panic_recover.go) 中 调用了 `parse` 包（示例 13.5）；不可解析的字符串会导致错误并被打印出来。\n\n示例 13.4 [parse.go](examples/chapter_13/parse/parse.go)：\n\n```go\n// parse.go\npackage parse\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"strconv\"\n)\n\n// A ParseError indicates an error in converting a word into an integer.\ntype ParseError struct {\n    Index int      // The index into the space-separated list of words.\n    Word  string   // The word that generated the parse error.\n    Err error // The raw error that precipitated this error, if any.\n}\n\n// String returns a human-readable error message.\nfunc (e *ParseError) String() string {\n    return fmt.Sprintf(\"pkg parse: error parsing %q as int\", e.Word)\n}\n\n// Parse parses the space-separated words in in put as integers.\nfunc Parse(input string) (numbers []int, err error) {\n    defer func() {\n        if r := recover(); r != nil {\n            var ok bool\n            err, ok = r.(error)\n            if !ok {\n                err = fmt.Errorf(\"pkg: %v\", r)\n            }\n        }\n    }()\n\n    fields := strings.Fields(input)\n    numbers = fields2numbers(fields)\n    return\n}\n\nfunc fields2numbers(fields []string) (numbers []int) {\n    if len(fields) == 0 {\n        panic(\"no words to parse\")\n    }\n    for idx, field := range fields {\n        num, err := strconv.Atoi(field)\n        if err != nil {\n            panic(&ParseError{idx, field, err})\n        }\n        numbers = append(numbers, num)\n    }\n    return\n}\n```\n\n示例 13.5 [panic_package.go](examples/chapter_13/panic_package.go)：\n\n```go\n// panic_package.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"./parse/parse\"\n)\n\nfunc main() {\n    var examples = []string{\n            \"1 2 3 4 5\",\n            \"100 50 25 12.5 6.25\",\n            \"2 + 2 = 4\",\n            \"1st class\",\n            \"\",\n    }\n\n    for _, ex := range examples {\n        fmt.Printf(\"Parsing %q:\\n  \", ex)\n        nums, err := parse.Parse(ex)\n        if err != nil {\n            fmt.Println(err) // here String() method from ParseError is used\n            continue\n        }\n        fmt.Println(nums)\n    }\n}\n```\n\n输出：\n\n```\nParsing \"1 2 3 4 5\":\n  [1 2 3 4 5]\nParsing \"100 50 25 12.5 6.25\":\n  pkg: pkg parse: error parsing \"12.5\" as int\nParsing \"2 + 2 = 4\":\n  pkg: pkg parse: error parsing \"+\" as int\nParsing \"1st class\":\n  pkg: pkg parse: error parsing \"1st\" as int\nParsing \"\":\n  pkg: no words to parse\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[从 panic 中恢复 (recover)](13.3.md)\n- 下一节：[一种用闭包处理错误的模式](13.5.md)\n"
  },
  {
    "path": "eBook/13.5.md",
    "content": "# 13.5 一种用闭包处理错误的模式\n\n每当函数返回时，我们应该检查是否有错误发生：但是这会导致重复乏味的代码。结合 defer/panic/recover 机制和闭包可以得到一个我们马上要讨论的更加优雅的模式。不过这个模式只有当所有的函数都是同一种签名时可用，这样就有相当大的限制。一个很好的使用它的例子是 web 应用，所有的处理函数都是下面这样：\n\n```go\nfunc handler1(w http.ResponseWriter, r *http.Request) { ... }\n```\n\n假设所有的函数都有这样的签名：\n\n```go\nfunc f(a type1, b type2)\n```\n\n参数的数量和类型是不相关的。\n\n我们给这个类型一个名字：\n\n```go\nfType1 = func f(a type1, b type2)\n```\n\n在我们的模式中使用了两个帮助函数：\n\n1）`check()`：这是用来检查是否有错误和 panic 发生的函数：\n\n```go\nfunc check(err error) { if err != nil { panic(err) } }\n```\n\n2）`errorhandler()`：这是一个包装函数。接收一个 `fType1` 类型的函数 `fn` 并返回一个调用 `fn` 的函数。里面就包含有 defer/recover 机制，这在 [13.3 节](13.3.md)中有相应描述。\n\n```go\nfunc errorHandler(fn fType1) fType1 {\n\treturn func(a type1, b type2) {\n\t\tdefer func() {\n\t\t\tif err, ok := recover().(error); ok {\n\t\t\t\tlog.Printf(\"run time panic: %v\", err)\n\t\t\t}\n\t\t}()\n\t\tfn(a, b)\n\t}\n}\n```\n\n当错误发生时会 recover 并打印在日志中；除了简单的打印，应用也可以用 `template` 包（参见 [15.7 节](15.7.md)）为用户生成自定义的输出。`check()` 函数会在所有的被调函数中调用，像这样：\n\n```go\nfunc f1(a type1, b type2) {\n\t...\n\tf, _, err := // call function/method\n\tcheck(err)\n\tt, err := // call function/method\n\tcheck(err)\n\t_, err2 := // call function/method\n\tcheck(err2)\n\t...\n}\n```\n\n通过这种机制，所有的错误都会被 recover，并且调用函数后的错误检查代码也被简化为调用 `check(err)` 即可。在这种模式下，不同的错误处理必须对应不同的函数类型；它们（错误处理）可能被隐藏在错误处理包内部。可选的更加通用的方式是用一个空接口类型的切片作为参数和返回值。\n\n我们会在 [15.5 节](15.5.md) 的 web 应用中使用这种模式。\n\n**练习 13.1**：[recover_dividebyzero.go](exercises/chapter_13/recover_divbyzero.go)\n\n用示例 13.3 中的编码模式通过整数除以 0 触发一个运行时 panic。\n\n**练习 13.2**：[panic_defer.go](exercises/chapter_13/panic_defer.go)\n\n阅读下面的完整程序。不要执行它，写出程序的输出结果。然后编译执行并验证你的预想。\n\n```go\n// panic_defer.go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tf()\n\tfmt.Println(\"Returned normally from f.\")\n}\n\nfunc f() {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tfmt.Println(\"Recovered in f\", r)\n\t\t}\n\t}()\n\tfmt.Println(\"Calling g.\")\n\tg(0)\n\tfmt.Println(\"Returned normally from g.\")\n}\n\nfunc g(i int) {\n\tif i > 3 {\n\t\tfmt.Println(\"Panicking!\")\n\t\tpanic(fmt.Sprintf(\"%v\", i))\n\t}\n\tdefer fmt.Println(\"Defer in g\", i)\n\tfmt.Println(\"Printing in g\", i)\n\tg(i + 1)\n}\n```\n\n输出：\n\n```\nCalling g.\nPrinting in g 0\nPrinting in g 1\nPrinting in g 2\nPrinting in g 3\nPanicking!\nDefer in g 3\nDefer in g 2\nDefer in g 1\nDefer in g 0\nRecovered in f 4\nReturned normally from f.\n```\n\n**练习 13.3**：[panic_defer_convint.go](exercises/chapter_13/panic_defer_convint.go)\n\n写一个 `ConvertInt64ToInt()` 函数把 `int64` 值转换为 `int` 值，如果发生错误（提示：参见 [4.5.2.1 节](04.5.md#4521-整型-int-和浮点型-float)）就 `panic()` 。然后在函数 `IntFromInt64` 中调用这个函数并 `recover()`，返回一个整数和一个错误。请测试这个函数！\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[自定义包中的错误处理和 panicking](13.4.md)\n- 下一节：[启动外部命令和程序](13.6.md)\n"
  },
  {
    "path": "eBook/13.6.md",
    "content": "# 13.6 启动外部命令和程序\n\n`os` 包有一个 `StartProcess` 函数可以调用或启动外部系统命令和二进制可执行文件；它的第一个参数是要运行的进程，第二个参数用来传递选项或参数，第三个参数是含有系统环境基本信息的结构体。\n\n这个函数返回被启动进程的 id (pid)，或者启动失败返回错误。\n\n`exec` 包中也有同样功能的更简单的结构体和函数；主要是 `exec.Command(name string, arg ...string)` 和 `Run()`。首先需要用系统命令或可执行文件的名字创建一个 `Command` 对象，然后用这个对象作为接收者调用 `Run()`。下面的程序（因为是执行 Linux 命令，只能在 Linux 下面运行）演示了它们的使用：\n\n示例 13.6 [exec.go](examples/chapter_13/exec.go)：\n\n```go\n// exec.go\npackage main\nimport (\n\t\"fmt\"\n    \"os/exec\"\n\t\"os\"\n)\n\nfunc main() {\n// 1) os.StartProcess //\n/*********************/\n/* Linux: */\nenv := os.Environ()\nprocAttr := &os.ProcAttr{\n\t\t\tEnv: env,\n\t\t\tFiles: []*os.File{\n\t\t\t\tos.Stdin,\n\t\t\t\tos.Stdout,\n\t\t\t\tos.Stderr,\n\t\t\t},\n\t\t}\n// 1st example: list files\npid, err := os.StartProcess(\"/bin/ls\", []string{\"ls\", \"-l\"}, procAttr)  \nif err != nil {\n\t\tfmt.Printf(\"Error %v starting process!\", err)  //\n\t\tos.Exit(1)\n}\nfmt.Printf(\"The process id is %v\", pid)\n```\n\n输出：\n\n``` go\nThe process id is &{2054 0}total 2056\n-rwxr-xr-x 1 ivo ivo 1157555 2011-07-04 16:48 Mieken_exec\n-rw-r--r-- 1 ivo ivo    2124 2011-07-04 16:48 Mieken_exec.go\n-rw-r--r-- 1 ivo ivo   18528 2011-07-04 16:48 Mieken_exec_go_.6\n-rwxr-xr-x 1 ivo ivo  913920 2011-06-03 16:13 panic.exe\n-rw-r--r-- 1 ivo ivo     180 2011-04-11 20:39 panic.go\n```\n\n```go\n// 2nd example: show all processes\npid, err = os.StartProcess(\"/bin/ps\", []string{\"ps\", \"-e\", \"-opid,ppid,comm\"}, procAttr)  \n\nif err != nil {\n\t\tfmt.Printf(\"Error %v starting process!\", err)  //\n\t\tos.Exit(1)\n}\n\nfmt.Printf(\"The process id is %v\", pid)\n```\n\n```go\n// 2) exec.Run //\n/***************/\n// Linux:  OK, but not for ls ?\n// cmd := exec.Command(\"ls\", \"-l\")  // no error, but doesn't show anything ?\n// cmd := exec.Command(\"ls\")  \t\t// no error, but doesn't show anything ?\n\tcmd := exec.Command(\"gedit\")  // this opens a gedit-window\n\terr = cmd.Run()\n\tif err != nil {\n\t\tfmt.Printf(\"Error %v executing command!\", err)\n\t\tos.Exit(1)\n\t}\n\tfmt.Printf(\"The command is %v\", cmd)\n// The command is &{/bin/ls [ls -l] []  <nil> <nil> <nil> 0xf840000210 <nil> true [0xf84000ea50 0xf84000e9f0 0xf84000e9c0] [0xf84000ea50 0xf84000e9f0 0xf84000e9c0] [] [] 0xf8400128c0}\n}\n// in Windows: uitvoering: Error fork/exec /bin/ls: The system cannot find the path specified. starting process!\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[一种用闭包处理错误的模式](13.5.md)\n- 下一节：[Go 中的单元测试和基准测试](13.7.md)\n"
  },
  {
    "path": "eBook/13.7.md",
    "content": "# 13.7 Go 中的单元测试和基准测试\n\n首先所有的包都应该有一定的必要文档，然后同样重要的是对包的测试。\n\n在第 3 章中提到了 Go 的测试工具 gotest， 我们已经在 [9.8 节](09.8.md)中使用过了。这里我们会用更多的例子进行详细说明。\n\n名为 testing 的包被专门用来进行自动化测试，日志和错误报告。并且还包含一些基准测试函数的功能。\n\n<u>备注：</u>gotest 是 Unix bash 脚本，所以在 Windows 下你需要配置 MINGW 环境（参见 [2.5 节](02.5.md)）；在 Windows 环境下把所有的 pkg/linux_amd64 替换成 pkg/windows。\n\n对一个包做（单元）测试，需要写一些可以频繁（每次更新后）执行的小块测试单元来检查代码的正确性。于是我们必须写一些 Go 源文件来测试代码。测试程序必须属于被测试的包，并且文件名满足这种形式 `*_test.go`，所以测试代码和包中的业务代码是分开的。\n\n`_test` 程序不会被普通的 Go 编译器编译，所以当放应用部署到生产环境时它们不会被部署；只有 gotest 会编译所有的程序：普通程序和测试程序。\n\n测试文件中必须导入 `\"testing\"` 包，并写一些名字以 `TestZzz` 打头的全局函数，这里的 `Zzz` 是被测试函数的字母描述，如 `TestFmtInterface()`，`TestPayEmployees()` 等。\n\n测试函数必须有这种形式的头部：\n\n```go\nfunc TestAbcde(t *testing.T)\n```\n\n`T` 是传给测试函数的结构类型，用来管理测试状态，支持格式化测试日志，如 `t.Log`，`t.Error`，`t.ErrorF` 等。在函数的结尾把输出跟想要的结果对比，如果不等就打印一个错误，成功的测试则直接返回。\n\n用下面这些函数来通知测试失败：\n\n1）```func (t *T) Fail()```\n\n\t\t标记测试函数为失败，然后继续执行（剩下的测试）。\n\n2）```func (t *T) FailNow()```\n\n\t\t标记测试函数为失败并中止执行；文件中别的测试也被略过，继续执行下一个文件。\n\n3）```func (t *T) Log(args ...interface{})```\n\n\t\targs 被用默认的格式格式化并打印到错误日志中。\n\n4）```func (t *T) Fatal(args ...interface{})```\n\n\t\t结合 先执行 3），然后执行 2）的效果。\n\n运行 go test 来编译测试程序，并执行程序中所有的 `TestZZZ` 函数。如果所有的测试都通过会打印出 `PASS`。\n\ngotest 可以接收一个或多个函数程序作为参数，并指定一些选项。\n\n结合 `--chatty` 或 `-v` 选项，每个执行的测试函数以及测试状态会被打印。\n\n例如：\n\n```bash\ngo test fmt_test.go --chatty\n=== RUN fmt.TestFlagParser\n--- PASS: fmt.TestFlagParser\n=== RUN fmt.TestArrayPrinter\n--- PASS: fmt.TestArrayPrinter\n...\n```\n\n`testing` 包中有一些类型和函数可以用来做简单的基准测试；测试代码中必须包含以 `BenchmarkZzz` 打头的函数并接收一个 `*testing.B` 类型的参数，比如：\n\n```go\nfunc BenchmarkReverse(b *testing.B) {\n\t...\n}\n```\n\n命令 ```go test –test.bench=.*``` 会运行所有的基准测试函数；代码中的函数会被调用 `N` 次（`N` 是非常大的数，如 `N = 1000000`），并展示 `N` 的值和函数执行的平均时间，单位为 ns（纳秒，ns/op）。如果是用 `testing.Benchmark()` 调用这些函数，直接运行程序即可。\n\n具体可以参见 [14.16 节](14.16.md) 中用 goroutines 运行基准测试的例子以及练习 13.4：[string_reverse_test.go](exercises/chapter_13/string_reverse_test.go)\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[启动外部命令和程序](13.6.md)\n- 下一节：[测试的具体例子](13.8.md)\n"
  },
  {
    "path": "eBook/13.8.md",
    "content": "# 13.8 测试的具体例子\n\n在练习 9.4 中你写了一个叫 [main_oddeven.go](exercises/chapter_9/main_oddeven.go) 的程序用来测试前 100 个整数是否是偶数。这个函数属于 `even` 包。\n\n下面是一种可能的方案：\n\n示例 13.7 [even_main.go](examples/chapter_13/even/even_main/even_main.go)：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"even/even\"\n)\n\nfunc main() {\n\tfor i:=0; i<=100; i++ {\n\t\tfmt.Printf(\"Is the integer %d even? %v\\n\", i, even.Even(i))\n\t}\n}\n```\n\n上面使用了 even.go 中的 `even` 包：\n\n示例 13.8 [even/even.go](examples/chapter_13/even/even/even.go)：\n\n```go\npackage even\n\nfunc Even(i int) bool {\t\t// Exported function\n\treturn i%2 == 0\n}\n\nfunc Odd(i int) bool {\t\t// Exported function\n\treturn i%2 != 0\n}\n```\n\n在 `even` 包的路径下，我们创建一个名为 oddeven_test.go 的测试程序：\n\n示例 13.9 [even/oddeven_test.go](examples/chapter_13/even/even/oddeven_test.go)：\n\n```go\npackage even\n\nimport \"testing\"\n\nfunc TestEven(t *testing.T) {\n\tif !Even(10) {\n\t\tt.Log(\" 10 must be even!\")\n\t\tt.Fail()\n\t}\n\tif Even(7) {\n\t\tt.Log(\" 7 is not even!\")\n\t\tt.Fail()\n\t}\n\n}\n\nfunc TestOdd(t *testing.T) {\n\tif !Odd(11) {\n\t\tt.Log(\" 11 must be odd!\")\n\t\tt.Fail()\n\t}\n\tif Odd(10) {\n\t\tt.Log(\" 10 is not odd!\")\n\t\tt.Fail()\n\t}\n}\n```\n\n由于测试需要具体的输入用例且不可能测试到所有的用例（非常像一个无穷的数），所以我们必须对要使用的测试用例思考再三。\n\n至少应该包括：\n\n- 正常的用例\n- 反面的用例（错误的输入，如用负数或字母代替数字，没有输入等）\n- 边界检查用例（如果参数的取值范围是 0 到 1000，检查 0 和 1000 的情况）\n\n可以直接执行 go install 安装 `even` 或者创建一个 以下内容的 Makefile：\n\n```bash\ninclude $(GOROOT)/src/Make.inc\nTARG=even\nGOFILES=\\\n       even.go\\\ninclude $(GOROOT)/src/Make.pkg\n```\n\n然后执行 make（或 gomake）命令来构建归档文件 even.a\n\n测试代码不能在 GOFILES 参数中引用，因为我们不希望生成的程序中有测试代码。如果包含了测试代码，go test 会给出错误提示！go test 会生成一个单独的包含测试代码的 `_test` 程序。\n\n现在我们可以用命令：`go test`（或 `make test`）来测试 even 包。\n\n因为示例 13.5 中的测试函数不会调用 t.Log 和 t.Fail，所以会得到一个 `PASS` 的结果。在这个简单例子中一切都正常执行。\n\n为了看到失败时的输出，把函数 `TestEven()` 改为：\n\n```go\nfunc TestEven(t *testing.T) {\n\tif Even(10) {\n\t\tt.Log(\"Everything OK: 10 is even, just a test to see failed output!\")\n\t\tt.Fail()\n \t}\n}\n```\n\n现在会调用 t.Log 和 t.Fail，得到的结果如下：\n\n```go\n--- FAIL: even.TestEven (0.00 seconds)\nEverything OK: 10 is even, just a test to see failed output!\nFAIL\n```\n\n**练习 13.4：**[string_reverse_test.go](exercises/chapter_13/string_reverse_test.go)\n\n为练习 7.14 [string_reverse.go](exercises/chapter_7/string_reverse.go) 写一个单元测试。\n\n把 string_reverse 放到自己的包 `strev` 中，只包含一个可导出函数 `Reverse()`。\n\n实现并测试它。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[Go 中的单元测试和基准测试](13.7.md)\n- 下一节：[用（测试数据）表驱动测试](13.9.md)\n"
  },
  {
    "path": "eBook/13.9.md",
    "content": "# 13.9 用（测试数据）表驱动测试\n\n编写测试代码时，一个较好的办法是把测试的输入数据和期望的结果写在一起组成一个数据表：表中的每条记录都是一个含有输入和期望值的完整测试用例，有时还可以结合像测试名字这样的额外信息来让测试输出更多的信息。\n\n实际测试时简单迭代表中的每条记录，并执行必要的测试。这在练习 [13.4](exercises/chapter_13/string_reverse_test.go) 中有具体的应用。\n\n可以抽象为下面的代码段：\n\n```go\nvar tests = []struct{ \t// Test table\n\tin  string\n\tout string\n\n}{\n\t{\"in1\", \"exp1\"},\n\t{\"in2\", \"exp2\"},\n\t{\"in3\", \"exp3\"},\n...\n}\n\nfunc TestFunction(t *testing.T) {\n\tfor i, tt := range tests {\n\t\ts := FuncToBeTested(tt.in)\n\t\tif s != tt.out {\n\t\t\tt.Errorf(\"%d. %q => %q, wanted: %q\", i, tt.in, s, tt.out)\n\t\t}\n\t}\n}\n```\n\n如果大部分函数都可以写成这种形式，那么写一个帮助函数 `verify()` 对实际测试会很有帮助：\n\n```go\nfunc verify(t *testing.T, testnum int, testcase, input, output, expected string) {\n\tif expected != output {\n\t\tt.Errorf(\"%d. %s with input = %s: output %s != %s\", testnum, testcase, input, output, expected)\n\t}\n}\n```\n\n`TestFunction()` 则变为：\n\n```go\nfunc TestFunction(t *testing.T) {\n\tfor i, tt := range tests {\n\t\ts := FuncToBeTested(tt.in)\n\t\tverify(t, i, \"FuncToBeTested: \", tt.in, s, tt.out)\n\t}\n}\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[测试的具体例子](13.8.md)\n- 下一节：[性能调试：分析并优化 Go 程序](13.10.md)\n"
  },
  {
    "path": "eBook/14.0.md",
    "content": "# 14.0 协程 (goroutine) 与通道 (channel)\n\n作为一门 21 世纪的语言，Go 原生支持应用之间的通信（网络，客户端和服务端，分布式计算，参见[第 15 章](15.0.md)和程序的并发。程序可以在不同的处理器和计算机上同时执行不同的代码段。Go 语言为构建并发程序的基本代码块是协程 (goroutine) 与通道 (channel)。他们需要语言，编译器，和 runtime 的支持。Go 语言提供的垃圾回收器对并发编程至关重要。\n\n**不要通过共享内存来通信，而通过通信来共享内存。**\n\n通信强制协作。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[性能调试：分析并优化 Go 程序](13.10.md)\n- 下一节：[并发、并行和协程](14.1.md)\n"
  },
  {
    "path": "eBook/14.1.md",
    "content": "# 14.1 并发、并行和协程\n\n## 14.1.1 什么是协程\n\n一个应用程序是运行在机器上的一个进程；进程是一个运行在自己内存地址空间里的独立执行体。一个进程由一个或多个操作系统线程组成，这些线程其实是共享同一个内存地址空间的一起工作的执行体。几乎所有'正式'的程序都是多线程的，以便让用户或计算机不必等待，或者能够同时服务多个请求（如 Web 服务器），或增加性能和吞吐量（例如，通过对不同的数据集并行执行代码）。一个并发程序可以在一个处理器或者内核上使用多个线程来执行任务，但是只有同一个程序在某个时间点同时运行在多核或者多处理器上才是真正的并行。\n\n并行是一种通过使用多处理器以提高速度的能力。所以并发程序可以是并行的，也可以不是。\n\n公认的，使用多线程的应用难以做到准确，最主要的问题是内存中的数据共享，它们会被多线程以无法预知的方式进行操作，导致一些无法重现或者随机的结果（称作*竞态*）。\n\n**不要使用全局变量或者共享内存，它们会给你的代码在并发运算的时候带来危险。**\n\n解决之道在于同步不同的线程，对数据加锁，这样同时就只有一个线程可以变更数据。在 Go 的标准库 `sync` 中有一些工具用来在低级别的代码中实现加锁；我们在第 [9.3](09.3.md) 节中讨论过这个问题。不过过去的软件开发经验告诉我们这会带来更高的复杂度，更容易使代码出错以及更低的性能，所以这个经典的方法明显不再适合现代多核/多处理器编程：`thread-per-connection` 模型不够有效。\n\nGo 更倾向于其他的方式，在诸多比较合适的范式中，有个被称作 `Communicating Sequential Processes（顺序通信处理）`（CSP, C. Hoare 发明的）还有一个叫做 `message passing-model（消息传递）`（已经运用在了其他语言中，比如 Erlang）。\n\n在 Go 中，应用程序并发处理的部分被称作 `goroutines（协程）`，它可以进行更有效的并发运算。在协程和操作系统线程之间并无一对一的关系：协程是根据一个或多个线程的可用性，映射（多路复用，执行于）在他们之上的；协程调度器在 Go 运行时很好的完成了这个工作。\n\n协程工作在相同的地址空间中，所以共享内存的方式一定是同步的；这个可以使用 `sync` 包来实现（参见第 [9.3](09.3.md) 节），不过我们很不鼓励这样做：Go 使用 `channels` 来同步协程（可以参见第 [14.2](14.2.md) 节等章节）\n\n当系统调用（比如等待 I/O）阻塞协程时，其他协程会继续在其他线程上工作。协程的设计隐藏了许多线程创建和管理方面的复杂工作。\n\n协程是轻量的，比线程更轻。它们痕迹非常不明显（使用少量的内存和资源）：使用 4K 的栈内存就可以在堆中创建它们。因为创建非常廉价，必要的时候可以轻松创建并运行大量的协程（在同一个地址空间中 100,000 个连续的协程）。并且它们对栈进行了分割，从而动态的增加（或缩减）内存的使用；栈的管理是自动的，但不是由垃圾回收器管理的，而是在协程退出后自动释放。\n\n协程可以运行在多个操作系统线程之间，也可以运行在线程之内，让你可以很小的内存占用就可以处理大量的任务。由于操作系统线程上的协程时间片，你可以使用少量的操作系统线程就能拥有任意多个提供服务的协程，而且 Go 运行时可以聪明的意识到哪些协程被阻塞了，暂时搁置它们并处理其他协程。\n\n存在两种并发方式：确定性的（明确定义排序）和非确定性的（加锁/互斥从而未定义排序）。Go 的协程和通道理所当然的支持确定性的并发方式（例如通道具有一个 sender 和一个 receiver）。我们会在第 [14.7](14.7.md) 节中使用一个常见的算法问题（工人问题）来对比两种处理方式。\n\n协程是通过使用关键字 `go` 调用（执行）一个函数或者方法来实现的（也可以是匿名或者 lambda 函数）。这样会在当前的计算过程中开始一个同时进行的函数，在相同的地址空间中并且分配了独立的栈，比如：`go sum(bigArray)`，在后台计算总和。\n\n协程的栈会根据需要进行伸缩，不出现栈溢出；开发者不需要关心栈的大小。当协程结束的时候，它会静默退出：用来启动这个协程的函数不会得到任何的返回值。\n\n任何 Go 程序都必须有的 `main()` 函数也可以看做是一个协程，尽管它并没有通过 `go` 来启动。协程可以在程序初始化的过程中运行（在 `init()` 函数中）。\n\n在一个协程中，比如它需要进行非常密集的运算，你可以在运算循环中周期的使用 `runtime.Gosched()`：这会让出处理器，允许运行其他协程；它并不会使当前协程挂起，所以它会自动恢复执行。使用 `Gosched()` 可以使计算均匀分布，使通信不至于迟迟得不到响应。\n\n## 14.1.2 并发和并行的差异\n\nGo 的并发原语提供了良好的并发设计基础：表达程序结构以便表示独立地执行的动作；所以 Go 的重点不在于并行的首要位置：并发程序可能是并行的，也可能不是。并行是一种通过使用多处理器以提高速度的能力。但往往是，一个设计良好的并发程序在并行方面的表现也非常出色。\n\n在当前的运行时（2012 年一月）实现中，Go 默认没有并行指令，只有一个独立的核心或处理器被专门用于 Go 程序，不论它启动了多少个协程；所以这些协程是并发运行的，但他们不是并行运行的：同一时间只有一个协程会处在运行状态。\n\n这个情况在以后可能会发生改变，不过届时，为了使你的程序可以使用多个核心运行，这时协程就真正的是并行运行了，你必须使用 `GOMAXPROCS` 变量。\n\n这会告诉运行时有多少个协程同时执行。\n\n并且只有 gc 编译器真正实现了协程，适当的把协程映射到操作系统线程。使用 `gccgo` 编译器，会为每一个协程创建操作系统线程。\n\n## 14.1.3 使用 GOMAXPROCS\n\n在 gc 编译器下（6g 或者 8g）你必须设置 `GOMAXPROCS` 为一个大于默认值 `1` 的数值来允许运行时支持使用多于 1 个的操作系统线程，所有的协程都会共享同一个线程除非将 `GOMAXPROCS` 设置为一个大于 1 的数。当 `GOMAXPROCS` 大于 1 时，会有一个线程池管理许多的线程。通过 `gccgo` 编译器 `GOMAXPROCS` 有效的与运行中的协程数量相等。假设 `n` 是机器上处理器或者核心的数量。如果你设置环境变量 `GOMAXPROCS>=n`，或者执行 `runtime.GOMAXPROCS(n)`，接下来协程会被分割（分散）到 `n` 个处理器上。更多的处理器并不意味着性能的线性提升。有这样一个经验法则，对于 n 个核心的情况设置 `GOMAXPROCS` 为 `n-1` 以获得最佳性能，也同样需要遵守这条规则：协程的数量 > `1 + GOMAXPROCS` > 1。\n\n所以如果在某一时间只有一个协程在执行，不要设置 `GOMAXPROCS`！\n\n还有一些通过实验观察到的现象：在一台 1 颗 CPU 的笔记本电脑上，增加 `GOMAXPROCS` 到 9 会带来性能提升。在一台 32 核的机器上，设置 `GOMAXPROCS=8` 会达到最好的性能，在测试环境中，更高的数值无法提升性能。如果设置一个很大的 `GOMAXPROCS` 只会带来轻微的性能下降；设置 `GOMAXPROCS=100`，使用 `top` 命令和 `H` 选项查看到只有 7 个活动的线程。\n\n增加 `GOMAXPROCS` 的数值对程序进行并发计算是有好处的；\n\n请看 [goroutine_select2.go](examples/chapter_14/goroutine_select2.go)\n\n总结：`GOMAXPROCS` 等同于（并发的）线程数量，在一台核心数多于 1 个的机器上，会尽可能有等同于核心数的线程在并行运行。\n\n## 14.1.4 如何用命令行指定使用的核心数量\n\n使用 `flags` 包，如下：\n\n```go\nvar numCores = flag.Int(\"n\", 2, \"number of CPU cores to use\")\n```\n在 main() 中：\n```go\nflag.Parse()\nruntime.GOMAXPROCS(*numCores)\n```\n\n协程可以通过调用 `runtime.Goexit()` 来停止，尽管这样做几乎没有必要。\n\n示例 14.1-[goroutine1.go](examples/chapter_14/goroutine1.go) 介绍了概念：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tfmt.Println(\"In main()\")\n\tgo longWait()\n\tgo shortWait()\n\tfmt.Println(\"About to sleep in main()\")\n\t// sleep works with a Duration in nanoseconds (ns) !\n\ttime.Sleep(10 * 1e9)\n\tfmt.Println(\"At the end of main()\")\n}\n\nfunc longWait() {\n\tfmt.Println(\"Beginning longWait()\")\n\ttime.Sleep(5 * 1e9) // sleep for 5 seconds\n\tfmt.Println(\"End of longWait()\")\n}\n\nfunc shortWait() {\n\tfmt.Println(\"Beginning shortWait()\")\n\ttime.Sleep(2 * 1e9) // sleep for 2 seconds\n\tfmt.Println(\"End of shortWait()\")\n}\n```\n\n输出：\n\n```\nIn main()\nAbout to sleep in main()\nBeginning longWait()\nBeginning shortWait()\nEnd of shortWait()\nEnd of longWait()\nAt the end of main() // after 10s\n```\n\n`main()`，`longWait()` 和 `shortWait()` 三个函数作为独立的处理单元按顺序启动，然后开始并行运行。每一个函数都在运行的开始和结束阶段输出了消息。为了模拟他们运算的时间消耗，我们使用了 `time` 包中的 `Sleep` 函数。`Sleep()` 可以按照指定的时间来暂停函数或协程的执行，这里使用了纳秒（`ns`，符号 `1e9` 表示 1 乘 10 的 9 次方，`e`=指数）。\n\n他们按照我们期望的顺序打印出了消息，几乎都一样，可是我们明白这是模拟出来的，以并行的方式。我们让 `main()` 函数暂停 10 秒从而确定它会在另外两个协程之后结束。如果不这样（如果我们让 `main()` 函数停止 4 秒），`main()` 会提前结束，`longWait()` 则无法完成。如果我们不在 `main()` 中等待，协程会随着程序的结束而消亡。\n\n当 `main()` 函数返回的时候，程序退出：它不会等待任何其他非 main 协程的结束。这就是为什么在服务器程序中，每一个请求都会启动一个协程来处理，`server()` 函数必须保持运行状态。通常使用一个无限循环来达到这样的目的。\n\n另外，协程是独立的处理单元，一旦陆续启动一些协程，你无法确定他们是什么时候真正开始执行的。你的代码逻辑必须独立于协程调用的顺序。\n\n为了对比使用一个线程，连续调用的情况，移除 `go` 关键字，重新运行程序。\n\n现在输出：\n\n```\nIn main()\nBeginning longWait()\nEnd of longWait()\nBeginning shortWait()\nEnd of shortWait()\nAbout to sleep in main()\nAt the end of main() // after 17 s\n```\n\n协程更有用的一个例子应该是在一个非常长的数组中查找一个元素。\n\n将数组分割为若干个不重复的切片，然后给每一个切片启动一个协程进行查找计算。这样许多并行的协程可以用来进行查找任务，整体的查找时间会缩短（除以协程的数量）。\n\n## 14.1.5 Go 协程 (goroutines) 和协程 (coroutines)\n\n（译者注：标题中的“Go协程 (goroutines)”即是 14 章讲的协程，指的是 Go 语言中的协程。而“协程(coroutines)”指的是其他语言中的协程概念，仅在本节出现。）\n\n在其他语言中，比如 C#，Lua 或者 Python 都有协程的概念。这个名字表明它和 Go 协程有些相似，不过有两点不同：\n\n- Go 协程意味着并行（或者可以以并行的方式部署），协程一般来说不是这样的\n- Go 协程通过通道来通信；协程通过让出和恢复操作来通信\n\nGo 协程比协程更强大，也很容易从协程的逻辑复用到 Go 协程。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[协程 (goroutine) 与通道 (channel)](14.0.md)\n- 下一节：[使用通道进行协程间通信](14.2.md)\n"
  },
  {
    "path": "eBook/14.10.md",
    "content": "# 14.10 复用\n\n## 14.10.1 典型的客户端/服务器（C/S）模式\n\n客户端-服务器应用正是 goroutines 和 channels 的亮点所在。\n\n客户端 (Client) 可以是运行在任意设备上的任意程序，它会按需发送请求 (request) 至服务器。服务器 (Server) 接收到这个请求后开始相应的工作，然后再将响应 (response) 返回给客户端。典型情况下一般是多个客户端（即多个请求）对应一个（或少量）服务器。例如我们日常使用的浏览器客户端，其功能就是向服务器请求网页。而 Web 服务器则会向浏览器响应网页数据。\n\n使用 Go 的服务器通常会在协程中执行向客户端的响应，故而会对每一个客户端请求启动一个协程。一个常用的操作方法是客户端请求自身中包含一个通道，而服务器则向这个通道发送响应。\n\n例如下面这个 `Request` 结构，其中内嵌了一个 `replyc` 通道。\n```go\ntype Request struct {\n    a, b      int    \n    replyc    chan int // reply channel inside the Request\n}\n```\n或者更通俗的：\n```go\ntype Reply struct{...}\ntype Request struct{\n    arg1, arg2, arg3 some_type\n    replyc chan *Reply\n}\n```\n\n\n接下来先使用简单的形式,服务器会为每一个请求启动一个协程并在其中执行 `run()` 函数，此举会将类型为 `binOp` 的 `op` 操作返回的 `int` 值发送到 `replyc` 通道。\n\n\n```go\ntype binOp func(a, b int) int\n\nfunc run(op binOp, req *Request) {\n    req.replyc <- op(req.a, req.b)\n}\n```\n`server()` 协程会无限循环以从 `chan *Request` 接收请求，并且为了避免被长时间操作所堵塞，它将为每一个请求启动一个协程来做具体的工作：\n\n```go\nfunc server(op binOp, service chan *Request) {\n    for {\n        req := <-service; // requests arrive here  \n        // start goroutine for request:        \n        go run(op, req);  // don’t wait for op to complete    \n    }\n}\n```\n`server()` 本身则是以协程的方式在 `startServer()` 函数中启动：\n\n```go\nfunc startServer(op binOp) chan *Request {\n    reqChan := make(chan *Request);\n    go server(op, reqChan);\n    return reqChan;\n}\n```\n`startServer()` 则会在 `main` 协程中被调用。\n\n在以下测试例子中，100 个请求会被发送到服务器，只有它们全部被送达后我们才会按相反的顺序检查响应：\n```go\nfunc main() {\n    adder := startServer(func(a, b int) int { return a + b })\n    const N = 100\n    var reqs [N]Request\n    for i := 0; i < N; i++ {\n        req := &reqs[i]\n        req.a = i\n        req.b = i + N\n        req.replyc = make(chan int)\n        adder <- req  // adder is a channel of requests\n    }\n    // checks:\n    for i := N - 1; i >= 0; i-- {\n        // doesn’t matter what order\n        if <-reqs[i].replyc != N+2*i {\n            fmt.Println(“fail at”, i)\n        } else {\n            fmt.Println(“Request “, i, “is ok!”)\n        }\n    }\n    fmt.Println(“done”)\n}\n```\n这些代码可以在 [multiplex_server.go](examples/chapter_14/multiplex_server.go) 找到\n\n输出：\n\n    Request 99 is ok!\n    Request 98 is ok!\n    ...\n    Request 1 is ok!\n    Request 0 is ok!\n    done\n\n这个程序仅启动了 100 个协程。然而即使执行 100,000 个协程我们也能在数秒内看到它完成。这说明了 Go 的协程是如何的轻量：如果我们启动相同数量的真实的线程，程序早就崩溃了。\n\n示例： 14.14-[multiplex_server.go](examples/chapter_14/multiplex_server.go)\n\n```go\npackage main\n\nimport \"fmt\"\n\ntype Request struct {\n\ta, b   int\n\treplyc chan int // reply channel inside the Request\n}\n\ntype binOp func(a, b int) int\n\nfunc run(op binOp, req *Request) {\n\treq.replyc <- op(req.a, req.b)\n}\n\nfunc server(op binOp, service chan *Request) {\n\tfor {\n\t\treq := <-service // requests arrive here\n\t\t// start goroutine for request:\n\t\tgo run(op, req) // don't wait for op\n\t}\n}\n\nfunc startServer(op binOp) chan *Request {\n\treqChan := make(chan *Request)\n\tgo server(op, reqChan)\n\treturn reqChan\n}\n\nfunc main() {\n\tadder := startServer(func(a, b int) int { return a + b })\n\tconst N = 100\n\tvar reqs [N]Request\n\tfor i := 0; i < N; i++ {\n\t\treq := &reqs[i]\n\t\treq.a = i\n\t\treq.b = i + N\n\t\treq.replyc = make(chan int)\n\t\tadder <- req\n\t}\n\t// checks:\n\tfor i := N - 1; i >= 0; i-- { // doesn't matter what order\n\t\tif <-reqs[i].replyc != N+2*i {\n\t\t\tfmt.Println(\"fail at\", i)\n\t\t} else {\n\t\t\tfmt.Println(\"Request \", i, \" is ok!\")\n\t\t}\n\t}\n\tfmt.Println(\"done\")\n}\n```\n## 14.10.2 卸载 (Teardown)：通过信号通道关闭服务器\n\n在上一个版本中 `server()` 在 `main()` 函数返回后并没有完全关闭，而被强制结束了。为了改进这一点，我们可以提供一个退出通道给 `server()` ：\n\n```go\nfunc startServer(op binOp) (service chan *Request, quit chan bool) {\n    service = make(chan *Request)\n    quit = make(chan bool)\n    go server(op, service, quit)\n    return service, quit\n}\n```\n\n`server()` 函数现在则使用 `select` 在 `service` 通道和 `quit` 通道之间做出选择：\n\n```go\nfunc server(op binOp, service chan *request, quit chan bool) {\n    for {\n        select {\n            case req := <-service:\n                go run(op, req) \n            case <-quit:\n                return   \n        }\n    }\n}\n```\n当 `quit` 通道接收到一个 `true` 值时，`server` 就会返回并结束。\n\n在 `main()` 函数中我们做出如下更改：\n\n```go\n    adder, quit := startServer(func(a, b int) int { return a + b })\n```\n\n在 `main()` 函数的结尾处我们放入这一行：`quit <- true`\n\n完整的代码在 [multiplex_server2.go](examples/chapter_14/multiplex_server2.go)，输出和上一个版本是一样的。\n\n示例： 14.15-[multiplex_server2.go](examples/chapter_14/multiplex_server2.go)\n```go\npackage main\n\nimport \"fmt\"\n\ntype Request struct {\n\ta, b   int\n\treplyc chan int // reply channel inside the Request\n}\n\ntype binOp func(a, b int) int\n\nfunc run(op binOp, req *Request) {\n\treq.replyc <- op(req.a, req.b)\n}\n\nfunc server(op binOp, service chan *Request, quit chan bool) {\n\tfor {\n\t\tselect {\n\t\tcase req := <-service:\n\t\t\tgo run(op, req)\n\t\tcase <-quit:\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc startServer(op binOp) (service chan *Request, quit chan bool) {\n\tservice = make(chan *Request)\n\tquit = make(chan bool)\n\tgo server(op, service, quit)\n\treturn service, quit\n}\n\nfunc main() {\n\tadder, quit := startServer(func(a, b int) int { return a + b })\n\tconst N = 100\n\tvar reqs [N]Request\n\tfor i := 0; i < N; i++ {\n\t\treq := &reqs[i]\n\t\treq.a = i\n\t\treq.b = i + N\n\t\treq.replyc = make(chan int)\n\t\tadder <- req\n\t}\n\t// checks:\n\tfor i := N - 1; i >= 0; i-- { // doesn't matter what order\n\t\tif <-reqs[i].replyc != N+2*i {\n\t\t\tfmt.Println(\"fail at\", i)\n\t\t} else {\n\t\t\tfmt.Println(\"Request \", i, \" is ok!\")\n\t\t}\n\t}\n\tquit <- true\n\tfmt.Println(\"done\")\n}\n```\n\n练习 14.13 [multiplex_server3.go](exercises/chapter_14/multiplex_server3.go)：使用之前的例子，编写一个在 `Request` 结构上带有 `String()` 方法的版本，它能决定服务器如何输出；并使用以下两个请求来测试这个程序：\n\n```go    \n    req1 := &Request{3, 4, make(chan int)}\n    req2 := &Request{150, 250, make(chan int)}\n    ...\n    // show the output\n    fmt.Println(req1,\"\\n\",req2)\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[实现 Futures 模式](14.9.md)\n- 下一节：[限制同时处理的请求数](14.11.md)\n"
  },
  {
    "path": "eBook/14.11.md",
    "content": "# 14.11 限制同时处理的请求数\n\n使用带缓冲区的通道很容易实现这一点（参见 [14.2.5](14.2.md#1425-%E5%90%8C%E6%AD%A5%E9%80%9A%E9%81%93-%E4%BD%BF%E7%94%A8%E5%B8%A6%E7%BC%93%E5%86%B2%E7%9A%84%E9%80%9A%E9%81%93)），其缓冲区容量就是同时处理请求的最大数量。程序 [max_tasks.go](examples/chapter_14/max_tasks.go) 虽然没有做什么有用的事但是却包含了这个技巧：超过 `MAXREQS` 的请求将不会被同时处理，因为当信号通道表示缓冲区已满时 `handle()` 函数会阻塞且不再处理其他请求，直到某个请求从 `sem` 中被移除。`sem` 就像一个信号量，这一专业术语用于在程序中表示特定条件的标志变量。\n\n示例：14.16-[max_tasks.go](examples/chapter_14/max_tasks.go)\n```go\npackage main\n\nconst MAXREQS = 50\n\nvar sem = make(chan int, MAXREQS)\n\ntype Request struct {\n\ta, b   int\n\treplyc chan int\n}\n\nfunc process(r *Request) {\n\t// do something\n}\n\nfunc handle(r *Request) {\n\tsem <- 1 // doesn't matter what we put in it\n\tprocess(r)\n\t<-sem // one empty place in the buffer: the next request can start\n}\n\nfunc server(service chan *Request) {\n\tfor {\n\t\trequest := <-service\n\t\tgo handle(request)\n\t}\n}\n\nfunc main() {\n\tservice := make(chan *Request)\n\tgo server(service)\n}\n```\n通过这种方式，应用程序可以通过使用缓冲通道（通道被用作信号量）使协程同步其对该资源的使用，从而充分利用有限的资源（如内存）。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[复用](14.10.md)\n- 下一节：[链式协程](14.12.md)\n"
  },
  {
    "path": "eBook/14.12.md",
    "content": "# 14.12 链式协程\n\n下面的演示程序 [chaining.go](examples/chapter_14/chaining.go) 再次展示了启动巨量的 Go 协程是多么容易。这些协程已全部在 `main()` 函数中的 `for` 循环里启动。当循环完成之后，一个 `0` 被写入到最右边的通道里，于是 100,000 个协程开始执行，接着 `1000000` 这个结果会在 1.5 秒之内被打印出来。\n\n\n这个程序同时也展示了如何通过 `flag.Int` 来解析命令行中的参数以指定协程数量，例如：`chaining -n=7000` 会生成 7000 个协程。\n\n\n示例 14.17-[chaining.go](examples/chapter_14/chaining.go)\n```go\npackage main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n)\n\nvar ngoroutine = flag.Int(\"n\", 100000, \"how many goroutines\")\n\nfunc f(left, right chan int) { left <- 1 + <-right }\n\nfunc main() {\n\tflag.Parse()\n\tleftmost := make(chan int)\n\tvar left, right chan int = nil, leftmost\n\tfor i := 0; i < *ngoroutine; i++ {\n\t\tleft, right = right, make(chan int)\n\t\tgo f(left, right)\n\t}\n\tright <- 0      // bang!\n\tx := <-leftmost // wait for completion\n\tfmt.Println(x)  // 100000, about 1.5 s\n}\n```\n（\n\n译者注：原本认为  `leftmost` 的结果为 `1` ，认为只在最初做了一次赋值，实际结果为 `100000`（无缓存信道具有同步阻塞的特性）\n\n1. 主线程的 `right <- 0`，right 不是最初循环的那个 `right`，而是最终循环的 `right`\n\n2. `for` 循环中最初的 `go f(left, right)` 因为没有发送者一直处于等待状态\n\n3. 当主线程的 `right <- 0` 执行时，类似于递归函数在最内层产生返回值一般\n\n）\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[限制同时处理的请求数](14.11.md)\n- 下一节：[在多核心上并行计算](14.13.md)\n\n"
  },
  {
    "path": "eBook/14.13.md",
    "content": "# 14.13 在多核心上并行计算\n\n假设我们有 `NCPU` 个 CPU 核心：`const  NCPU = 4 //对应一个四核处理器` 然后我们想把计算量分成 `NCPU` 个部分，每一个部分都和其他部分并行运行。\n\n这可以通过以下代码所示的方式完成（我们且省略具体参数）\n\n```go\nfunc DoAll(){\n    sem := make(chan int, NCPU) // Buffering optional but sensible\n    for i := 0; i < NCPU; i++ {\n        go DoPart(sem)\n    }\n    // Drain the channel sem, waiting for NCPU tasks to complete\n    for i := 0; i < NCPU; i++ {\n        <-sem // wait for one task to complete\n    }\n    // All done.\n}\n\nfunc DoPart(sem chan int) {\n    // do the part of the computation\n    sem <-1 // signal that this piece is done\n}\n\nfunc main() {\n    runtime.GOMAXPROCS(NCPU) // runtime.GOMAXPROCS = NCPU\n    DoAll()\n}\n```\n\n- `DoAll()` 函数创建了一个 `sem` 通道，每个并行计算都将在对其发送完成信号；在一个 `for` 循环中 `NCPU` 个协程被启动了，每个协程会承担 `1/NCPU` 的工作量。每一个 `DoPart()` 协程都会向 `sem` 通道发送完成信号。\n\n- `DoAll()` 会在 `for` 循环中等待 `NCPU` 个协程完成：`sem` 通道就像一个信号量，这份代码展示了一个经典的信号量模式。（参见 [14.2.7](14.2.md#1427-%E4%BF%A1%E5%8F%B7%E9%87%8F%E6%A8%A1%E5%BC%8F)）\n\n在以上运行模型中，您还需将 `GOMAXPROCS` 设置为 `NCPU`（参见 [14.1.3](14.1.md#1413-%E4%BD%BF%E7%94%A8-gomaxprocs)）。\n\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[协程链](14.12.md)\n- 下一节：[并行化大量数据的计算](14.14.md)\n"
  },
  {
    "path": "eBook/14.14.md",
    "content": "# 14.14 并行化大量数据的计算\n\n假设我们需要处理一些数量巨大且互不相关的数据项，它们从一个 `in` 通道被传递进来，当我们处理完以后又要将它们放入另一个 `out` 通道，就像一个工厂流水线一样。处理每个数据项也可能包含许多步骤：\n\nPreprocess（预处理） / StepA（步骤A） / StepB（步骤B） / ... / PostProcess（后处理）\n\n一个典型的用于解决按顺序执行每个步骤的顺序流水线算法可以写成下面这样：\n\n```go \nfunc SerialProcessData(in <-chan *Data, out chan<- *Data) {\n    for data := range in {\n        tmpA := PreprocessData(data)\n        tmpB := ProcessStepA(tmpA)\n        tmpC := ProcessStepB(tmpB)\n        out <- PostProcessData(tmpC)\n    }\n}\n```\n\n一次只执行一个步骤，并且按顺序处理每个项目：在第 1 个项目没有被 `PostProcessData()` 并放入 `out` 通道之前绝不会处理第 2 个项目。\n\n如果你仔细想想，你很快就会发现这将会造成巨大的时间浪费。\n\n一个更高效的计算方式是让每一个处理步骤作为一个协程独立工作。每一个步骤从上一步的输出通道中获得输入数据。这种方式仅有极少数时间会被浪费，而大部分时间所有的步骤都在一直执行中：\n\n```go\nfunc ParallelProcessData (in <-chan *Data, out chan<- *Data) {\n    // make channels:\n    preOut := make(chan *Data, 100)\n    stepAOut := make(chan *Data, 100)\n    stepBOut := make(chan *Data, 100)\n    stepCOut := make(chan *Data, 100)\n    // start parallel computations:\n    go PreprocessData(in, preOut)\n    go ProcessStepA(preOut,StepAOut)\n    go ProcessStepB(StepAOut,StepBOut)\n    go ProcessStepC(StepBOut,StepCOut)\n    go PostProcessData(StepCOut,out)\n}   \n```\n\n通道的缓冲区大小可以用来进一步优化整个过程。\n\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[在多核心上并行计算](14.13.md)\n- 下一节：[漏桶算法](14.15.md)\n"
  },
  {
    "path": "eBook/14.15.md",
    "content": "# 14.15 漏桶算法\n\n（译者注：翻译遵照原文，但是对于完全没听过这个算法的人来说比较晦涩，请配合代码片段理解）\n\n考虑以下的客户端-服务器结构：客户端协程执行一个无限循环从某个源头（也许是网络）接收数据；数据读取到 `Buffer` 类型的缓冲区。为了避免分配过多的缓冲区以及释放缓冲区，它保留了一份空闲缓冲区列表，并且使用一个缓冲通道来表示这个列表：`var freeList = make(chan *Buffer,100)`\n\n这个可重用的缓冲区队列 (`freeList`) 与服务器是共享的。 当接收数据时，客户端尝试从 `freeList` 获取缓冲区；但如果此时通道为空，则会分配新的缓冲区。一旦消息被加载后，它将被发送到服务器上的 `serverChan` 通道：\n\n```go\nvar serverChan = make(chan *Buffer)\n```\n\n以下是客户端的算法代码：\n\n```go\nfunc client() {\n   for {\n       var b *Buffer\n       // Grab a buffer if available; allocate if not \n       select {\n           case b = <-freeList:\n               // Got one; nothing more to do\n           default:\n               // None free, so allocate a new one\n               b = new(Buffer)\n       }\n       loadInto(b)         // Read next message from the network\n       serverChan <- b     // Send to server\n       \n   }\n}\n```\n\n服务器的循环则接收每一条来自客户端的消息并处理它，之后尝试将缓冲返回给共享的空闲缓冲区：\n\n```go\nfunc server() {\n    for {\n        b := <-serverChan       // Wait for work.\n        process(b)\n        // Reuse buffer if there's room.\n        select {\n            case freeList <- b:\n                // Reuse buffer if free slot on freeList; nothing more to do\n            default:\n                // Free list full, just carry on: the buffer is 'dropped'\n        }\n    }\n}\n```\n\n但是这种方法在 `freeList` 通道已满的时候是行不通的，因为无法放入空闲 `freeList` 通道的缓冲区会被“丢到地上”由垃圾收集器回收（故名：漏桶算法）。\n\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[并行化大量数据的计算](14.14.md)\n- 下一节：[对 Go 协程进行基准测试](14.16.md)\n"
  },
  {
    "path": "eBook/14.16.md",
    "content": "# 14.16 对 Go 协程进行基准测试\n\n在 [13.7 节](13.7.md) 我们提到了在 Go 语言中对你的函数进行基准测试。在此我们将其应用到一个用协程向通道写入整数再读出的实例中。这个函数将通过 `testing.Benchmark()` 调用 `N` 次（例如：`N = 1,000,000`），`BenchMarkResult` 有一个 `String()` 方法来输出其结果。`N` 的值将由 `gotest` 来判断并取得一个足够大的数字，以获得合理的基准测试结果。当然同样的基准测试方法也适用于普通函数。\n\n如果你想排除指定部分的代码或者更具体的指定要测试的部分，可以使用 `testing.B.startTimer()` 和 `testing.B.stopTimer()` 来开始或结束计时器。基准测试只有在所有的测试通过后才能运行！ \n\n示例：14.18-[benchmark_channels.go](examples/chapter_14/benchmark_channels.go)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nfunc main() {\n\tfmt.Println(\" sync\", testing.Benchmark(BenchmarkChannelSync).String())\n\tfmt.Println(\"buffered\", testing.Benchmark(BenchmarkChannelBuffered).String())\n}\n\nfunc BenchmarkChannelSync(b *testing.B) {\n\tch := make(chan int)\n\tgo func() {\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\tch <- i\n\t\t}\n\t\tclose(ch)\n\t}()\n\tfor range ch {\n\t}\n}\n\nfunc BenchmarkChannelBuffered(b *testing.B) {\n\tch := make(chan int, 128)\n\tgo func() {\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\tch <- i\n\t\t}\n\t\tclose(ch)\n\t}()\n\tfor range ch {\n\t}\n}\n```\n\n输出：\n\n```\n  Output:Windows:  N       Time 1 op   Operations per sec\n  sync      1000000  2443 ns/op  -->  409 332 / s\n  buffered   1000000  4850 ns/op  -->  810 477 / s\n  Linux:\n```\n\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[漏桶算法](14.15.md)\n- 下一节：[使用通道并发访问对象](14.17.md)\n"
  },
  {
    "path": "eBook/14.17.md",
    "content": "# 14.17 使用通道并发访问对象\n\n为了保护对象被并发访问修改，我们可以使用协程在后台顺序执行匿名函数来替代使用同步互斥锁。在下面的程序中我们有一个类型 `Person` 中包含一个字段 `chF` ，这是一个用于存放匿名函数的通道。\n\n这个结构在构造函数 `NewPerson()` 中初始化的同时会启动一个后台协程 `backend()`。`backend()` 方法会在一个无限循环中执行 `chF` 中放置的所有函数，有效地将它们序列化从而提供了安全的并发访问。更改和读取 `salary` 的方法会通过将一个匿名函数写入 `chF` 通道中，然后让 `backend()` 按顺序执行以达到其目的。需注意的是 `Salary()` 方法创建的闭包函数是如何将 `fChan` 通道包含在其中的。\n\n当然，这是一个简化的例子，它不应该被用在这种案例下。但是它却向我们展示了在更复杂的场景中该如何解决这种问题。\n\n示例：14.19-[conc_access.go](examples/chapter_14/conc_access.go)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n)\n\ntype Person struct {\n\tName   string\n\tsalary float64\n\tchF    chan func()\n}\n\nfunc NewPerson(name string, salary float64) *Person {\n\tp := &Person{name, salary, make(chan func())}\n\tgo p.backend()\n\treturn p\n}\n\nfunc (p *Person) backend() {\n\tfor f := range p.chF {\n\t\tf()\n\t}\n}\n\n// Set salary.\nfunc (p *Person) SetSalary(sal float64) {\n\tp.chF <- func() { p.salary = sal }\n}\n\n// Retrieve salary.\nfunc (p *Person) Salary() float64 {\n\tfChan := make(chan float64)\n\tp.chF <- func() { fChan <- p.salary }\n\treturn <-fChan\n}\n\nfunc (p *Person) String() string {\n\treturn \"Person - name is: \" + p.Name + \" - salary is: \" + strconv.FormatFloat(p.Salary(), 'f', 2, 64)\n}\n\nfunc main() {\n\tbs := NewPerson(\"Smith Bill\", 2500.5)\n\tfmt.Println(bs)\n\tbs.SetSalary(4000.25)\n\tfmt.Println(\"Salary changed:\")\n\tfmt.Println(bs)\n}\n```\n\n输出：\n\n```\nPerson - name is: Smith Bill - salary is: 2500.50\nSalary changed:\nPerson - name is: Smith Bill - salary is: 4000.25\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[对Go协程进行基准测试](14.16.md)\n- 下一章：[网络，模板和网页应用](15.0.md)\n\n"
  },
  {
    "path": "eBook/14.2.md",
    "content": "# 14.2 协程间的信道\n\n## 14.2.1 概念\n\n在第一个例子中，协程是独立执行的，他们之间没有通信。他们必须通信才会变得更有用：彼此之间发送和接收信息并且协调/同步他们的工作。协程可以使用共享变量来通信，但是很不提倡这样做，因为这种方式给所有的共享内存的多线程都带来了困难。\n\n而 Go 有一种特殊的类型，*通道（channel）*，就像一个可以用于发送类型化数据的管道，由其负责协程之间的通信，从而避开所有由共享内存导致的陷阱；这种通过通道进行通信的方式保证了同步性。数据在通道中进行传递：*在任何给定时间，一个数据被设计为只有一个协程可以对其访问，所以不会发生数据竞争。* 数据的所有权（可以读写数据的能力）也因此被传递。\n\n工厂的传送带是个很有用的例子。一个机器（生产者协程）在传送带上放置物品，另外一个机器（消费者协程）拿到物品并打包。\n\n通道服务于通信的两个目的：值的交换，同步的，保证了两个计算（协程）任何时候都是可知状态。\n\n![](images/14.2_fig14.1.png?raw=true)\n\n通常使用这样的格式来声明通道：`var identifier chan datatype`\n\n未初始化的通道的值是 `nil`。\n\n所以通道只能传输一种类型的数据，比如 `chan int` 或者 `chan string`，所有的类型都可以用于通道，空接口 `interface{}` 也可以，甚至可以（有时非常有用）创建通道的通道。\n\n通道实际上是类型化消息的队列：使数据得以传输。它是先进先出(FIFO) 的结构所以可以保证发送给他们的元素的顺序（有些人知道，通道可以比作 Unix shells 中的双向管道 (two-way pipe) ）。通道也是引用类型，所以我们使用 `make()` 函数来给它分配内存。这里先声明了一个字符串通道 ch1，然后创建了它（实例化）：\n\n```go\nvar ch1 chan string\nch1 = make(chan string)\n```\n\n当然可以更短： `ch1 := make(chan string)`。\n\n这里我们构建一个 int 通道的通道： `chanOfChans := make(chan chan int)`。\n\n或者函数通道：`funcChan := make(chan func())`（相关示例请看第 [14.17](14.17.md) 节）。\n\n所以通道是第一类对象：可以存储在变量中，作为函数的参数传递，从函数返回以及通过通道发送它们自身。另外它们是类型化的，允许类型检查，比如尝试使用整数通道发送一个指针。\n\n## 14.2.2 通信操作符 <-\n\n这个操作符直观的标示了数据的传输：信息按照箭头的方向流动。\n\n流向通道（发送）\n\n`ch <- int1` 表示：用通道 `ch` 发送变量 `int1`（双目运算符，中缀 = 发送）\n\n从通道流出（接收），三种方式：\n\n`int2 = <- ch` 表示：变量 `int2` 从通道 ch（一元运算的前缀操作符，前缀 = 接收）接收数据（获取新值）；假设 `int2` 已经声明过了，如果没有的话可以写成：`int2 := <- ch`。\n\n`<- ch` 可以单独调用获取通道的（下一个）值，当前值会被丢弃，但是可以用来验证，所以以下代码是合法的：\n\n```go\nif <- ch != 1000{\n\t...\n}\n```\n\n同一个操作符 `<-` 既用于**发送**也用于**接收**，但 Go 会根据操作对象弄明白该干什么 。虽非强制要求，但为了可读性通道的命名通常以 `ch` 开头或者包含 `chan` 。通道的发送和接收都是原子操作：它们总是互不干扰地完成。下面的示例展示了通信操作符的使用。\n\n示例 14.2-[goroutine2.go](examples/chapter_14/goroutine2.go)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tch := make(chan string)\n\n\tgo sendData(ch)\n\tgo getData(ch)\n\n\ttime.Sleep(1e9)\n}\n\nfunc sendData(ch chan string) {\n\tch <- \"Washington\"\n\tch <- \"Tripoli\"\n\tch <- \"London\"\n\tch <- \"Beijing\"\n\tch <- \"Tokyo\"\n}\n\nfunc getData(ch chan string) {\n\tvar input string\n\t// time.Sleep(2e9)\n\tfor {\n\t\tinput = <-ch\n\t\tfmt.Printf(\"%s \", input)\n\t}\n}\n```\n\n输出：\n\n```\nWashington Tripoli London Beijing tokyo\n```\n\n`main()` 函数中启动了两个协程：`sendData()` 通过通道 ch 发送了 5 个字符串，`getData()` 按顺序接收它们并打印出来。\n\n如果 2 个协程需要通信，你必须给他们同一个通道作为参数才行。\n\n尝试一下如果注释掉 `time.Sleep(1e9)` 会如何。\n\n我们发现协程之间的同步非常重要：\n\n- `main()` 等待了 1 秒让两个协程完成，如果不这样，`sendData()` 就没有机会输出。\n- `getData()` 使用了无限循环：它随着 `sendData()` 的发送完成和 `ch` 变空也结束了。\n- 如果我们移除一个或所有 `go` 关键字，程序无法运行，Go 运行时会抛出 panic：\n\n```\n---- Error run E:/Go/Goboek/code examples/chapter 14/goroutine2.exe with code Crashed ---- Program exited with code -2147483645: panic: all goroutines are asleep-deadlock!\n```\n\n为什么会这样？运行时 (runtime) 会检查所有的协程（像本例中只有一个）是否在等待着什么东西（可从某个通道读取或者写入某个通道），这意味着程序将无法继续执行。这是死锁 (deadlock) 的一种形式，而运行时 (runtime) 可以为我们检测到这种情况。\n\n注意：不要使用打印状态来表明通道的发送和接收顺序：由于打印状态和通道实际发生读写的时间延迟会导致和真实发生的顺序不同。\n\n练习 14.4：解释一下为什么如果在函数 `getData()` 的一开始插入 `time.Sleep(2e9)`，不会出现错误但也没有输出呢。\n\n## 14.2.3 通道阻塞\n\n默认情况下，通信是同步且无缓冲的：在有接受者接收数据之前，发送不会结束。可以想象一个无缓冲的通道在没有空间来保存数据的时候：必须要一个接收者准备好接收通道的数据然后发送者可以直接把数据发送给接收者。所以通道的发送/接收操作在对方准备好之前是阻塞的：\n\n1）对于同一个通道，发送操作（协程或者函数中的），在接收者准备好之前是阻塞的：如果 `ch` 中的数据无人接收，就无法再给通道传入其他数据：新的输入无法在通道非空的情况下传入。所以发送操作会等待 `ch` 再次变为可用状态：就是通道值被接收时（可以传入变量）。\n\n2）对于同一个通道，接收操作是阻塞的（协程或函数中的），直到发送者可用：如果通道中没有数据，接收者就阻塞了。\n\n尽管这看上去是非常严格的约束，实际在大部分情况下工作的很不错。\n\n程序 [channel_block.go](examples/chapter_14/channel_block.go) 验证了以上理论，一个协程在无限循环中给通道发送整数数据。不过因为没有接收者，只输出了一个数字 `0`。\n\n示例 14.3-[channel_block.go](examples/chapter_14/channel_block.go)\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tch1 := make(chan int)\n\tgo pump(ch1)       // pump hangs\n\tfmt.Println(<-ch1) // prints only 0\n}\n\nfunc pump(ch chan int) {\n\tfor i := 0; ; i++ {\n\t\tch <- i\n\t}\n}\n```\n\n输出：\n\n```\n0\n```\n\n`pump()` 函数为通道提供数值，也被叫做生产者。\n\n为通道解除阻塞定义了 `suck()` 函数来在无限循环中读取通道，参见示例 14.4-[channel_block2.go](examples/chapter_14/channel_block2.go)：\n\n```go\nfunc suck(ch chan int) {\n\tfor {\n\t\tfmt.Println(<-ch)\n\t}\n}\n```\n在 `main()` 中使用协程开始它：\n\n```go\ngo pump(ch1)\ngo suck(ch1)\ntime.Sleep(1e9)\n```\n\n给程序 1 秒的时间来运行：输出了上万个整数。\n\n练习 14.1：[channel_block3.go](exercises/chapter_14/channel_block3.go)：写一个通道证明它的阻塞性，开启一个协程接收通道的数据，持续 15 秒，然后给通道放入一个值。在不同的阶段打印消息并观察输出。\n\n## 14.2.4 通过一个（或多个）通道交换数据进行协程同步。\n\n通信是一种同步形式：通过通道，两个协程在通信（协程会合）中某刻同步交换数据。无缓冲通道成为了多个协程同步的完美工具。\n\n甚至可以在通道两端互相阻塞对方，形成了叫做**死锁**的状态。Go 运行时会检查并 `panic()`，停止程序。死锁几乎完全是由糟糕的设计导致的。\n\n无缓冲通道会被阻塞。设计无阻塞的程序可以避免这种情况，或者使用带缓冲的通道。\n\n练习 14.2： [blocking.go](exercises/chapter_14/blocking.go)\n\n解释为什么下边这个程序会导致 panic：所有的协程都休眠了 - 死锁！\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc f1(in chan int) {\n\tfmt.Println(<-in)\n}\n\nfunc main() {\n\tout := make(chan int)\n\tout <- 2\n\tgo f1(out)\n}\n```\n\n## 14.2.5 同步通道-使用带缓冲的通道\n\n一个无缓冲通道只能包含 1 个元素，有时显得很局限。我们给通道提供了一个缓存，可以在扩展的 `make` 命令中设置它的容量，如下：\n\n```go\nbuf := 100\nch1 := make(chan string, buf)\n```\n\n`buf` 是通道可以同时容纳的元素（这里是 `string`）个数\n\n在缓冲满载（缓冲被全部使用）之前，给一个带缓冲的通道发送数据是不会阻塞的，而从通道读取数据也不会阻塞，直到缓冲空了。\n\n缓冲容量和类型无关，所以可以（尽管可能导致危险）给一些通道设置不同的容量，只要他们拥有同样的元素类型。内置的 `cap()` 函数可以返回缓冲区的容量。\n\n如果容量大于 0，通道就是异步的了：缓冲满载（发送）或变空（接收）之前通信不会阻塞，元素会按照发送的顺序被接收。如果容量是 0 或者未设置，通信仅在收发双方准备好的情况下才可以成功。\n\n同步：`ch :=make(chan type, value)`\n\n- `value == 0 -> synchronous`, unbuffered （阻塞）\n- `value > 0 -> asynchronous`, buffered（非阻塞）取决于 `value` 元素\n\n若使用通道的缓冲，你的程序会在“请求”激增的时候表现更好：更具弹性，专业术语叫：更具有伸缩性(scalable)。在设计算法时首先考虑使用无缓冲通道，只在不确定的情况下使用缓冲。\n\n练习 14.3：[channel_buffer.go](exercises/chapter_14/channel_buffer.go)：给 [channel_block3.go](exercises/chapter_14/channel_block3.go) 的通道增加缓冲并观察输出有何不同。\n\n## 14.2.6 协程中用通道输出结果\n\n为了知道计算何时完成，可以通过信道回报。在例子 `go sum(bigArray)` 中，要这样写：\n\n```go\nch := make(chan int)\ngo sum(bigArray, ch) // bigArray puts the calculated sum on ch\n// .. do something else for a while\nsum := <- ch // wait for, and retrieve the sum\n```\n\n也可以使用通道来达到同步的目的，这个很有效的用法在传统计算机中称为信号量 (semaphore)。或者换个方式：通过通道发送信号告知处理已经完成（在协程中）。\n\n在其他协程运行时让 `main` 程序无限阻塞的通常做法是在 `main()` 函数的最后放置一个 `select {}`。\n\n也可以使用通道让 `main` 程序等待协程完成，就是所谓的信号量模式，我们会在接下来的部分讨论。\n\n## 14.2.7 信号量模式\n\n下边的片段阐明：协程通过在通道 `ch` 中放置一个值来处理结束的信号。`main()` 协程等待 `<-ch` 直到从中获取到值。\n\n我们期望从这个通道中获取返回的结果，像这样：\n\n```go\nfunc compute(ch chan int){\n\tch <- someComputation() // when it completes, signal on the channel.\n}\n\nfunc main(){\n\tch := make(chan int) \t// allocate a channel.\n\tgo compute(ch)\t\t// start something in a goroutines\n\tdoSomethingElseForAWhile()\n\tresult := <- ch\n}\n```\n\n这个信号也可以是其他的，不返回结果，比如下面这个协程中的匿名函数 (lambda) 协程：\n\n```go\nch := make(chan int)\ngo func(){\n\t// doSomething\n\tch <- 1 // Send a signal; value does not matter\n}()\ndoSomethingElseForAWhile()\n<- ch\t// Wait for goroutine to finish; discard sent value.\n```\n\n或者等待两个协程完成，每一个都会对切片 `s` 的一部分进行排序，片段如下：\n\n```go\ndone := make(chan bool)\n// doSort is a lambda function, so a closure which knows the channel done:\ndoSort := func(s []int){\n\tsort(s)\n\tdone <- true\n}\ni := pivot(s)\ngo doSort(s[:i])\ngo doSort(s[i:])\n<-done\n<-done\n```\n\n下边的代码，用完整的信号量模式对长度为 `N` 的 `float64` 切片进行了 `N` 个 `doSomething()` 计算并同时完成，通道 `sem` 分配了相同的长度（且包含空接口类型的元素），待所有的计算都完成后，发送信号（通过放入值）。在循环中从通道 `sem` 不停的接收数据来等待所有的协程完成。\n\n```go\ntype Empty interface {}\nvar empty Empty\n...\ndata := make([]float64, N)\nres := make([]float64, N)\nsem := make(chan Empty, N)\n...\nfor i, xi := range data {\n\tgo func (i int, xi float64) {\n\t\tres[i] = doSomething(i, xi)\n\t\tsem <- empty\n\t} (i, xi)\n}\n// wait for goroutines to finish\nfor i := 0; i < N; i++ { <-sem }\n```\n\n注意上述代码中闭合函数的用法：`i`、`xi` 都是作为参数传入闭合函数的，这一做法使得每个协程（译者注：在其启动时）获得一份 `i` 和 `xi` 的单独拷贝，从而向闭合函数内部屏蔽了外层循环中的 `i` 和 `xi` 变量；否则，`for` 循环的下一次迭代会更新所有协程中 `i` 和 `xi` 的值。另一方面，切片 `res` 没有传入闭合函数，因为协程不需要 `res` 的单独拷贝。切片 `res` 也在闭合函数中但并不是参数。\n\n## 14.2.8 实现并行的 `for` 循环\n\n在上一部分章节 [14.2.7](14.2.md#1427-信号量模式) 的代码片段中：`for` 循环的每一个迭代是并行完成的：\n\n```go\nfor i, v := range data {\n\tgo func (i int, v float64) {\n\t\tdoSomething(i, v)\n\t\t...\n\t} (i, v)\n}\n```\n\n在 `for` 循环中并行计算迭代可能带来很好的性能提升。不过所有的迭代都必须是独立完成的。有些语言比如 Fortress 或者其他并行框架以不同的结构实现了这种方式，在 Go 中用协程实现起来非常容易：\n\n## 14.2.9 用带缓冲通道实现一个信号量\n\n信号量是实现互斥锁（排外锁）常见的同步机制，限制对资源的访问，解决读写问题，比如没有实现信号量的 `sync` 的 Go 包，使用带缓冲的通道可以轻松实现：\n\n- 带缓冲通道的容量和要同步的资源容量相同\n- 通道的长度（当前存放的元素个数）与当前资源被使用的数量相同\n- 容量减去通道的长度就是未处理的资源个数（标准信号量的整数值）\n\n不用管通道中存放的是什么，只关注长度；因此我们创建了一个长度可变但容量为 0（字节）的通道：\n\n```go\ntype Empty interface {}\ntype semaphore chan Empty\n```\n\n将可用资源的数量 `N` 来初始化信号量 `semaphore`：`sem = make(semaphore, N)`\n\n然后直接对信号量进行操作：\n\n```go\n// acquire n resources\nfunc (s semaphore) P(n int) {\n\te := new(Empty)\n\tfor i := 0; i < n; i++ {\n\t\ts <- e\n\t}\n}\n\n// release n resources\nfunc (s semaphore) V(n int) {\n\tfor i:= 0; i < n; i++{\n\t\t<- s\n\t}\n}\n```\n\n可以用来实现一个互斥的例子：\n\n```go\n/* mutexes */\nfunc (s semaphore) Lock() {\n\ts.P(1)\n}\n\nfunc (s semaphore) Unlock(){\n\ts.V(1)\n}\n\n/* signal-wait */\nfunc (s semaphore) Wait(n int) {\n\ts.P(n)\n}\n\nfunc (s semaphore) Signal() {\n\ts.V(1)\n}\n```\n\n练习 14.5：[gosum.go](exercises/chapter_14/gosum.go)：用这种习惯用法写一个程序，开启一个协程来计算 2 个整数的和并等待计算结果并打印出来。\n\n练习 14.6：[producer_consumer.go](exercises/chapter_14/producer_consumer.go)：用这种习惯用法写一个程序，有两个协程，第一个提供数字 0，10，20，...，90 并将他们放入通道，第二个协程从通道中读取并打印。`main()` 等待两个协程完成后再结束。\n\n**习惯用法：通道工厂模式**\n\n编程中常见的另外一种模式如下：不将通道作为参数传递给协程，而用函数来生成一个通道并返回（工厂角色）；函数内有个匿名函数被协程调用。\n\n在 [channel_block2.go](examples/chapter_14/channel_block2.go) 加入这种模式便有了示例 14.5-[channel_idiom.go](examples/chapter_14/channel_idiom.go)：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tstream := pump()\n\tgo suck(stream)\n\ttime.Sleep(1e9)\n}\n\nfunc pump() chan int {\n\tch := make(chan int)\n\tgo func() {\n\t\tfor i := 0; ; i++ {\n\t\t\tch <- i\n\t\t}\n\t}()\n\treturn ch\n}\n\nfunc suck(ch chan int) {\n\tfor {\n\t\tfmt.Println(<-ch)\n\t}\n}\n```\n\n## 14.2.10 给通道使用 for 循环\n\n`for` 循环的 `range` 语句可以用在通道 `ch` 上，便可以从通道中获取值，像这样：\n\n```go\nfor v := range ch {\n\tfmt.Printf(\"The value is %v\\n\", v)\n}\n```\n\n它从指定通道中读取数据直到通道关闭，才继续执行下边的代码。很明显，另外一个协程必须写入 `ch`（不然代码就阻塞在 `for` 循环了），而且必须在写入完成后才关闭。`suck()` 函数可以这样写，且在协程中调用这个动作，程序变成了这样：\n\n示例 14.6-[channel_idiom2.go](examples/chapter_14/channel_idiom2.go)：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tsuck(pump())\n\ttime.Sleep(1e9)\n}\n\nfunc pump() chan int {\n\tch := make(chan int)\n\tgo func() {\n\t\tfor i := 0; ; i++ {\n\t\t\tch <- i\n\t\t}\n\t}()\n\treturn ch\n}\n\nfunc suck(ch chan int) {\n\tgo func() {\n\t\tfor v := range ch {\n\t\t\tfmt.Println(v)\n\t\t}\n\t}()\n}\n```\n\n**习惯用法：通道迭代器模式**\n\n这个模式用到了后边 [14.6 章](14.6.md)示例 [producer_consumer.go](exercises/chapter_14/producer_consumer.go) 的生产者-消费者模式。通常，需要从包含了地址索引字段 `items` 的容器给通道填入元素。为容器的类型定义一个方法 `Iter()`，返回一个只读的通道（参见第 [14.2.11](14.2.md#14211-通道的方向) 节）`items`，如下：\n\n```go\nfunc (c *container) Iter () <- chan item {\n\tch := make(chan item)\n\tgo func () {\n\t\tfor i:= 0; i < c.Len(); i++{\t// or use a for-range loop\n\t\t\tch <- c.items[i]\n\t\t}\n\t} ()\n\treturn ch\n}\n```\n\n在协程里，一个 `for` 循环迭代容器 `c` 中的元素（对于树或图的算法，这种简单的 `for` 循环可以替换为深度优先搜索）。\n\n调用这个方法的代码可以这样迭代容器：\n\n```go\nfor x := range container.Iter() { ... }\n```\n\n其运行在自己启动的协程中，所以上边的迭代用到了一个通道和两个协程（可能运行在不同的线程上）。 这样我们就有了一个典型的生产者-消费者模式。如果在程序结束之前，向通道写值的协程未完成工作，则这个协程不会被垃圾回收；这是设计使然。这种看起来并不符合预期的行为正是由通道这种线程安全的通信方式所导致的。如此一来，一个协程为了写入一个永远无人读取的通道而被挂起就成了一个 bug ，而并非你预想中的那样被悄悄回收掉 (garbage-collected) 了。\n\n**习惯用法：生产者消费者模式**\n\n假设你有 `Produce()` 函数来产生 `Consume()` 函数需要的值。它们都可以运行在独立的协程中，生产者在通道中放入给消费者读取的值。整个处理过程可以替换为无限循环：\n\n```go\nfor {\n\tConsume(Produce())\n}\n```\n\n## 14.2.11 通道的方向\n\n通道类型可以用注解来表示它只发送或者只接收：\n\n```go\nvar send_only chan<- int \t\t// channel can only receive data\nvar recv_only <-chan int\t\t// channel can only send data\n```\n\n只接收的通道 (`<-chan T`) 无法关闭，因为关闭通道是发送者用来表示不再给通道发送值了，所以对只接收通道是没有意义的。通道创建的时候都是双向的，但也可以分配给有方向的通道变量，就像以下代码：\n\n```go\nvar c = make(chan int) // bidirectional\ngo source(c)\ngo sink(c)\n\nfunc source(ch chan<- int){\n\tfor { ch <- 1 }\n}\n\nfunc sink(ch <-chan int) {\n\tfor { <-ch }\n}\n```\n\n**习惯用法：管道和选择器模式**\n\n更具体的例子还有协程处理它从通道接收的数据并发送给输出通道：\n\n```go\nsendChan := make(chan int)\nreceiveChan := make(chan string)\ngo processChannel(sendChan, receiveChan)\n\nfunc processChannel(in <-chan int, out chan<- string) {\n\tfor inValue := range in {\n\t\tresult := ... /// processing inValue\n\t\tout <- result\n\t}\n}\n```\n\n通过使用方向注解来限制协程对通道的操作。\n\n这里有一个来自 Go 指导的很赞的例子，打印了输出的素数，使用选择器（‘筛’）作为它的算法。每个 prime 都有一个选择器，如下图：\n\n![](images/14.2_fig14.2.png?raw=true)\n\n版本1：示例 14.7-[sieve1.go](examples/chapter_14/sieve1.go)\n\n```go\n// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.package main\npackage main\n\nimport \"fmt\"\n\n// Send the sequence 2, 3, 4, ... to channel 'ch'.\nfunc generate(ch chan int) {\n\tfor i := 2; ; i++ {\n\t\tch <- i // Send 'i' to channel 'ch'.\n\t}\n}\n\n// Copy the values from channel 'in' to channel 'out',\n// removing those divisible by 'prime'.\nfunc filter(in, out chan int, prime int) {\n\tfor {\n\t\ti := <-in // Receive value of new variable 'i' from 'in'.\n\t\tif i%prime != 0 {\n\t\t\tout <- i // Send 'i' to channel 'out'.\n\t\t}\n\t}\n}\n\n// The prime sieve: Daisy-chain filter processes together.\nfunc main() {\n\tch := make(chan int) // Create a new channel.\n\tgo generate(ch)      // Start generate() as a goroutine.\n\tfor {\n\t\tprime := <-ch\n\t\tfmt.Print(prime, \" \")\n\t\tch1 := make(chan int)\n\t\tgo filter(ch, ch1, prime)\n\t\tch = ch1\n\t}\n}\n```\n\n协程 `filter(in, out chan int, prime int)` 拷贝整数到输出通道，丢弃掉可以被 `prime` 整除的数字。然后每个 `prime` 又开启了一个新的协程，生成器和选择器并发请求。\n\n输出：\n\n```\n2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101\n103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223\n227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349\n353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479\n487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 601 607 613 617 619\n631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 739 743 751 757 761 769\n773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 881 883 887 907 911 919 929\n937 941 947 953 967 971 977 983 991 997 1009 1013...\n```\n\n第二个版本引入了上边的习惯用法：函数 `sieve()`、`generate()` 和 `filter()` 都是工厂；它们创建通道并返回，而且使用了协程的 lambda 函数。`main()` 函数现在短小清晰：它调用 `sieve()` 返回了包含素数的通道，然后通过 `fmt.Println(<-primes)` 打印出来。\n\n版本2：示例 14.8-[sieve2.go](examples/chapter_14/sieve2.go)\n\n```go\n// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport (\n\t\"fmt\"\n)\n\n// Send the sequence 2, 3, 4, ... to returned channel\nfunc generate() chan int {\n\tch := make(chan int)\n\tgo func() {\n\t\tfor i := 2; ; i++ {\n\t\t\tch <- i\n\t\t}\n\t}()\n\treturn ch\n}\n\n// Filter out input values divisible by 'prime', send rest to returned channel\nfunc filter(in chan int, prime int) chan int {\n\tout := make(chan int)\n\tgo func() {\n\t\tfor {\n\t\t\tif i := <-in; i%prime != 0 {\n\t\t\t\tout <- i\n\t\t\t}\n\t\t}\n\t}()\n\treturn out\n}\n\nfunc sieve() chan int {\n\tout := make(chan int)\n\tgo func() {\n\t\tch := generate()\n\t\tfor {\n\t\t\tprime := <-ch\n\t\t\tch = filter(ch, prime)\n\t\t\tout <- prime\n\t\t}\n\t}()\n\treturn out\n}\n\nfunc main() {\n\tprimes := sieve()\n\tfor {\n\t\tfmt.Println(<-primes)\n\t}\n}\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[并发、并行和协程](14.1.md)\n- 下一节：[协程同步：关闭通道-测试阻塞的通道](14.3.md)\n"
  },
  {
    "path": "eBook/14.3.md",
    "content": "# 14.3 协程的同步：关闭通道-测试阻塞的通道\n\n通道可以被显式的关闭；尽管它们和文件不同：不必每次都关闭。只有在当需要告诉接收者不会再提供新的值的时候，才需要关闭通道。只有发送者需要关闭通道，接收者永远不会需要。\n\n继续看示例 [goroutine2.go](examples/chapter_14/goroutine2.go)（示例 14.2）：我们如何在通道的 `sendData()` 完成的时候发送一个信号，`getData()` 又如何检测到通道是否关闭或阻塞？\n\n第一个可以通过函数 `close(ch)` 来完成：这个将通道标记为无法通过发送操作 `<-` 接受更多的值；给已经关闭的通道发送或者再次关闭都会导致运行时的 `panic()`。在创建一个通道后使用 `defer` 语句是个不错的办法（类似这种情况）：\n\n```go\nch := make(chan float64)\ndefer close(ch)\n```\n\n第二个问题可以使用逗号 ok 模式用来检测通道是否被关闭。\n\n如何来检测可以收到没有被阻塞（或者通道没有被关闭）？\n\n```go\nv, ok := <-ch   // ok is true if v received value\n```\n\n通常和 `if` 语句一起使用：\n\n```go\nif v, ok := <-ch; ok {\n  process(v)\n}\n```\n\n或者在 `for` 循环中接收的时候，当关闭的时候使用 `break`：\n\n```go\nv, ok := <-ch\nif !ok {\n  break\n}\nprocess(v)\n```\n\n而检测通道当前是否阻塞，需要使用 `select`（参见第 [14.4](14.4.md) 节）。\n\n```go\nselect {\ncase v, ok := <-ch:\n  if ok {\n    process(v)\n  } else {\n    fmt.Println(\"The channel is closed\")\n  }\ndefault:\n  fmt.Println(\"The channel is blocked\")\n}\n```\n\n在示例程序 14.2 中使用这些可以改进为版本 [goroutine3.go](examples/chapter_14/goroutine3.go)，输出相同。\n\n实现非阻塞通道的读取，需要使用 `select`（参见第 [14.4](14.4.md) 节）。\n\n示例 14.9-[goroutine3.go](examples/chapter_14/goroutine3.go)：\n\n```go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tch := make(chan string)\n\tgo sendData(ch)\n\tgetData(ch)\n}\n\nfunc sendData(ch chan string) {\n\tch <- \"Washington\"\n\tch <- \"Tripoli\"\n\tch <- \"London\"\n\tch <- \"Beijing\"\n\tch <- \"Tokio\"\n\tclose(ch)\n}\n\nfunc getData(ch chan string) {\n\tfor {\n\t\tinput, open := <-ch\n\t\tif !open {\n\t\t\tbreak\n\t\t}\n\t\tfmt.Printf(\"%s \", input)\n\t}\n}\n```\n\n改变了以下代码：\n\n- 现在只有 `sendData()` 是协程，`getData()` 和 `main()` 在同一个线程中：\n\n```go\ngo sendData(ch)\ngetData(ch)\n```\n\n- 在 `sendData()` 函数的最后，关闭了通道：\n\n```go\nfunc sendData(ch chan string) {\n\tch <- \"Washington\"\n\tch <- \"Tripoli\"\n\tch <- \"London\"\n\tch <- \"Beijing\"\n\tch <- \"Tokio\"\n\tclose(ch)\n}\n```\n\n- 在 `for` 循环的 `getData()` 中，在每次接收通道的数据之前都使用 `if !open` 来检测：\n\n```go\nfor {\n\t\tinput, open := <-ch\n\t\tif !open {\n\t\t\tbreak\n\t\t}\n\t\tfmt.Printf(\"%s \", input)\n\t}\n```\n\n使用 for-range 语句来读取通道是更好的办法，因为这会自动检测通道是否关闭：\n\n```go\nfor input := range ch {\n  \tprocess(input)\n}\n```\n\n阻塞和生产者-消费者模式：\n\n在[第 14.2.10 节](14.2.md)的通道迭代器中，两个协程经常是一个阻塞另外一个。如果程序工作在多核心的机器上，大部分时间只用到了一个处理器。可以通过使用带缓冲（缓冲空间大于 0）的通道来改善。比如，缓冲大小为 100，迭代器在阻塞之前，至少可以从容器获得 100 个元素。如果消费者协程在独立的内核运行，就有可能让协程不会出现阻塞。\n\n由于容器中元素的数量通常是已知的，需要让通道有足够的容量放置所有的元素。这样，迭代器就不会阻塞（尽管消费者协程仍然可能阻塞）。然而，这实际上加倍了迭代容器所需要的内存使用量，所以通道的容量需要限制一下最大值。记录运行时间和性能测试可以帮助你找到最小的缓存容量带来最好的性能。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[协程间的信道](14.2.md)\n- 下一节：[使用 select 切换协程](14.4.md)\n"
  },
  {
    "path": "eBook/14.4.md",
    "content": "# 14.4 使用 select 切换协程\n\n从不同的并发执行的协程中获取值可以通过关键字 `select` 来完成，它和 `switch` 控制语句非常相似（[章节 5.3](05.3.md)）也被称作通信开关；它的行为像是“你准备好了吗”的轮询机制；`select` 监听进入通道的数据，也可以是用通道发送值的时候。\n\n```go\nselect {\ncase u:= <- ch1:\n        ...\ncase v:= <- ch2:\n        ...\n        ...\ndefault: // no value ready to be received\n        ...\n}\n```\n\n`default` 语句是可选的；`fallthrough` 行为，和普通的 `switch` 相似，是不允许的。在任何一个 `case` 中执行 `break` 或者 `return`，select 就结束了。\n\n`select` 做的就是：选择处理列出的多个通信情况中的一个。\n\n- 如果都阻塞了，会等待直到其中一个可以处理\n- 如果多个可以处理，随机选择一个\n- 如果没有通道操作可以处理并且写了 `default` 语句，它就会执行：`default` 永远是可运行的（这就是准备好了，可以执行）。\n\n在 `select` 中使用发送操作并且有 `default` 可以确保发送不被阻塞！如果没有 `default`，`select` 就会一直阻塞。\n\n`select` 语句实现了一种监听模式，通常用在（无限）循环中；在某种情况下，通过 `break` 语句使循环退出。\n\n在程序 [goroutine_select.go](examples/chapter_14/goroutine_select.go) 中有 2 个通道 `ch1` 和 `ch2`，三个协程 `pump1()`、`pump2()` 和 `suck()`。这是一个典型的生产者消费者模式。在无限循环中，`ch1` 和 `ch2` 通过 `pump1()` 和 `pump2()` 填充整数；`suck()` 也是在无限循环中轮询输入的，通过 `select` 语句获取 `ch1` 和 `ch2` 的整数并输出。选择哪一个 `case` 取决于哪一个通道收到了信息。程序在 `main` 执行 1 秒后结束。\n\n示例 14.10-[goroutine_select.go](examples/chapter_14/goroutine_select.go)：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tch1 := make(chan int)\n\tch2 := make(chan int)\n\n\tgo pump1(ch1)\n\tgo pump2(ch2)\n\tgo suck(ch1, ch2)\n\n\ttime.Sleep(1e9)\n}\n\nfunc pump1(ch chan int) {\n\tfor i := 0; ; i++ {\n\t\tch <- i * 2\n\t}\n}\n\nfunc pump2(ch chan int) {\n\tfor i := 0; ; i++ {\n\t\tch <- i + 5\n\t}\n}\n\nfunc suck(ch1, ch2 chan int) {\n\tfor {\n\t\tselect {\n\t\tcase v := <-ch1:\n\t\t\tfmt.Printf(\"Received on channel 1: %d\\n\", v)\n\t\tcase v := <-ch2:\n\t\t\tfmt.Printf(\"Received on channel 2: %d\\n\", v)\n\t\t}\n\t}\n}\n```\n\n输出：\n\n```\nReceived on channel 2: 5\nReceived on channel 2: 6\nReceived on channel 1: 0\nReceived on channel 2: 7\nReceived on channel 2: 8\nReceived on channel 2: 9\nReceived on channel 2: 10\nReceived on channel 1: 2\nReceived on channel 2: 11\n...\nReceived on channel 2: 47404\nReceived on channel 1: 94346\nReceived on channel 1: 94348\n```\n\n一秒内的输出非常惊人，如果我们给它计数 ([goroutine_select2.go](examples/chapter_14/goroutine_select2.go))，得到了 90000 个左右的数字。\n\n## 练习：\n\n练习 14.7：\n\n- a）在练习 5.4 的 [for_loop.go](exercises/chapter_5/for_loop.go) 中，有一个常见的 `for` 循环打印数字。在函数 `tel()` 中实现一个 `for` 循环，用协程开始这个函数并在其中给通道发送数字。`main()` 线程从通道中获取并打印。不要使用 `time.Sleep()` 来同步：[goroutine_panic.go](exercises/chapter_14/goroutine_panic.go)\n- b）也许你的方案有效，但可能会引发运行时的 `panic()`：`throw:all goroutines are asleep-deadlock!` 为什么会这样？你如何解决这个问题？[goroutine_close.go](exercises/chapter_14/goroutine_close.go)\n- c）解决 a）的另外一种方式：使用一个额外的通道传递给协程，然后在结束的时候随便放点什么进去。`main()` 线程检查是否有数据发送给了这个通道，如果有就停止：[goroutine_select.go](exercises/chapter_14/goroutine_select.go)\n\n\n练习 14.8：\n\n从示例 [6.13 fibonacci.go](examples/chapter_6/fibonacci.go) 的斐波那契程序开始，制定解决方案，使斐波那契周期计算独立到协程中，并可以把结果发送给通道。\n\n结束的时候关闭通道。`main()` 函数读取通道并打印结果：[goFibonacci.go](exercises/chapter_14/gofibonacci.go)\n\n使用练习 [6.9 fibonacci2.go](exercises/chapter_6/fibonacci2.go) 中的算法写一个更短的 [gofibonacci2.go](exercises/chapter_14/gofibonacci2.go)\n\n使用 `select` 语句来写，并让通道退出 ([gofibonacci_select.go](exercises/chapter_14/gofibonacci_select.go))\n\n注意：当给结果计时并和 6.13 对比时，我们发现使用通道通信的性能开销有轻微削减；这个例子中的算法使用协程并非性能最好的选择；但是 [gofibonacci3](exercises/chapter_14/gofibonacci3.go) 方案使用了 2 个协程带来了 3 倍的提速。\n\n\n练习 14.9：\n\n做一个随机位生成器，程序可以提供无限的随机 0 或者 1 的序列：[random_bitgen.go](exercises/chapter_14/random_bitgen.go)\n\n练习 14.10：[polar_to_cartesian.go](exercises/chapter_14/polar_to_cartesian.go)\n\n（这是一种综合练习，使用到第 4、9、11 章和本章的内容。）写一个可交互的控制台程序，要求用户输入二位平面极坐标上的点（半径和角度（度））。计算对应的笛卡尔坐标系的点的 `x` 和 `y` 并输出。使用极坐标和笛卡尔坐标的结构体。\n\n使用通道和协程：\n\n- `channel1` 用来接收极坐标\n- `channel2` 用来接收笛卡尔坐标\n\n转换过程需要在协程中进行，从 `channel1` 中读取然后发送到 `channel2`。实际上做这种计算不提倡使用协程和通道，但是如果运算量很大很耗时，这种方案设计就非常合适了。\n\n练习 14.11： [concurrent_pi.go](exercises/chapter_14/concurrent_pi.go) / [concurrent_pi2.go](exercises/chapter_14/concurrent_pi2.go)\n\n使用以下序列在协程中计算 pi：开启一个协程来计算公式中的每一项并将结果放入通道，`main()` 函数收集并累加结果，打印出 pi 的近似值。\n\n![](images/14.4_piseries.png?raw=true)\n\n计算执行时间（参见第 [6.11](6.11.md) 节）\n\n再次声明这只是为了一边练习协程的概念一边找点乐子。\n\n如果你需要的话可使用 `math.pi` 中的 `Pi`；而且不使用协程会运算的更快。一个急速版本：使用 `GOMAXPROCS`，开启和 `GOMAXPROCS` 同样多个协程。\n\n**习惯用法：后台服务模式**\n\n服务通常是是用后台协程中的无限循环实现的，在循环中使用 `select` 获取并处理通道中的数据：\n\n```go\n// Backend goroutine.\nfunc backend() {\n\tfor {\n\t\tselect {\n\t\tcase cmd := <-ch1:\n\t\t\t// Handle ...\n\t\tcase cmd := <-ch2:\n\t\t\t...\n\t\tcase cmd := <-chStop:\n\t\t\t// stop server\n\t\t}\n\t}\n}\n```\n\n在程序的其他地方给通道 `ch1`，`ch2` 发送数据，比如：通道 `stop` 用来清理结束服务程序。\n\n另一种方式（但是不太灵活）就是（客户端）在 `chRequest` 上提交请求，后台协程循环这个通道，使用 `switch` 根据请求的行为来分别处理：\n\n```go\nfunc backend() {\n\tfor req := range chRequest {\n\t\tswitch req.Subjext() {\n\t\t\tcase A1:  // Handle case ...\n\t\t\tcase A2:  // Handle case ...\n\t\t\tdefault:\n\t\t\t  // Handle illegal request ..\n\t\t\t  // ...\n\t\t}\n\t}\n}\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[通道的同步：关闭通道-测试阻塞的通道](14.3.md)\n- 下一节：[通道，超时和计时器（Ticker）](14.5.md)\n"
  },
  {
    "path": "eBook/14.5.md",
    "content": "# 14.5 通道、超时和计时器（Ticker）\n\n`time` 包中有一些有趣的功能可以和通道组合使用。\n\n其中就包含了 `time.Ticker` 结构体，这个对象以指定的时间间隔重复的向通道 `C` 发送时间值：\n\n```go\ntype Ticker struct {\n    C <-chan Time // the channel on which the ticks are delivered.\n    // contains filtered or unexported fields\n    ...\n}\n```\n\n时间间隔的单位是 `ns`（纳秒，`int64`），在工厂函数 `time.NewTicker` 中以 `Duration` 类型的参数传入：`func NewTicker(dur) *Ticker`。\n\n在协程周期性的执行一些事情（打印状态日志，输出，计算等等）的时候非常有用。\n\n调用 `Stop()` 使计时器停止，在 `defer` 语句中使用。这些都很好地适应 `select` 语句:\n\n```go\nticker := time.NewTicker(updateInterval)\ndefer ticker.Stop()\n...\nselect {\ncase u:= <-ch1:\n    ...\ncase v:= <-ch2:\n    ...\ncase <-ticker.C:\n    logState(status) // call some logging function logState\ndefault: // no value ready to be received\n    ...\n}\n```\n\n`time.Tick()` 函数声明为 `Tick(d Duration) <-chan Time`，当你想返回一个通道而不必关闭它的时候这个函数非常有用：它以 `d` 为周期给返回的通道发送时间，`d` 是纳秒数。如果需要，像下边的代码一样，可以限制处理频率（函数 `client.Call()` 是一个 RPC 调用，这里暂不赘述（参见第 [15.9](15.9.md) 节））：\n\n```go\nimport \"time\"\n\nrate_per_sec := 10\nvar dur Duration = 1e9 / rate_per_sec\nchRate := time.Tick(dur) // a tick every 1/10th of a second\nfor req := range requests {\n    <- chRate // rate limit our Service.Method RPC calls\n    go client.Call(\"Service.Method\", req, ...)\n}\n```\n\n这样只会按照指定频率处理请求：`chRate` 阻塞了更高的频率。每秒处理的频率可以根据机器负载（和/或）资源的情况而增加或减少。\n\n问题 14.1：扩展上边的代码，思考如何承载周期请求数的暴增（提示：使用带缓冲通道和计时器对象）。\n\n定时器 (`Timer`)  结构体看上去和计时器 (`Ticker`) 结构体的确很像（构造为 `NewTimer(d Duration)`），但是它只发送一次时间，在 `Dration d` 之后。\n\n还有 `time.After(d)` 函数，声明如下：\n\n```go\nfunc After(d Duration) <-chan Time\n```\n\n在 `Duration d` 之后，当前时间被发到返回的通道；所以它和 `NewTimer(d).C` 是等价的；它类似 `Tick()`，但是 `After()` 只发送一次时间。下边有个很具体的示例，很好的阐明了 `select` 中 `default` 的作用：\n\n示例 14.11：[timer_goroutine.go](examples/chapter_14/timer_goroutine.go)：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\ttick := time.Tick(1e8)\n\tboom := time.After(5e8)\n\tfor {\n\t\tselect {\n\t\tcase <-tick:\n\t\t\tfmt.Println(\"tick.\")\n\t\tcase <-boom:\n\t\t\tfmt.Println(\"BOOM!\")\n\t\t\treturn\n\t\tdefault:\n\t\t\tfmt.Println(\"    .\")\n\t\t\ttime.Sleep(5e7)\n\t\t}\n\t}\n}\n```\n\n输出：\n\n```\n    .\n    .\ntick.\n    .\n    .\ntick.\n    .\n    .\ntick.\n    .\n    .\ntick.\n    .\n    .\ntick.\nBOOM!\n```\n\n**习惯用法：简单超时模式**\n\n要从通道 `ch` 中接收数据，但是最多等待 1 秒。先创建一个信号通道，然后启动一个 `lambda` 协程，协程在给通道发送数据之前是休眠的：\n\n```go\ntimeout := make(chan bool, 1)\ngo func() {\n        time.Sleep(1e9) // one second\n        timeout <- true\n}()\n```\n\n然后使用 `select` 语句接收 `ch` 或者 `timeout` 的数据：如果 `ch` 在 1 秒内没有收到数据，就选择到了 `time` 分支并放弃了 `ch` 的读取。\n\n```go\nselect {\n    case <-ch:\n        // a read from ch has occured\n    case <-timeout:\n        // the read from ch has timed out\n        break\n}\n```\n\n第二种形式：取消耗时很长的同步调用\n\n也可以使用 `time.After()` 函数替换 `timeout-channel`。可以在 `select` 中通过 `time.After()` 发送的超时信号来停止协程的执行。以下代码，在 `timeoutNs` 纳秒后执行 `select` 的 `timeout` 分支后，执行 `client.Call` 的协程也随之结束，不会给通道 `ch` 返回值：\n\n```go\nch := make(chan error, 1)\ngo func() { ch <- client.Call(\"Service.Method\", args, &reply) } ()\nselect {\ncase resp := <-ch\n    // use resp and reply\ncase <-time.After(timeoutNs):\n    // call timed out\n    break\n}\n```\n\n注意缓冲大小设置为 `1` 是必要的，可以避免协程死锁以及确保超时的通道可以被垃圾回收。此外，需要注意在有多个 `case` 符合条件时， `select` 对 `case` 的选择是伪随机的，如果上面的代码稍作修改如下，则 `select` 语句可能不会在定时器超时信号到来时立刻选中 `time.After(timeoutNs)` 对应的 `case`，因此协程可能不会严格按照定时器设置的时间结束。\n\n```go\nch := make(chan int, 1)\ngo func() { for { ch <- 1 } } ()\nL:\nfor {\n    select {\n    case <-ch:\n        // do something\n    case <-time.After(timeoutNs):\n        // call timed out\n        break L\n    }\n}\n```\n\n第三种形式：假设程序从多个复制的数据库同时读取。只需要一个答案，需要接收首先到达的答案，`Query` 函数获取数据库的连接切片并请求。并行请求每一个数据库并返回收到的第一个响应：\n\n```go\nfunc Query(conns []Conn, query string) Result {\n    ch := make(chan Result, 1)\n    for _, conn := range conns {\n        go func(c Conn) {\n            select {\n            case ch <- c.DoQuery(query):\n            default:\n            }\n        }(conn)\n    }\n    return <- ch\n}\n```\n\n再次声明，结果通道 `ch` 必须是带缓冲的：以保证第一个发送进来的数据有地方可以存放，确保放入的首个数据总会成功，所以第一个到达的值会被获取而与执行的顺序无关。正在执行的协程可以总是可以使用 `runtime.Goexit()` 来停止。\n\n\n在应用中缓存数据：\n\n应用程序中用到了来自数据库（或者常见的数据存储）的数据时，经常会把数据缓存到内存中，因为从数据库中获取数据的操作代价很高；如果数据库中的值不发生变化就没有问题。但是如果值有变化，我们需要一个机制来周期性的从数据库重新读取这些值：缓存的值就不可用（过期）了，而且我们也不希望用户看到陈旧的数据。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[使用select切换协程](14.4.md)\n- 下一节：[协程和恢复（recover）](14.6.md)\n"
  },
  {
    "path": "eBook/14.6.md",
    "content": "# 14.6 协程和恢复 (recover)\n\n一个用到 `recover()` 的程序（参见[第 13.3 节](13.3.md)停掉了服务器内部一个失败的协程而不影响其他协程的工作。\n\n```go\nfunc server(workChan <-chan *Work) {\n    for work := range workChan {\n        go safelyDo(work)   // start the goroutine for that work\n    }\n}\n\nfunc safelyDo(work *Work) {\n    defer func() {\n        if err := recover(); err != nil {\n            log.Printf(\"Work failed with %s in %v\", err, work)\n        }\n    }()\n    do(work)\n}\n```\n\n上边的代码，如果 `do(work)` 发生 `panic()`，错误会被记录且协程会退出并释放，而其他协程不受影响。\n\n因为 `recover()` 总是返回 `nil`，除非直接在 `defer` 修饰的函数中调用，`defer` 修饰的代码可以调用那些自身可以使用 `panic()` 和 `recover()` 避免失败的库例程（库函数）。举例，`safelyDo()` 中 `defer` 修饰的函数可能在调用 `recover()` 之前就调用了一个 `logging()` 函数，panicking 状态不会影响 `logging()` 代码的运行。因为加入了恢复模式，函数 `do()`（以及它调用的任何东西）可以通过调用 `panic()` 来摆脱不好的情况。但是恢复是在 panicking 的协程内部的：不能被另外一个协程恢复。\n\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[通道，超时和计时器](14.5.md)\n- 下一节：[对比新旧模型：任务和工作](14.7.md)\n"
  },
  {
    "path": "eBook/14.7.md",
    "content": "# 14.7 新旧模型对比：任务和 worker\n\n假设我们需要处理很多任务；一个 worker 处理一项任务。任务可以被定义为一个结构体（具体的细节在这里并不重要）：\n\n```go\ntype Task struct {\n    // some state\n}\n```\n\n旧模式：使用共享内存进行同步\n\n由各个任务组成的任务池共享内存；为了同步各个 worker 以及避免资源竞争，我们需要对任务池进行加锁保护：\n\n```go\n    type Pool struct {\n        Mu      sync.Mutex\n        Tasks   []*Task\n    }\n```\n`sync.Mutex`（[参见9.3](09.3.md)）是互斥锁：它用来在代码中保护临界区资源：同一时间只有一个 go 协程 (goroutine) 可以进入该临界区。如果出现了同一时间多个 go 协程都进入了该临界区，则会产生竞争：`Pool` 结构就不能保证被正确更新。在传统的模式中（经典的面向对象的语言中应用得比较多，比如 C++，JAVA，C#），worker 代码可能这样写：\n\n```go\nfunc Worker(pool *Pool) {\n    for {\n        pool.Mu.Lock()\n        // begin critical section:\n        task := pool.Tasks[0]        // take the first task\n        pool.Tasks = pool.Tasks[1:]  // update the pool of tasks\n        // end critical section\n        pool.Mu.Unlock()\n        process(task)\n    }\n}\n```\n\n这些 worker 有许多都可以并发执行；他们可以在 go 协程中启动。一个 worker 先将 `pool` 锁定，从 `pool` 获取第一项任务，再解锁和处理任务。加锁保证了同一时间只有一个 go 协程可以进入到 `pool` 中：一项任务有且只能被赋予一个 worker 。如果不加锁，则工作协程可能会在 `task:=pool.Tasks[0]` 发生切换，导致 `pool.Tasks=pool.Tasks[1:]` 结果异常：一些 worker 获取不到任务，而一些任务可能被多个 worker 得到。加锁实现同步的方式在工作协程比较少时可以工作得很好，但是当工作协程数量很大，任务量也很多时，处理效率将会因为频繁的加锁/解锁开销而降低。当工作协程数增加到一个阈值时，程序效率会急剧下降，这就成为了瓶颈。\n\n新模式：使用通道\n\n使用通道进行同步：使用一个通道接受需要处理的任务，一个通道接受处理完成的任务（及其结果）。worker 在协程中启动，其数量 `N` 应该根据任务数量进行调整。\n\n主线程扮演着 Master 节点角色，可能写成如下形式：\n\n```go\n    func main() {\n        pending, done := make(chan *Task), make(chan *Task)\n        go sendWork(pending)       // put tasks with work on the channel\n        for i := 0; i < N; i++ {   // start N goroutines to do work\n            go Worker(pending, done)\n        }\n        consumeWork(done)          // continue with the processed tasks\n    }\n```\n\nworker 的逻辑比较简单：从 `pending` 通道拿任务，处理后将其放到 `done` 通道中：\n\n```go\n    func Worker(in, out chan *Task) {\n        for {\n            t := <-in\n            process(t)\n            out <- t\n        }\n    }\n```\n\n这里并不使用锁：从通道得到新任务的过程没有任何竞争。随着任务数量增加，worker 数量也应该相应增加，同时性能并不会像第一种方式那样下降明显。在 `pending` 通道中存在一份任务的拷贝，第一个 worker 从 `pending` 通道中获得第一个任务并进行处理，这里并不存在竞争（对一个通道读数据和写数据的整个过程是原子性的：参见 [14.2.2](14.2.md)）。某一个任务会在哪一个 worker 中被执行是不可知的，反过来也是。worker 数量的增多也会增加通信的开销，这会对性能有轻微的影响。\n\n从这个简单的例子中可能很难看出第二种模式的优势，但含有复杂锁运用的程序不仅在编写上显得困难，也不容易编写正确，使用第二种模式的话，就无需考虑这么复杂的东西了。\n\n因此，第二种模式对比第一种模式而言，不仅性能是一个主要优势，而且还有个更大的优势：代码显得更清晰、更优雅。一个更符合 go 语言习惯的 worker 写法：\n\n**IDIOM: Use an in- and out-channel instead of locking**\n\n```go\n    func Worker(in, out chan *Task) {\n        for {\n            t := <-in\n            process(t)\n            out <- t\n        }\n    }\n```\n\n对于任何可以建模为 Master-Worker 范例的问题，一个类似于 worker 使用通道进行通信和交互、Master 进行整体协调的方案都能完美解决。如果系统部署在多台机器上，各个机器上执行 Worker 协程，Master 和 Worker 之间使用 netchan 或者 RPC 进行通信（参见 [15 章](15.0.md)）。\n\n怎么选择是该使用锁还是通道？\n\n通道是一个较新的概念，本节我们着重强调了在 go 协程里通道的使用，但这并不意味着经典的锁方法就不能使用。go 语言让你可以根据实际问题进行选择：创建一个优雅、简单、可读性强、在大多数场景性能表现都能很好的方案。如果你的问题适合使用锁，也不要忌讳使用它。go 语言注重实用，什么方式最能解决你的问题就用什么方式，而不是强迫你使用一种编码风格。下面列出一个普遍的经验法则：\n\n* 使用锁的情景：\n    - 访问共享数据结构中的缓存信息\n    - 保存应用程序上下文和状态信息数据\n  \n* 使用通道的情景：\n    - 与异步操作的结果进行交互\n    - 分发任务\n    - 传递数据所有权\n\n当你发现你的锁使用规则变得很复杂时，可以反省使用通道会不会使问题变得简单些。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[协程和恢复（recover）](14.6.md)\n- 下一节：[惰性生成器实现](14.8.md)\n"
  },
  {
    "path": "eBook/14.8.md",
    "content": "# 14.8 惰性生成器的实现\n\n生成器是指当被调用时返回一个序列中下一个值的函数，例如：\n\n```go\n    generateInteger() => 0\n    generateInteger() => 1\n    generateInteger() => 2\n    ....\n```\n\n生成器每次返回的是序列中下一个值而非整个序列；这种特性也称之为惰性求值：只在你需要时进行求值，同时保留相关变量资源（内存和 CPU）：这是一项在需要时对表达式进行求值的技术。例如，生成一个无限数量的偶数序列：要产生这样一个序列并且在一个一个的使用可能会很困难，而且内存会溢出！但是一个含有通道和 go 协程的函数能轻易实现这个需求。\n\n在 14.12 的例子中，我们实现了一个使用 `int` 型通道来实现的生成器。通道被命名为 `yield` 和 `resume` ，这些词经常在协程代码中使用。\n\n示例 14.12 [lazy_evaluation.go](examples/chapter_14/lazy_evaluation.go)：\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n)\n\nvar resume chan int\n\nfunc integers() chan int {\n    yield := make(chan int)\n    count := 0\n    go func() {\n        for {\n            yield <- count\n            count++\n        }\n    }()\n    return yield\n}\n\nfunc generateInteger() int {\n    return <-resume\n}\n\nfunc main() {\n    resume = integers()\n    fmt.Println(generateInteger())  //=> 0\n    fmt.Println(generateInteger())  //=> 1\n    fmt.Println(generateInteger())  //=> 2    \n}\n```\n\n有一个细微的区别是从通道读取的值可能会是稍早前产生的，并不是在程序被调用时生成的。如果确实需要这样的行为，就得实现一个请求响应机制。当生成器生成数据的过程是计算密集型且各个结果的顺序并不重要时，那么就可以将生成器放入到 go 协程实现并行化。但是得小心，使用大量的 go 协程的开销可能会超过带来的性能增益。\n\n这些原则可以概括为：通过巧妙地使用空接口、闭包和高阶函数，我们能实现一个通用的惰性生产器的工厂函数 `BuildLazyEvaluator`（这个应该放在一个工具包中实现）。工厂函数需要一个函数和一个初始状态作为输入参数，返回一个无参、返回值是生成序列的函数。传入的函数需要计算出下一个返回值以及下一个状态参数。在工厂函数中，创建一个通道和无限循环的 go 协程。返回值被放到了该通道中，返回函数稍后被调用时从该通道中取得该返回值。每当取得一个值时，下一个值即被计算。在下面的例子中，定义了一个 `evenFunc` 函数，其是一个惰性生成函数：在 `main()` 函数中，我们创建了前 10 个偶数，每个都是通过调用 `even()` 函数取得下一个值的。为此，我们需要在 `BuildLazyIntEvaluator` 函数中具体化我们的生成函数，然后我们能够基于此做出定义。\n\n示例 14.13 [general_lazy_evalution1.go](examples/chapter_14/general_lazy_evalution1.go)：\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n)\n\ntype Any interface{}\ntype EvalFunc func(Any) (Any, Any)\n\nfunc main() {\n    evenFunc := func(state Any) (Any, Any) {\n        os := state.(int)\n        ns := os + 2\n        return os, ns\n    }\n    \n    even := BuildLazyIntEvaluator(evenFunc, 0)\n    \n    for i := 0; i < 10; i++ {\n        fmt.Printf(\"%vth even: %v\\n\", i, even())\n    }\n}\n\nfunc BuildLazyEvaluator(evalFunc EvalFunc, initState Any) func() Any {\n    retValChan := make(chan Any)\n    loopFunc := func() {\n        var actState Any = initState\n        var retVal Any\n        for {\n            retVal, actState = evalFunc(actState)\n            retValChan <- retVal\n        }\n    }\n    retFunc := func() Any {\n        return <- retValChan\n    }\n    go loopFunc()\n    return retFunc\n}\n\nfunc BuildLazyIntEvaluator(evalFunc EvalFunc, initState Any) func() int {\n    ef := BuildLazyEvaluator(evalFunc, initState)\n    return func() int {\n        return ef().(int)\n    }\n}\n```\n\n输出：\n```go\n0th even: 0\n1th even: 2\n2th even: 4\n3th even: 6\n4th even: 8\n5th even: 10\n6th even: 12\n7th even: 14\n8th even: 16\n9th even: 18\n```\n\n练习14.12：[general_lazy_evaluation2.go](exercises/chapter_14/general_lazy_evalution2.go)\n通过使用 14.12 中工厂函数生成前 10 个斐波那契数\n\n提示：因为斐波那契数增长很迅速，使用 `uint64` 类型。\n注：这种计算通常被定义为递归函数，但是在没有尾递归的语言中，例如 go 语言，这可能会导致栈溢出，但随着 go 语言中堆栈可扩展的优化，这个问题就不那么严重。这里的诀窍是使用了惰性求值。gccgo 编译器在某些情况下会实现尾递归。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[新旧模型对比：任务和worker](14.7.md)\n- 下一节：[实现 Futures 模式](14.9.md)\n"
  },
  {
    "path": "eBook/14.9.md",
    "content": "# 14.9 实现 Futures 模式\n\n所谓 Futures 就是指：有时候在你使用某一个值之前需要先对其进行计算。这种情况下，你就可以在另一个处理器上进行该值的计算，到使用时，该值就已经计算完毕了。\n\nFutures 模式通过闭包和通道可以很容易实现，类似于生成器，不同地方在于 Futures 需要返回一个值。\n\n参考条目文献给出了一个很精彩的例子：假设我们有一个矩阵类型，我们需要计算两个矩阵 A 和 B 乘积的逆，首先我们通过函数 `Inverse(M)` 分别对其进行求逆运算，再将结果相乘。如下函数 `InverseProduct()` 实现了如上过程：\n\n```go\nfunc InverseProduct(a Matrix, b Matrix) {\n    a_inv := Inverse(a)\n    b_inv := Inverse(b)\n    return Product(a_inv, b_inv)\n}\n```\n\n在这个例子中，`a` 和 `b` 的求逆矩阵需要先被计算。那么为什么在计算 `b` 的逆矩阵时，需要等待 `a` 的逆计算完成呢？显然不必要，这两个求逆运算其实可以并行执行的。换句话说，调用 `Product()` 函数只需要等到 `a_inv` 和 `b_inv` 的计算完成。如下代码实现了并行计算方式：\n\n```go\nfunc InverseProduct(a Matrix, b Matrix) {\n    a_inv_future := InverseFuture(a)   // start as a goroutine\n    b_inv_future := InverseFuture(b)   // start as a goroutine\n    a_inv := <-a_inv_future\n    b_inv := <-b_inv_future\n    return Product(a_inv, b_inv)\n}\n```\n\n`InverseFuture()` 函数以 `goroutine` 的形式起了一个闭包，该闭包会将矩阵求逆结果放入到 `future` 通道中：\n\n```go\nfunc InverseFuture(a Matrix) chan Matrix {\n    future := make(chan Matrix)\n    go func() {\n        future <- Inverse(a)\n    }()\n    return future\n}\n```\n\n当开发一个计算密集型库时，使用 Futures 模式设计 API 接口是很有意义的。在你的包使用 Futures 模式，且能保持友好的 API 接口。此外，Futures 可以通过一个异步的 API 暴露出来。这样你可以以最小的成本将包中的并行计算移到用户代码中。（参见参考文件 18：[http://www.golangpatterns.info/concurrency/futures](http://www.golangpatterns.info/concurrency/futures)）\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[惰性生成器的实现](14.8.md)\n- 下一节：[复用](14.10.md)\n"
  },
  {
    "path": "eBook/15.0.md",
    "content": "# 15.0 网络、模板与网页应用\n\nGo 在编写 web 应用方面非常得力。因为目前它还没有 GUI（Graphic User Interface 即图形化用户界面）的框架，通过文本或者模板展现的 html 页面是目前 Go 编写界面应用程序的唯一方式。（**译者注：实际上在翻译的时候，已经有了一些不太成熟的 GUI 库，例如：go ui 。**）\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[使用通道并发访问对象](14.17.md)\n- 下一节：[tcp 服务器](15.1.md)\n\n"
  },
  {
    "path": "eBook/15.1.md",
    "content": "# 15.1 tcp 服务器\n\n这部分我们将使用 TCP 协议和在 14 章讲到的协程范式编写一个简单的客户端-服务器应用，一个 (web) 服务器应用需要响应众多客户端的并发请求：Go 会为每一个客户端产生一个协程用来处理请求。我们需要使用 net 包中网络通信的功能。它包含了处理 TCP/IP 以及 UDP 协议、域名解析等方法。\n\n服务器端代码是一个单独的文件：\n\n示例 15.1 [server.go](examples/chapter_15/server.go)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net\"\n)\n\nfunc main() {\n\tfmt.Println(\"Starting the server ...\")\n\t// 创建 listener\n\tlistener, err := net.Listen(\"tcp\", \"localhost:50000\")\n\tif err != nil {\n\t\tfmt.Println(\"Error listening\", err.Error())\n\t\treturn //终止程序\n\t}\n\t// 监听并接受来自客户端的连接\n\tfor {\n\t\tconn, err := listener.Accept()\n\t\tif err != nil {\n\t\t\tfmt.Println(\"Error accepting\", err.Error())\n\t\t\treturn // 终止程序\n\t\t}\n\t\tgo doServerStuff(conn)\n\t}\n}\n\nfunc doServerStuff(conn net.Conn) {\n\tfor {\n\t\tbuf := make([]byte, 512)\n\t\tlen, err := conn.Read(buf)\n\t\tif err != nil {\n\t\t\tfmt.Println(\"Error reading\", err.Error())\n\t\t\treturn //终止程序\n\t\t}\n\t\tfmt.Printf(\"Received data: %v\", string(buf[:len]))\n\t}\n}\n```\n\n在 `main()` 中创建了一个 `net.Listener` 类型的变量 `listener`，他实现了服务器的基本功能：用来监听和接收来自客户端的请求（基于 TCP 协议下，位于 IP 地址为 127.0.0.1、端口为 50000 的 localhost）。`Listen()` 函数可以返回一个 `error` 类型的错误变量。用一个无限 `for` 循环的 `listener.Accept()` 来等待客户端的请求。客户端的请求将产生一个 `net.Conn` 类型的连接变量。然后一个独立的协程使用这个连接执行 `doServerStuff()`，开始使用一个 512 字节的缓冲 `data` 来读取客户端发送来的数据，并且把它们打印到服务器的终端，`len()` 获取客户端发送的数据字节数；当客户端发送的所有数据都被读取完成时，协程就结束了。这段程序会为每一个客户端连接创建一个独立的协程。必须先运行服务器代码，再运行客户端代码。\n\n客户端代码写在另一个文件 client.go 中：\n\n示例 15.2 [client.go](examples/chapter_15/client.go)\n\n```go\npackage main\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"strings\"\n)\n\nfunc main() {\n\t//打开连接:\n\tconn, err := net.Dial(\"tcp\", \"localhost:50000\")\n\tif err != nil {\n\t\t//由于目标计算机积极拒绝而无法创建连接\n\t\tfmt.Println(\"Error dialing\", err.Error())\n\t\treturn // 终止程序\n\t}\n\n\tinputReader := bufio.NewReader(os.Stdin)\n\tfmt.Println(\"First, what is your name?\")\n\tclientName, _ := inputReader.ReadString('\\n')\n\t// fmt.Printf(\"CLIENTNAME %s\", clientName)\n\ttrimmedClient := strings.Trim(clientName, \"\\r\\n\") // Windows 平台下用 \"\\r\\n\"，Linux平台下使用 \"\\n\"\n\t// 给服务器发送信息直到程序退出：\n\tfor {\n\t\tfmt.Println(\"What to send to the server? Type Q to quit.\")\n\t\tinput, _ := inputReader.ReadString('\\n')\n\t\ttrimmedInput := strings.Trim(input, \"\\r\\n\")\n\t\t// fmt.Printf(\"input:--%s--\", input)\n\t\t// fmt.Printf(\"trimmedInput:--%s--\", trimmedInput)\n\t\tif trimmedInput == \"Q\" {\n\t\t\treturn\n\t\t}\n\t\t_, err = conn.Write([]byte(trimmedClient + \" says: \" + trimmedInput))\n\t}\n}\n```\n客户端通过 `net.Dial()` 创建了一个和服务器之间的连接。\n\n它通过无限循环从 `os.Stdin` 接收来自键盘的输入，直到输入了“Q”。注意裁剪 `\\r` 和 `\\n` 字符（仅 Windows 平台需要）。裁剪后的输入被 `connection` 的 `Write()` 方法发送到服务器。\n\n当然，服务器必须先启动好，如果服务器并未开始监听，客户端是无法成功连接的。\n\n如果在服务器没有开始监听的情况下运行客户端程序，客户端会停止并打印出以下错误信息：\n\n```\ndial tcp [::1]:xxxx: connectex: No connection could be made because the target machine actively refused it.\n```\n\n打开命令提示符并转到服务器和客户端代码所在的目录，输入 `go run server.go`，接下来控制台出现以下信息：`Starting the server ...`\n\n在 Windows 系统中，我们可以通过 CTRL+C 停止程序。\n\n然后开启 2 个或者 3 个独立的控制台窗口，分别启动客户端程序\n\n以下是服务器的输出：\n\n```\nStarting the Server ...\nReceived data: IVO says: Hi Server, what's up ?\nReceived data: CHRIS says: Are you busy server ?\nReceived data: MARC says: Don't forget our appointment tomorrow !\n```\n当客户端输入“Q”并结束程序时，服务器会输出以下信息：\n\n```\nError reading WSARecv tcp 127.0.0.1:50000: The specified network name is no longer available.\n```\n在网络编程中 `net.Dial()` 函数是非常重要的，一旦你连接到远程系统，函数就会返回一个 `Conn` 类型的接口，我们可以用它发送和接收数据。`Dial()` 函数简洁地抽象了网络层和传输层。所以不管是 IPv4 还是 IPv6，TCP 或者 UDP 都可以使用这个公用接口。\n\n以下示例先使用 TCP 协议连接远程 80 端口，然后使用 UDP 协议连接，最后使用 TCP 协议连接 IPv6 地址：\n\n示例 15.3 [dial.go](examples/chapter_15/dial.go)\n\n```go\n// make a connection with www.example.org:\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n)\n\nfunc main() {\n\tconn, err := net.Dial(\"tcp\", \"192.0.32.10:80\") // tcp ipv4\n\tcheckConnection(conn, err)\n\tconn, err = net.Dial(\"udp\", \"192.0.32.10:80\") // udp\n\tcheckConnection(conn, err)\n\tconn, err = net.Dial(\"tcp\", \"[2620:0:2d0:200::10]:80\") // tcp ipv6\n\tcheckConnection(conn, err)\n}\nfunc checkConnection(conn net.Conn, err error) {\n\tif err != nil {\n\t\tfmt.Printf(\"error %v connecting!\", err)\n\t\tos.Exit(1)\n\t}\n\tfmt.Printf(\"Connection is made with %v\\n\", conn)\n}\n```\n下边也是一个使用 `net` 包从 socket 中打开，写入，读取数据的例子：\n\n示例 15.4 [socket.go](examples/chapter_15/socket.go)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n)\n\nfunc main() {\n\tvar (\n\t\thost          = \"www.apache.org\"\n\t\tport          = \"80\"\n\t\tremote        = host + \":\" + port\n\t\tmsg    string = \"GET / \\n\"\n\t\tdata          = make([]uint8, 4096)\n\t\tread          = true\n\t\tcount         = 0\n\t)\n\t// 创建一个 socket\n\tcon, err := net.Dial(\"tcp\", remote)\n\t// 发送我们的消息，一个 http GET 请求\n\tio.WriteString(con, msg)\n\t// 读取服务器的响应\n\tfor read {\n\t\tcount, err = con.Read(data)\n\t\tread = (err == nil)\n\t\tfmt.Printf(string(data[0:count]))\n\t}\n\tcon.Close()\n}\n```\n\n**练习 15.1** \n\n编写新版本的客户端和服务器 ([client1.go](exercises/chapter_15/client1.go) / [server1.go](exercises/chapter_15/server1.go))：\n\n*\t增加一个检查错误的函数 `checkError(error)`；讨论如下方案的利弊：为什么这个重构可能并没有那么理想？看看在 [示例 15.14](examples/chapter_15/template_validation.go) 中它是如何被解决的\n*\t使客户端可以通过发送一条命令 SH 来关闭服务器\n*\t让服务器可以保存已经连接的客户端列表（他们的名字）；当客户端发送 `WHO` 指令的时候，服务器将显示如下列表：\n```\nThis is the client list: 1:active, 0=inactive\nUser IVO is 1\nUser MARC is 1\nUser CHRIS is 1\n```\n注意：当服务器运行的时候，你无法编译/连接同一个目录下的源码来产生一个新的版本，因为 `server.exe` 正在被操作系统使用而无法被替换成新的版本。\n\n下边这个版本的 simple_tcp_server.go 从很多方面优化了第一个 tcp 服务器的示例 server.go 并且拥有更好的结构，它只用了 80 行代码！\n\n示例 15.5 [simple_tcp_server.go](examples/chapter_15/simple_tcp_server.go)：\n\n```go\n// Simple multi-thread/multi-core TCP server.\npackage main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n)\n\nconst maxRead = 25\n\nfunc main() {\n\tflag.Parse()\n\tif flag.NArg() != 2 {\n\t\tpanic(\"usage: host port\")\n\t}\n\thostAndPort := fmt.Sprintf(\"%s:%s\", flag.Arg(0), flag.Arg(1))\n\tlistener := initServer(hostAndPort)\n\tfor {\n\t\tconn, err := listener.Accept()\n\t\tcheckError(err, \"Accept: \")\n\t\tgo connectionHandler(conn)\n\t}\n}\n\nfunc initServer(hostAndPort string) *net.TCPListener {\n\tserverAddr, err := net.ResolveTCPAddr(\"tcp\", hostAndPort)\n\tcheckError(err, \"Resolving address:port failed: '\"+hostAndPort+\"'\")\n\tlistener, err := net.ListenTCP(\"tcp\", serverAddr)\n\tcheckError(err, \"ListenTCP: \")\n\tprintln(\"Listening to: \", listener.Addr().String())\n\treturn listener\n}\n\nfunc connectionHandler(conn net.Conn) {\n\tconnFrom := conn.RemoteAddr().String()\n\tprintln(\"Connection from: \", connFrom)\n\tsayHello(conn)\n\tfor {\n\t\tvar ibuf []byte = make([]byte, maxRead+1)\n\t\tlength, err := conn.Read(ibuf[0:maxRead])\n\t\tibuf[maxRead] = 0 // to prevent overflow\n\t\tswitch err {\n\t\tcase nil:\n\t\t\thandleMsg(length, err, ibuf)\n\t\tcase os.EAGAIN: // try again\n\t\t\tcontinue\n\t\tdefault:\n\t\t\tgoto DISCONNECT\n\t\t}\n\t}\nDISCONNECT:\n\terr := conn.Close()\n\tprintln(\"Closed connection: \", connFrom)\n\tcheckError(err, \"Close: \")\n}\n\nfunc sayHello(to net.Conn) {\n\tobuf := []byte{'L', 'e', 't', '\\'', 's', ' ', 'G', 'O', '!', '\\n'}\n\twrote, err := to.Write(obuf)\n\tcheckError(err, \"Write: wrote \"+string(wrote)+\" bytes.\")\n}\n\nfunc handleMsg(length int, err error, msg []byte) {\n\tif length > 0 {\n\t\tprint(\"<\", length, \":\")\n\t\tfor i := 0; ; i++ {\n\t\t\tif msg[i] == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tfmt.Printf(\"%c\", msg[i])\n\t\t}\n\t\tprint(\">\")\n\t}\n}\n\nfunc checkError(error error, info string) {\n\tif error != nil {\n\t\tpanic(\"ERROR: \" + info + \" \" + error.Error()) // terminate program\n\t}\n}\n```\n（**译者注：应该是由于 Go 版本的更新，会提示 os.EAGAIN undefined，修改后的代码：[simple_tcp_server_v1.go](examples/chapter_15/simple_tcp_server_v1.go)**）\n\n都有哪些改进？\n\n*\t服务器地址和端口不再是硬编码，而是通过命令行参数传入，并通过 `flag` 包来读取这些参数。这里使用了 `flag.NArg()` 检查是否按照期望传入了 2 个参数：\n\n```go\nif flag.NArg() != 2 {\n\tpanic(\"usage: host port\")\n}\n```\n传入的参数通过 `fmt.Sprintf()` 函数格式化成字符串\n```go\nhostAndPort := fmt.Sprintf(\"%s:%s\", flag.Arg(0), flag.Arg(1))\n```\n*\t在 `initServer()` 函数中通过 `net.ResolveTCPAddr()` 得到了服务器地址和端口，这个函数最终返回了一个 `*net.TCPListener`\n*\t每一个连接都会以协程的方式运行 `connectionHandler()` 函数。函数首先通过 `conn.RemoteAddr()` 获取到客户端的地址并显示出来\n*\t它使用 `conn.Write()` 发送 Go 推广消息给客户端\n*\t它使用一个 25 字节的缓冲读取客户端发送的数据并一一打印出来。如果读取的过程中出现错误，代码会进入 `switch` 语句 `default` 分支，退出无限循环并关闭连接。如果是操作系统的 `EAGAIN` 错误，它会重试。\n*\t所有的错误检查都被重构在独立的函数 `checkError` 中，当错误产生时，利用错误上下文来触发 panic。\n\n在命令行中输入 `simple_tcp_server localhost 50000` 来启动服务器程序，然后在独立的命令行窗口启动一些 client.go 的客户端。当有两个客户端连接的情况下服务器的典型输出如下，这里我们可以看到每个客户端都有自己的地址：\n```\nE:\\Go\\GoBoek\\code examples\\chapter 14>simple_tcp_server localhost 50000\nListening to: 127.0.0.1:50000\nConnection from: 127.0.0.1:49346\n<25:Ivo says: Hi server, do y><12:ou hear me ?>\nConnection from: 127.0.0.1:49347\n<25:Marc says: Do you remembe><25:r our first meeting serve><2:r?>\n```\n\n**net.Error：**\n`net` 包返回的错误类型遵循惯例为 `error`，但有些错误实现包含额外的方法，他们被定义为 `net.Error` 接口：\n```go\npackage net\n\ntype Error interface {\n\tTimeout() bool // 错误是否超时\n\tTemporary() bool // 是否是临时错误\n}\n```\n通过类型断言，客户端代码可以测试 `net.Error`，从而区分是临时发生的还是必然会出现的错误。举例来说，一个网络爬虫程序在遇到临时发生的错误时可能会休眠或者重试，如果是一个必然发生的错误，则他会放弃继续执行。\n```go\n// in a loop - some function returns an error err\nif nerr, ok := err.(net.Error); ok && nerr.Temporary() {\n\ttime.Sleep(1e9)\n\tcontinue // try again\n}\nif err != nil {\n\tlog.Fatal(err)\n}\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[网络、模版与网页应用](15.0.md)\n- 下一节：[一个简单的网页服务器](15.2.md)\n"
  },
  {
    "path": "eBook/15.10.md",
    "content": "# 15.10 基于网络的通道 netchan\n\n备注：Go 团队决定改进并重新打造 `netchan` 包的现有版本，它已被移至 `old/netchan`。`old/` 目录用于存放过时的包代码，它们不会成为 Go 1.x 的一部分。本节仅出于向后兼容性讨论 `netchan` 包的概念。\n\n一项和 `rpc` 密切相关的技术是基于网络的通道。类似 [14 章](14.0.md)所使用的通道都是本地的，它们仅存在于被执行的机器内存空间中。`netchan` 包实现了类型安全的网络化通道：它允许一个通道两端出现由网络连接的不同计算机。其实现原理是，在其中一台机器上将传输数据发送到通道中，那么就可以被另一台计算机上同类型的通道接收。一个导出器 (`exporter`) 会按名称发布（一组）通道。导入器 (`importer`) 连接到导出的机器，并按名称导入这些通道。之后，两台机器就可按通常的方式来使用通道。网络通道不是同步的，它们类似于带缓存的通道。\n\n发送端示例代码如下：\n```go\nexp, err := netchan.NewExporter(\"tcp\", \"netchanserver.mydomain.com:1234\")\nif err != nil {\n\tlog.Fatalf(\"Error making Exporter: %v\", err)\n}\nch := make(chan myType)\nerr := exp.Export(\"sendmyType\", ch, netchan.Send)\nif err != nil {\n\tlog.Fatalf(\"Send Error: %v\", err)\n}\n```\n\n接收端示例代码如下：\n```go\nimp, err := netchan.NewImporter(\"tcp\", \"netchanserver.mydomain.com:1234\")\nif err != nil {\n\tlog.Fatalf(\"Error making Importer: %v\", err)\n}\nch := make(chan myType)\nerr = imp.Import(\"sendmyType\", ch, netchan.Receive)\nif err != nil {\n\tlog.Fatalf(\"Receive Error: %v\", err)\n}\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[用 rpc 实现远程过程调用](15.9.md)\n- 下一节：[与 websocket 通信](15.11.md)\n"
  },
  {
    "path": "eBook/15.11.md",
    "content": "# 15.11 与 websocket 通信\n\n备注：Go 团队决定从 Go 1 起，将 `websocket`  包移出 Go 标准库，转移到 `code.google.com/p/go` 下的子项目 `websocket`，同时预计近期将做重大更改。\n\n`import \"websocket\"` 这行要改成：\n```go\nimport websocket \"code.google.com/p/go/websocket\"\n```\n\n与 http 协议相反，websocket 是通过客户端与服务器之间的对话，建立的基于单个持久连接的协议。然而在其他方面，其功能几乎与 http 相同。在示例 15.24 中，我们有一个典型的 websocket 服务器，他会自启动并监听 websocket 客户端的连入。示例 15.25 演示了 5 秒后会终止的客户端代码。当连接到来时，服务器先打印 `new connection`，当客户端停止时，服务器打印 `EOF => closing connection`。\n\n示例 15.24 [websocket_server.go](examples/chapter_15/websocket_server.go)\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"websocket\"\n)\n\nfunc server(ws *websocket.Conn) {\n\tfmt.Printf(\"new connection\\n\")\n\tbuf := make([]byte, 100)\n\tfor {\n\t\tif _, err := ws.Read(buf); err != nil {\n\t\t\tfmt.Printf(\"%s\", err.Error())\n\t\t\tbreak\n\t\t}\n\t}\n\tfmt.Printf(\" => closing connection\\n\")\n\tws.Close()\n}\n\nfunc main() {\n\thttp.Handle(\"/websocket\", websocket.Handler(server))\n\terr := http.ListenAndServe(\":12345\", nil)\n\tif err != nil {\n\t\tpanic(\"ListenAndServe: \" + err.Error())\n\t}\n}\n```\n\n示例 15.25 [websocket_client.go](examples/chapter_15/websocket_client.go)\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\t\"websocket\"\n)\n\nfunc main() {\n\tws, err := websocket.Dial(\"ws://localhost:12345/websocket\", \"\",\n\t\t\"http://localhost/\")\n\tif err != nil {\n\t\tpanic(\"Dial: \" + err.Error())\n\t}\n\tgo readFromServer(ws)\n\ttime.Sleep(5e9)\n    ws.Close()\n}\n\nfunc readFromServer(ws *websocket.Conn) {\n\tbuf := make([]byte, 1000)\n\tfor {\n\t\tif _, err := ws.Read(buf); err != nil {\n\t\t\tfmt.Printf(\"%s\\n\", err.Error())\n\t\t\tbreak\n\t\t}\n\t}\n}\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[基于网络的通道 netchan](15.10.md)\n- 下一节：[用 smtp 发送邮件](15.12.md)\n"
  },
  {
    "path": "eBook/15.12.md",
    "content": "# 15.12 用 smtp 发送邮件\n\n`smtp` 包实现了用于发送邮件的“简单邮件传输协议”（Simple Mail Transfer Protocol）。它有一个 `Client` 类型，代表一个连接到 SMTP 服务器的客户端：\n\n- `Dial()` 方法返回一个已连接到 SMTP 服务器的客户端 `Client`\n- 设置 `Mail`（from，即发件人）和 `Rcpt`（to，即收件人）\n- `Data()` 方法返回一个用于写入数据的 `Writer`，这里利用 `buf.WriteTo(wc)` 写入\n\n示例 15.26 [smtp.go](examples/chapter_15/smtp.go)\n```go\npackage main\n\nimport (\n\t\"bytes\"\n\t\"log\"\n\t\"net/smtp\"\n)\n\nfunc main() {\n\t// Connect to the remote SMTP server.\n\tclient, err := smtp.Dial(\"mail.example.com:25\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\t// Set the sender and recipient.\n\tclient.Mail(\"sender@example.org\")\n\tclient.Rcpt(\"recipient@example.net\")\n\t// Send the email body.\n\twc, err := client.Data()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer wc.Close()\n\tbuf := bytes.NewBufferString(\"This is the email body.\")\n\tif _, err = buf.WriteTo(wc); err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n```\n\n如果需要认证，或有多个收件人时，也可以用 `SendMail()` 函数发送。它连接到地址为 `addr` 的服务器；如果可以，切换到 TLS（“传输层安全”加密和认证协议），并用 PLAIN 机制认证；然后以 `from` 作为发件人，`to` 作为收件人列表，`msg` 作为邮件内容，发出一封邮件：\n```go\nfunc SendMail(addr string, a Auth, from string, to []string, msg []byte) error\n```\n\n示例 15.27 [smtp_auth.go](examples/chapter_15/smtp_auth.go)\n```go\npackage main\n\nimport (\n\t\"log\"\n\t\"net/smtp\"\n)\n\nfunc main() {\n\t// Set up authentication information.\n\tauth := smtp.PlainAuth(\n\t\t\"\",\n\t\t\"user@example.com\",\n\t\t\"password\",\n\t\t\"mail.example.com\",\n\t)\n\t// Connect to the server, authenticate, set the sender and recipient,\n\t// and send the email all in one step.\n\terr := smtp.SendMail(\n\t\t\"mail.example.com:25\",\n\t\tauth,\n\t\t\"sender@example.org\",\n\t\t[]string{\"recipient@example.net\"},\n\t\t[]byte(\"This is the email body.\"),\n\t)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n}\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[与 websocket 通信](15.11.md)\n- 下一章：[常见的陷阱与错误](16.0.md)\n"
  },
  {
    "path": "eBook/15.2.md",
    "content": "# 15.2 一个简单的 web 服务器\n\nhttp 是比 tcp 更高层的协议，它描述了网页服务器如何与客户端浏览器进行通信。Go 提供了 `net/http` 包，我们马上就来看下。先从一些简单的示例开始，首先编写一个“Hello world!”网页服务器：[查看示例 15.6](examples/chapter_15/hello_world_webserver.go)\n\n我们引入了 `http` 包并启动了网页服务器，和 [15.1 节](15.1.md)的 `net.Listen(\"tcp\", \"localhost:50000\")` 函数的 tcp 服务器是类似的，使用 `http.ListenAndServe(\"localhost:8080\", nil)` 函数，如果成功会返回空，否则会返回一个错误（地址 `localhost` 部分可以省略，`8080` 是指定的端口号）。\n\n`http.URL` 用于表示网页地址，其中字符串属性 `Path` 用于保存 url 的路径；`http.Request` 描述了客户端请求，内含一个 `URL` 字段。\n\n如果 `req` 是来自 html 表单的 POST 类型请求，`“var1”` 是该表单中一个输入域的名称，那么用户输入的值就可以通过 Go 代码 `req.FormValue(\"var1\")` 获取到（见 [15.4 节](15.4.md)）。还有一种方法是先执行 `request.ParseForm()`，然后再获取 `request.Form[\"var1\"]` 的第一个返回参数，就像这样：\n```go\nvar1, found := request.Form[\"var1\"]\n```\n第二个参数 `found` 为 `true`。如果 `var1` 并未出现在表单中，`found` 就是 `false`。\n\n表单属性实际上是 `map[string][]string` 类型。网页服务器发送一个 `http.Response` 响应，它是通过 `http.ResponseWriter` 对象输出的，后者组装了 HTTP 服务器响应，通过对其写入内容，我们就将数据发送给了 HTTP 客户端。\n\n现在我们仍然要编写程序，以实现服务器必须做的事，即如何处理请求。这是通过 `http.HandleFunc()` 函数完成的。在这个例子中，当根路径“/”（url 地址是 `http://localhost:8080`）被请求的时候（或者这个服务器上的其他任意地址），`HelloServer()` 函数就被执行了。这个函数是 `http.HandlerFunc` 类型的，它们通常被命名为 Prefhandler，和某个路径前缀 Pref 匹配。\n\n`http.HandleFunc` 注册了一个处理函数（这里是 `HelloServer()`）来处理对应 `/` 的请求。\n\n`/` 可以被替换为其他更特定的 url，比如 `/create`，`/edit` 等等；你可以为每一个特定的 url 定义一个单独的处理函数。这个函数需要两个参数：第一个是 `ReponseWriter` 类型的 `w`；第二个是请求 `req`。程序向 `w` 写入了 `Hello` 和 `r.URL.Path[1:]` 组成的字符串：末尾的 `[1:]` 表示“创建一个从索引为 1 的字符到结尾的子切片”，用来丢弃路径开头的“/”，`fmt.Fprintf()` 函数完成了本次写入（见 [12.8 节](12.8.md)）；另一种可行的写法是 `io.WriteString(w, \"hello, world!\\n\")`。\n\n总结：第一个参数是请求的路径，第二个参数是当路径被请求时，需要调用的处理函数的引用。\n\n示例 15.6 [hello_world_webserver.go](examples/chapter_15/hello_world_webserver.go)：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc HelloServer(w http.ResponseWriter, req *http.Request) {\n\tfmt.Println(\"Inside HelloServer handler\")\n\tfmt.Fprintf(w, \"Hello,\"+req.URL.Path[1:])\n}\n\nfunc main() {\n\thttp.HandleFunc(\"/\", HelloServer)\n\terr := http.ListenAndServe(\"localhost:8080\", nil)\n\tif err != nil {\n\t\tlog.Fatal(\"ListenAndServe: \", err.Error())\n\t}\n}\n```\n使用命令行启动程序，会打开一个命令窗口显示如下文字：\n```\nStarting Process E:/Go/GoBoek/code_examples/chapter_14/hello_world_webserver.exe...\n```\n\n然后打开浏览器并输入 url 地址：`http://localhost:8080/world`，浏览器就会出现文字：`Hello, world`，网页服务器会响应你在 `:8080/` 后边输入的内容。\n\n`fmt.Println()` 在服务器端控制台打印状态；在每个处理函数被调用时，把请求记录下来也许更为有用。\n\n注：\n1）前两行（没有错误处理代码）可以替换成以下写法：\n```go\nhttp.ListenAndServe(\":8080\", http.HandlerFunc(HelloServer))\n```\n\n2）`fmt.Fprint()` 和 `fmt.Fprintf()` 都是可以用来写入 `http.ResponseWriter` 的函数（他们实现了 `io.Writer`）。\n\n比如我们可以使用\n\n```go\nfmt.Fprintf(w, \"<h1>%s</h1><div>%s</div>\", title, body)\n```\n来构建一个非常简单的网页并插入 `title` 和 `body` 的值。\n\n如果你需要更多复杂的替换，使用模板包（见 [15.7节](15.7.md)）\n\n3）如果你需要使用安全的 https 连接，使用 `http.ListenAndServeTLS()` 代替 `http.ListenAndServe()`\n\n4）除了 `http.HandleFunc(\"/\", Hfunc)`，其中的 `HFunc` 是一个处理函数，签名为：\n\n```go\nfunc HFunc(w http.ResponseWriter, req *http.Request) {\n\t...\n}\n```\n也可以使用这种方式：`http.Handle(\"/\", http.HandlerFunc(HFunc))`\n\n`HandlerFunc` 只是定义了上述 `HFunc` 签名的别名：\n\n```go\ntype HandlerFunc func(ResponseWriter, *Request)\n```\n\n它是一个可以把普通的函数当做 HTTP 处理器 (`Handler`) 的适配器。如果函数 `f` 声明得合适，`HandlerFunc(f)` 就是一个执行 `f` 函数的 `Handler` 对象。\n\n`http.Handle()` 的第二个参数也可以是 `T` 类型的对象 `obj`：`http.Handle(\"/\", obj)`。\n\n如果 `T` 有 `ServeHTTP()` 方法，那就实现了 http 的 `Handler` 接口：\n```go\nfunc (obj *Typ) ServeHTTP(w http.ResponseWriter, req *http.Request) {\n\t...\n}\n```\n\n这个用法也在 [15.8 节](15.8.md) `Counter` 和 `Chan` 类型上使用。只要实现了 `http.Handler`，`http` 包就可以处理任何 HTTP 请求。\n\n练习 15.2：[webhello2.go](exercises/chapter_15/webhello2.go)\n\n编写一个网页服务器监听端口 9999，有如下处理函数：\n\n*\t当请求 `http://localhost:9999/hello/Name` 时，响应：`hello Name`（`Name` 需是一个合法的姓，比如 Chris 或者 Madeleine）\n\n*\t当请求 `http://localhost:9999/shouthello/Name` 时，响应：`hello NAME`\n\n练习 15.3：[hello_server.go](exercises/chapter_15/hello_server.go)\n\n创建一个空结构 `hello` 并为它实现 `http.Handler`。运行并测试。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[tcp 服务器](15.1.md)\n- 下一节：[访问并读取页面数据](15.3.md)\n"
  },
  {
    "path": "eBook/15.3.md",
    "content": "# 15.3 访问并读取页面数据\n\n在下边这个程序中，数组中的 url 都将被访问：会发送一个简单的 `http.Head()` 请求查看返回值；它的声明如下：`func Head(url string) (r *Response, err error)`\n\n返回的响应 `Response` 其状态码会被打印出来。\n\n示例 15.7 [poll_url.go](examples/chapter_15/poll_url.go)：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n)\n\nvar urls = []string{\n\t\"http://www.google.com/\",\n\t\"http://golang.org/\",\n\t\"http://blog.golang.org/\",\n}\n\nfunc main() {\n\t// Execute an HTTP HEAD request for all url's\n\t// and returns the HTTP status string or an error string.\n\tfor _, url := range urls {\n\t\tresp, err := http.Head(url)\n\t\tif err != nil {\n\t\t\tfmt.Println(\"Error:\", url, err)\n\t\t}\n\t\tfmt.Println(url, \": \", resp.Status)\n\t}\n}\n```\n\n输出为：\n\n\thttp://www.google.com/ : 302 Found\n\thttp://golang.org/ : 200 OK\n\thttp://blog.golang.org/ : 200 OK\n\n***译者注*** 由于国内的网络环境现状，很有可能见到如下超时错误提示：\n\n​\tError: http://www.google.com/ Head http://www.google.com/: dial tcp 216.58.221.100:80: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.\n\n在下边的程序中我们使用 `http.Get()` 获取并显示网页内容；`Get()` 返回值中的 `Body` 属性包含了网页内容，然后我们用 `ioutil.ReadAll()` 把它读出来：\n\n示例 15.8 [http_fetch.go](examples/chapter_15/http_fetch.go)：\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\tres, err := http.Get(\"http://www.google.com\")\n\tcheckError(err)\n\tdata, err := ioutil.ReadAll(res.Body)\n\tcheckError(err)\n\tfmt.Printf(\"Got: %q\", string(data))\n}\n\nfunc checkError(err error) {\n\tif err != nil {\n\t\tlog.Fatalf(\"Get : %v\", err)\n\t}\n}\n```\n\n当访问不存在的网站时，这里有一个 `checkError` 输出错误的例子：\n\n\t2011/09/30 11:24:15 Get: Get http://www.google.bex: dial tcp www.google.bex:80:GetHostByName: No such host is known.\n\n***译者注*** 和上一个例子相似，你可以把 google.com 更换为一个国内可以顺畅访问的网址进行测试\n\n在下边的程序中，我们获取一个 Twitter 用户的状态，通过 `xml` 包将这个状态解析成为一个结构：\n\n示例 15.9 [twitter_status.go](examples/chapter_15/twitter_status.go)\n\n```go\npackage main\n\nimport (\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"net/http\"\n)\n\n/*这个结构会保存解析后的返回数据。\n他们会形成有层级的 XML，可以忽略一些无用的数据*/\ntype Status struct {\n\tText string\n}\n\ntype User struct {\n\tXMLName xml.Name\n\tStatus  Status\n}\n\nfunc main() {\n\t// 发起请求查询推特 Goodland 用户的状态\n\tresponse, _ := http.Get(\"http://twitter.com/users/Googland.xml\")\n\t// 初始化 XML 返回值的结构\n\tuser := User{xml.Name{\"\", \"user\"}, Status{\"\"}}\n\t// 将 XML 解析为我们的结构\n\txml.Unmarshal(response.Body, &user)\n\tfmt.Printf(\"status: %s\", user.Status.Text)\n}\n```\n\n输出：\n\n\tstatus: Robot cars invade California, on orders from Google: Google has been testing self-driving cars ... http://bit.ly/cbtpUN http://retwt.me/97p<exit code=\"0\" msg=\"process exited normally\"/>\n\n**译者注** 和上边的示例相似，你可能无法获取到 xml 数据，另外由于 Go 版本的更新，`xml.Unmarshal()` 函数的第一个参数必需是 `[]byte` 类型，而无法传入 `Body`。\n\n我们会在 [15.4 节](15.4.md) 中用到 `http` 包中的其他重要的函数：\n\n*\t`http.Redirect(w ResponseWriter, r *Request, url string, code int)`：这个函数会让浏览器重定向到 `url`（可以是基于请求 url 的相对路径），同时指定状态码。\n*\t`http.NotFound(w ResponseWriter, r *Request)`：这个函数将返回网页没有找到，HTTP 404 错误。\n*\t`http.Error(w ResponseWriter, error string, code int)`：这个函数返回特定的错误信息和 HTTP 代码。\n*\t另一个 `http.Request` 对象 `req` 的重要属性：`req.Method`，这是一个包含 `GET` 或 `POST` 字符串，用来描述网页是以何种方式被请求的。\n\nGo 为所有的 HTTP 状态码定义了常量，比如：\n```go\nhttp.StatusContinue\t\t= 100\nhttp.StatusOK\t\t\t= 200\nhttp.StatusFound\t\t= 302\nhttp.StatusBadRequest\t\t= 400\nhttp.StatusUnauthorized\t\t= 401\nhttp.StatusForbidden\t\t= 403\nhttp.StatusNotFound\t\t= 404\nhttp.StatusInternalServerError\t= 500\n```\n\n你可以使用 `w.header().Set(\"Content-Type\", \"../..\")` 设置头信息。\n\n比如在网页应用发送 html 字符串的时候，在输出之前执行 `w.Header().Set(\"Content-Type\", \"text/html\")`（通常不是必要的）。\n\n练习 15.4：扩展 http_fetch.go 使之可以从控制台读取 url，使用 [12.1 节](12.1.md)学到的接收控制台输入的方法 ([http_fetch2.go](examples/chapter_15/http_fetch2.go))\n\n练习 15.5：获取 json 格式的推特状态，就像示例 15.9 ([twitter_status_json.go](exercises/chapter_15/twitter_status_json.go))\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[一个简单的网页服务器](15.2.md)\n- 下一节：[写一个简单的网页应用](15.4.md)\n"
  },
  {
    "path": "eBook/15.4.md",
    "content": "# 15.4 写一个简单的网页应用\r\n\r\n下边的程序在端口 `8088` 上启动了一个网页服务器；`SimpleServer()` 会处理 url/`test1` 使它在浏览器输出 `hello world`。`FormServer` 会处理 url/`test2`：如果 url 最初由浏览器请求，那么它是一个 `GET` 请求，返回一个 `form` 常量，包含了简单的 `input` 表单，这个表单里有一个文本框和一个提交按钮。当在文本框输入一些东西并点击提交按钮的时候，会发起一个 POST 请求。`FormServer()` 中的代码用到了 `switch` 来区分两种情况。请求为 POST 类型时，`name` 属性为 `inp` 的文本框的内容可以这样获取：`request.FormValue(\"inp\")`。然后将其写回浏览器页面中。在控制台启动程序，然后到浏览器中打开 url `http://localhost:8088/test2` 来测试这个程序：\r\n\r\n\r\n示例 15.10 [simple_webserver.go](examples/chapter_15/simple_webserver.go)\r\n\r\n```go\r\npackage main\r\n\r\nimport (\r\n\t\"io\"\r\n\t\"net/http\"\r\n)\r\n\r\nconst form = `\r\n\t<html><body>\r\n\t\t<form action=\"#\" method=\"post\" name=\"bar\">\r\n\t\t\t<input type=\"text\" name=\"in\" />\r\n\t\t\t<input type=\"submit\" value=\"submit\"/>\r\n\t\t</form>\r\n\t</body></html>\r\n`\r\n\r\n/* handle a simple get request */\r\nfunc SimpleServer(w http.ResponseWriter, request *http.Request) {\r\n\tio.WriteString(w, \"<h1>hello, world</h1>\")\r\n}\r\n\r\nfunc FormServer(w http.ResponseWriter, request *http.Request) {\r\n\tw.Header().Set(\"Content-Type\", \"text/html\")\r\n\tswitch request.Method {\r\n\tcase \"GET\":\r\n\t\t/* display the form to the user */\r\n\t\tio.WriteString(w, form)\r\n\tcase \"POST\":\r\n\t\t/* handle the form data, note that ParseForm must\r\n\t\t   be called before we can extract form data */\r\n\t\t//request.ParseForm();\r\n\t\t//io.WriteString(w, request.Form[\"in\"][0])\r\n\t\tio.WriteString(w, request.FormValue(\"in\"))\r\n\t}\r\n}\r\n\r\nfunc main() {\r\n\thttp.HandleFunc(\"/test1\", SimpleServer)\r\n\thttp.HandleFunc(\"/test2\", FormServer)\r\n\tif err := http.ListenAndServe(\":8088\", nil); err != nil {\r\n\t\tpanic(err)\r\n\t}\r\n}\r\n```\r\n\r\n注：当使用字符串常量表示 html 文本的时候，包含 `<html><body>...</body></html>` 对于让浏览器将它识别为 html 文档非常重要。\r\n\r\n更安全的做法是在处理函数中，在写入返回内容之前将头部的 `content-type` 设置为 `text/html`：`w.Header().Set(\"Content-Type\", \"text/html\")`。\r\n\r\n`\"Content-Type\"` 会让浏览器认为它可以使用函数 `http.DetectContentType([]byte(form))` 来处理收到的数据。\r\n\r\n练习 15.6 [statistics.go](exercises/chapter_15/statistics.go)\r\n\r\n编写一个网页程序，可以让用户输入一连串的数字。计算出这些数字的均值和中值，并且打印出来，就像下边这张截图一样：\r\n\r\n<img src=\"images/15.4_fig15.1.jpg?raw=true\" style=\"zoom:80%;\" />\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[访问并读取页面](15.3.md)\r\n- 下一节：[确保网页应用健壮](15.5.md)\r\n"
  },
  {
    "path": "eBook/15.5.md",
    "content": "# 15.5 确保网页应用健壮\n\n当网页应用的处理函数发生 panic，服务器会简单地终止运行。这可不妙：网页服务器必须是足够健壮的程序，能够承受任何可能的突发问题。\n\n首先能想到的是在每个处理函数中使用 `defer`/`recover()`，不过这样会产生太多的重复代码。[13.5 节](13.5.md)使用闭包的错误处理模式是更优雅的方案。我们把这种机制应用到前一章的简单网页服务器上。实际上，它可以被简单地应用到任何网页服务器程序中。\n\n为增强代码可读性，我们为页面处理函数创建一个类型：\n```go\ntype HandleFnc func(http.ResponseWriter, *http.Request)\n```\n\n我们的错误处理函数应用了 [13.5 节](13.5.md)的模式，变成了以下的 `logPanics()` 函数：\n```go\nfunc logPanics(function HandleFnc) HandleFnc {\n\treturn func(writer http.ResponseWriter, request *http.Request) {\n\t\tdefer func() {\n\t\t\tif x := recover(); x != nil {\n\t\t\t\tlog.Printf(\"[%v] caught panic: %v\", request.RemoteAddr, x)\n\t\t\t}\n\t\t}()\n\t\tfunction(writer, request)\n\t}\n}\n```\n\n然后我们用 `logPanics()` 来包装对处理函数的调用：\n```go\nhttp.HandleFunc(\"/test1\", logPanics(SimpleServer))\nhttp.HandleFunc(\"/test2\", logPanics(FormServer))\n```\n\n处理函数现在可以恢复 panic 调用，类似 [13.5 节](13.5.md)中的错误检测函数。完整代码如下：\n\n示例 15.11 [robust_webserver.go](examples/chapter_15/robust_webserver.go)\n\n```go\npackage main\n\nimport (\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n)\n\nconst form = `<html><body><form action=\"#\" method=\"post\" name=\"bar\">\n\t\t<input type=\"text\" name=\"in\"/>\n\t\t<input type=\"submit\" value=\"Submit\"/>\n\t</form></html></body>`\n\ntype HandleFnc func(http.ResponseWriter, *http.Request)\n\n/* handle a simple get request */\nfunc SimpleServer(w http.ResponseWriter, request *http.Request) {\n\tio.WriteString(w, \"<h1>hello, world</h1>\")\n}\n\n/* handle a form, both the GET which displays the form\n   and the POST which processes it.*/\nfunc FormServer(w http.ResponseWriter, request *http.Request) {\n\tw.Header().Set(\"Content-Type\", \"text/html\")\n\tswitch request.Method {\n\tcase \"GET\":\n\t\t/* display the form to the user */\n\t\tio.WriteString(w, form)\n\tcase \"POST\":\n\t\t/* handle the form data, note that ParseForm must\n\t\t   be called before we can extract form data*/\n\t\t//request.ParseForm();\n\t\t//io.WriteString(w, request.Form[\"in\"][0])\n\t\tio.WriteString(w, request.FormValue(\"in\"))\n\t}\n}\n\nfunc main() {\n\thttp.HandleFunc(\"/test1\", logPanics(SimpleServer))\n\thttp.HandleFunc(\"/test2\", logPanics(FormServer))\n\tif err := http.ListenAndServe(\":8088\", nil); err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc logPanics(function HandleFnc) HandleFnc {\n\treturn func(writer http.ResponseWriter, request *http.Request) {\n\t\tdefer func() {\n\t\t\tif x := recover(); x != nil {\n\t\t\t\tlog.Printf(\"[%v] caught panic: %v\", request.RemoteAddr, x)\n\t\t\t}\n\t\t}()\n\t\tfunction(writer, request)\n\t}\n}\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[写一个简单的网页应用](15.4.md)\n- 下一节：[用模板编写网页应用](15.6.md)\n"
  },
  {
    "path": "eBook/15.6.md",
    "content": "# 15.6 用模板编写网页应用\n\n以下程序是用 100 行以内代码实现可行的 wiki 网页应用，它由一组页面组成，用于阅读、编辑和保存。它是来自 Go 网站 codelab 的 wiki 制作教程，我所知的最好的 Go 教程之一，非常值得进行完整的实验，以见证并理解程序是如何被构建起来的（[https://golang.org/doc/articles/wiki/](https://golang.org/doc/articles/wiki/)）。这里，我们将以自顶向下的视角，从整体上给出程序的补充说明。程序是网页服务器，它必须从命令行启动，监听某个端口，例如 8080。浏览器可以通过请求 URL 阅读 wiki 页面的内容，例如：`http://localhost:8080/view/page1`。\n\n接着，页面的文本内容从一个文件中读取，并显示在网页中。它包含一个超链接，指向编辑页面（`http://localhost:8080/edit/page1`）。编辑页面将内容显示在一个文本域中，用户可以更改文本，点击“保存”按钮保存到对应的文件中。然后回到阅读页面显示更改后的内容。如果某个被请求阅读的页面不存在（例如：`http://localhost:8080/edit/page999`），程序可以作出识别，立即重定向到编辑页面，如此新的 wiki 页面就可以被创建并保存。\n\nwiki 页面需要一个标题和文本内容，它在程序中被建模为如下结构体，Body 字段存放内容，由字节切片组成。\n```go\ntype Page struct {\n\tTitle string\n\tBody  []byte\n}\n```\n\n为了在可执行程序之外维护 wiki 页面内容，我们简单地使用了文本文件作为持久化存储。程序、必要的模板和文本文件可以在 [wiki](examples/chapter_15/wiki) 中找到。\n\n示例 15.12 [wiki.go](examples/chapter_15/wiki/wiki.go)\n\n```go\npackage main\n\nimport (\n\t\"net/http\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"regexp\"\n\t\"text/template\"\n)\n\nconst lenPath = len(\"/view/\")\n\nvar titleValidator = regexp.MustCompile(\"^[a-zA-Z0-9]+$\")\nvar templates = make(map[string]*template.Template)\nvar err error\n\ntype Page struct {\n\tTitle string\n\tBody  []byte\n}\n\nfunc init() {\n\tfor _, tmpl := range []string{\"edit\", \"view\"} {\n\t\ttemplates[tmpl] = template.Must(template.ParseFiles(tmpl + \".html\"))\n\t}\n}\n\nfunc main() {\n\thttp.HandleFunc(\"/view/\", makeHandler(viewHandler))\n\thttp.HandleFunc(\"/edit/\", makeHandler(editHandler))\n\thttp.HandleFunc(\"/save/\", makeHandler(saveHandler))\n\terr := http.ListenAndServe(\"localhost:8080\", nil)\n\tif err != nil {\n\t\tlog.Fatal(\"ListenAndServe: \", err.Error())\n\t}\n}\n\nfunc makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\ttitle := r.URL.Path[lenPath:]\n\t\tif !titleValidator.MatchString(title) {\n\t\t\thttp.NotFound(w, r)\n\t\t\treturn\n\t\t}\n\t\tfn(w, r, title)\n\t}\n}\n\nfunc viewHandler(w http.ResponseWriter, r *http.Request, title string) {\n\tp, err := load(title)\n\tif err != nil { // page not found\n\t\thttp.Redirect(w, r, \"/edit/\"+title, http.StatusFound)\n\t\treturn\n\t}\n\trenderTemplate(w, \"view\", p)\n}\n\nfunc editHandler(w http.ResponseWriter, r *http.Request, title string) {\n\tp, err := load(title)\n\tif err != nil {\n\t\tp = &Page{Title: title}\n\t}\n\trenderTemplate(w, \"edit\", p)\n}\n\nfunc saveHandler(w http.ResponseWriter, r *http.Request, title string) {\n\tbody := r.FormValue(\"body\")\n\tp := &Page{Title: title, Body: []byte(body)}\n\terr := p.save()\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\thttp.Redirect(w, r, \"/view/\"+title, http.StatusFound)\n}\n\nfunc renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {\n\terr := templates[tmpl].Execute(w, p)\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t}\n}\n\nfunc (p *Page) save() error {\n\tfilename := p.Title + \".txt\"\n\t// file created with read-write permissions for the current user only\n\treturn ioutil.WriteFile(filename, p.Body, 0600)\n}\n\nfunc load(title string) (*Page, error) {\n\tfilename := title + \".txt\"\n\tbody, err := ioutil.ReadFile(filename)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &Page{Title: title, Body: body}, nil\n}\n```\n\n让我们来通读代码：\n\n- 首先导入必要的包。由于我们在构建网页服务器，`http` 当然是必须的。不过还导入了 `io/ioutil` 来方便地读写文件，`regexp` 用于验证输入标题，以及 `template` 来动态创建 html 文档。\n- 为避免黑客构造特殊输入攻击服务器，我们用如下正则表达式检查用户在浏览器上输入的 URL（同时也是 wiki 页面标题）：\n  ```go\n  var titleValidator = regexp.MustCompile(\"^[a-zA-Z0-9]+$\")\n  ```\n  `makeHandler` 会用它对请求管控。\n- 必须有一种机制把 `Page` 结构体数据插入到网页的标题和内容中，可以利用 `template` 包通过如下步骤完成：\n\t1. 先在文本编辑器中创建 html 模板文件，例如 view.html：\n\t```html\n\t<h1>{{.Title |html}}</h1>\n\t<p>[<a href=\"/edit/{{.Title |html}}\">edit</a>]</p>\n\t<div>{{printf \"%s\" .Body |html}}</div>\n\t```\n\t把要插入的数据结构字段放在 `{{` 和 `}}` 之间，这里是把 `Page` 结构体数据 `{{.Title |html}}` 和 `{{printf \"%s\" .Body |html}}` 插入页面（当然可以是非常复杂的 html，但这里尽可能地简化了，以突出模板的原理。）（`{{.Title |html}}` 和 `{{printf \"%s\" .Body |html}}` 语法说明详见后续章节）。\n\n\t2. `template.Must(template.ParseFiles(tmpl + \".html\"))` 把模板文件转换为 `*template.Template` 类型的对象，为了高效，在程序运行时仅做一次解析，在 `init()` 函数中处理可以方便地达到目的。所有模板对象都被保持在内存中，存放在以 html 文件名作为索引的 `map` 中：\n\t```go\n\ttemplates = make(map[string]*template.Template)\n\t```\n\t这种技术被称为*模板缓存*，是推荐的最佳实践。\n\n\t3. 为了真正从模板和结构体构建出页面，必须使用：\n\t```go\n\ttemplates[tmpl].Execute(w, p)\n\t```\n\t它基于模板执行，用 `Page` 结构体对象 `p` 作为参数对模板进行替换，并写入 `ResponseWriter` 对象 `w`。必须检查该方法的 `error` 返回值，万一有一个或多个错误，我们可以调用 `http.Error()` 来明示。在我们的应用程序中，这段代码会被多次调用，所以把它提取为单独的函数 `renderTemplate()`。\n- 在 `main()` 中网页服务器用 `ListenAndServe()` 启动并监听 8080 端口。但正如 [15.2节](15.2.md) 那样，需要先为紧接在 URL `localhost:8080/` 之后， 以 `view`, `edit` 或 `save` 开头的 url 路径定义一些处理函数。在大多数网页服务器应用程序中，这形成了一系列 URL 路径到处理函数的映射，类似于 Ruby 和 Rails，Django 或 ASP.NET MVC 这样的 MVC 框架中的路由表。请求的 URL 与这些路径尝试匹配，较长的路径被优先匹配。如不与任何路径匹配，则调用 / 的处理程序。\n\n  在此定义了 3 个处理函数，由于包含重复的启动代码，我们将其提取到单独的 `makeHandler()` 函数中。这是一个值得研究的特殊高阶函数：其参数是一个函数，返回一个新的闭包函数：\n```go\nfunc makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\ttitle := r.URL.Path[lenPath:]\n\t\tif !titleValidator.MatchString(title) {\n\t\t\thttp.NotFound(w, r)\n\t\t\treturn\n\t\t}\n\t\tfn(w, r, title)\n\t}\n}\n```\n- 闭包封闭了函数变量 `fn` 来构造其返回值。但在此之前，它先用 `titleValidator.MatchString(title)` 验证输入标题 `title` 的有效性。如果标题包含了字母和数字以外的字符，就触发 `NotFound` 错误（例如：尝试 `localhost:8080/view/page++`）。`viewHandler`，`editHandler` 和 `saveHandler` 都是传入 `main()` 中 `makeHandler` 的参数，类型必须都与 `fn` 相同。\n- `viewHandler` 尝试按标题读取文本文件，这是通过调用 `load()` 函数完成的，它会构建文件名并用 `ioutil.ReadFile` 读取内容。如果文件存在，其内容会存入字符串中。一个指向 `Page` 结构体的指针按字面量被创建：`&Page{Title: title, Body: body}`。\n\n  另外，该值和表示没有 error 的 `nil` 值一起返回给调用者。然后在 `renderTemplate` 中将该结构体与模板对象整合。\n\n  万一发生错误，也就是说 wiki 页面在磁盘上不存在，错误会被返回给 `viewHandler`，此时会自动重定向，跳转请求对应标题的编辑页面。\n\n- `editHandler` 基本上也差不多：尝试读取文件，如果存在则用“编辑”模板来渲染；万一发生错误，创建一个新的包含指定标题的 `Page` 对象并渲染。\n- 当在编辑页面点击“保存”按钮时，触发保存页面内容的动作。按钮须放在 html 表单中，它开头是这样的：\n  ```html\n  <form action=\"/save/{{.Title |html}}\" method=\"POST\">\n  ```\n  \n  这意味着，当提交表单到类似 `http://localhost/save/{Title}` 这样的 URL 格式时，一个 POST 请求被发往网页服务器。针对这样的 URL 我们已经定义好了处理函数：`saveHandler()`。在 request 对象上调用 `FormValue()` 方法，可以提取名称为 body 的文本域内容，用这些信息构造一个 `Page` 对象，然后尝试通过调用 `save()` 方法保存其内容。万一运行失败，执行 `http.Error` 以将错误显示到浏览器。如果保存成功，重定向浏览器到该页的阅读页面。`save()` 函数非常简单，利用 `ioutil.WriteFile()`，写入 `Page` 结构体的 `Body` 字段到文件 `filename` 中，之后会被用于模板替换占位符 `{{printf \"%s\" .Body |html}}`。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[确保网页应用健壮](15.5.md)\n- 下一节：[探索 template 包](15.7.md)\n"
  },
  {
    "path": "eBook/15.7.md",
    "content": "# 15.7 探索 template 包\n\n（`template` 包的文档可以在 [https://golang.org/pkg/text/template/](https://golang.org/pkg/text/template/) 找到。）\n\n在前一章节，我们使用 template 对象把数据结构整合到 HTML 模板中。这项技术确实对网页应用程序非常有用，然而模板是一项更为通用的技术方案：数据驱动的模板被创建出来，以生成文本输出。HTML 仅是其中的一种特定使用案例。\n\n模板通过与数据结构的整合来生成，通常为结构体或其切片。当数据项传递给 `tmpl.Execute()` ，它用其中的元素进行替换， 动态地重写某一小段文本。**只有被导出的数据项**才可以被整合进模板中。可以在 `{{` 和 `}}` 中加入数据求值或控制结构。数据项可以是值或指针，接口隐藏了他们的差异。\n\n## 15.7.1 字段替换：`{{.FieldName}}`\n\n要在模板中包含某个字段的内容，使用双花括号括起以点 (`.`) 开头的字段名。例如，假设 `Name` 是某个结构体的字段，其值要在被模板整合时替换，则在模板中使用文本 `{{.Name}}`。当 `Name` 是 `map` 的键时这么做也是可行的。要创建一个新的 `Template` 对象，调用 `template.New()`，其字符串参数可以指定模板的名称。正如 [15.5 节](15.5.md)出现过的，`Parse()` 方法通过解析模板定义字符串，生成模板的内部表示。当使用包含模板定义字符串的文件时，将文件路径传递给 `ParseFiles()` 来解析。解析过程如产生错误，这两个函数第二个返回值 `error != nil`。最后通过 `Execute()` 方法，数据结构中的内容与模板整合，并将结果写入方法的第一个参数中，其类型为 `io.Writer`。再一次地，可能会有 `error` 返回。以下程序演示了这些步骤，输出通过 `os.Stdout()` 被写到控制台。\n\n示例 15.13 [template_field.go](examples/chapter_15/template_field.go)\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"text/template\"\n)\n\ntype Person struct {\n\tName string\n\tnonExportedAgeField string\n}\n\nfunc main() {\n\tt := template.New(\"hello\")\n\tt, _ = t.Parse(\"hello {{.Name}}!\")\n\tp := Person{Name: \"Mary\", nonExportedAgeField: \"31\"}\n\tif err := t.Execute(os.Stdout, p); err != nil {\n\t\tfmt.Println(\"There was an error:\", err.Error())\n\t}\n}\n```\n\n输出：`hello Mary!`\n\n如果数据结构中包含一个未导出的字段，当我们尝试把它整合到类似这样的定义字符串：\n```go\nt, _ = t.Parse(\"your age is {{.nonExportedAgeField}}!\")\n```\n会产生错误：\n```\nThere was an error: template: nonexported template hello:1: can’t evaluate field nonExportedAgeField in type main.Person.\n```\n\n如果只是想简单地把 `Execute()` 方法的第二个参数用于替换，使用 `{{.}}`。\n\n当在浏览器环境中进行这些步骤，应首先使用 `html` 过滤器来过滤内容，例如 `{{html .}}`， 或者对 `FieldName` 过滤：`{{ .FieldName |html }}`。\n\n`|html` 这部分代码，是请求模板引擎在输出 `FieldName` 的结果前把值传递给 html 格式化器，它会执行 HTML 字符转义（例如把 `>` 替换为 `&gt;`）。这可以避免用户输入数据破坏 HTML 文档结构。\n\n## 15.7.2 验证模板格式\n\n为了确保模板定义语法是正确的，使用 `Must()` 函数处理 `Parse` 的返回结果。在下面的例子中 `tOK` 是正确的模板， `tErr` 验证时发生错误，会导致运行时 panic。\n\n示例 15.14 [template_validation.go](examples/chapter_15/template_validation.go)\n\n```go\npackage main\n\nimport (\n\t\"text/template\"\n\t\"fmt\"\n)\n\nfunc main() {\n\ttOk := template.New(\"ok\")\n\t//a valid template, so no panic with Must:\n\ttemplate.Must(tOk.Parse(\"/* and a comment */ some static text: {{ .Name }}\"))\n\tfmt.Println(\"The first one parsed OK.\")\n\tfmt.Println(\"The next one ought to fail.\")\n\ttErr := template.New(\"error_template\")\n\ttemplate.Must(tErr.Parse(\" some static text {{ .Name }\"))\n}\n```\n\n输出：\n\n\tThe first one parsed OK.\n\tThe next one ought to fail.\n\tpanic: template: error_template:1: unexpected \"}\" in operand\n\n模板语法出现错误比较少见，可以使用 [13.3节](13.3.md) 概括的 `defer/recover` 机制来报告并纠正错误。\n\n在代码中常见到这 3 个基本函数被串联使用：\n```go\nvar strTempl = template.Must(template.New(\"TName\").Parse(strTemplateHTML))\n```\n\n练习 15.7 [template_validation_recover.go](exercises/chapter_15/template_validation_recover.go)\n\n在上述示例代码上实现 defer/recover 机制。\n\n## 15.7.3 `If-else`\n\n运行 `Execute()` 产生的结果来自模板的输出，它包含静态文本，以及被 `{{}}` 包裹的称之为*管道*的文本。例如，运行这段代码（示例 15.15 [pipline1.go](examples/chapter_15/pipeline1.go)）：\n```go\nt := template.New(\"template test\")\nt = template.Must(t.Parse(\"This is just static text. \\n{{\\\"This is pipeline data - because it is evaluated within the double braces.\\\"}} {{`So is this, but within reverse quotes.`}}\\n\"))\nt.Execute(os.Stdout, nil)\n```\n\n输出结果为：\n\n\tThis is just static text.\n\tThis is pipeline data—because it is evaluated within the double braces. So is this, but within reverse quotes.\n\n现在我们可以对管道数据的输出结果用 `if-else-end` 设置条件约束：如果管道是空的，类似于：\n```html\n{{if ``}} Will not print. {{end}}\n```\n那么 `if` 条件的求值结果为 `false`，不会有输出内容。但如果是这样：\n```html\n{{if `anything`}} Print IF part. {{else}} Print ELSE part.{{end}}\n```\n会输出 `Print IF part.`。以下程序演示了这点：\n\n示例 15.16 [template_ifelse.go](examples/chapter_15/template_ifelse.go)\n\n```go\npackage main\n\nimport (\n\t\"os\"\n\t\"text/template\"\n)\n\nfunc main() {\n\ttEmpty := template.New(\"template test\")\n\ttEmpty = template.Must(tEmpty.Parse(\"Empty pipeline if demo: {{if ``}} Will not print. {{end}}\\n\")) //empty pipeline following if\n\ttEmpty.Execute(os.Stdout, nil)\n\n\ttWithValue := template.New(\"template test\")\n\ttWithValue = template.Must(tWithValue.Parse(\"Non empty pipeline if demo: {{if `anything`}} Will print. {{end}}\\n\")) //non empty pipeline following if condition\n\ttWithValue.Execute(os.Stdout, nil)\n\n\ttIfElse := template.New(\"template test\")\n\ttIfElse = template.Must(tIfElse.Parse(\"if-else demo: {{if `anything`}} Print IF part. {{else}} Print ELSE part.{{end}}\\n\")) //non empty pipeline following if condition\n\ttIfElse.Execute(os.Stdout, nil)\n}\n```\n\n输出：\n\n\tEmpty pipeline if demo:\n\tNon empty pipeline if demo: Will print.\n\tif-else demo: Print IF part.\n\n## 15.7.4 点号和 `with-end`\n\n点号 (`.`) 可以在 Go 模板中使用：其值 `{{.}}` 被设置为当前管道的值。\n\n`with` 语句将点号设为管道的值。如果管道是空的，那么不管 `with-end` 块之间有什么，都会被忽略。在被嵌套时，点号根据最近的作用域取得值。以下程序演示了这点：\n\n示例 15.17 [template_with_end.go](examples/chapter_15/template_with_end.go)\n\n```go\npackage main\n\nimport (\n\t\"os\"\n\t\"text/template\"\n)\n\nfunc main() {\n\tt := template.New(\"test\")\n\tt, _ = t.Parse(\"{{with `hello`}}{{.}}{{end}}!\\n\")\n\tt.Execute(os.Stdout, nil)\n\n\tt, _ = t.Parse(\"{{with `hello`}}{{.}} {{with `Mary`}}{{.}}{{end}}{{end}}!\\n\")\n\tt.Execute(os.Stdout, nil)\n}\n```\n\n输出：\n\n\thello!\n\thello Mary!\n\n## 15.7.5 模板变量 `$`\n\n可以在模板内为管道设置本地变量，变量名以 `$` 符号作为前缀。变量名只能包含字母、数字和下划线。以下示例使用了多种形式的有效变量名。\n\n示例 15.18 [template_variables.go](examples/chapter_15/template_variables.go)\n\n```go\npackage main\n\nimport (\n\t\"os\"\n\t\"text/template\"\n)\n\nfunc main() {\n\tt := template.New(\"test\")\n\tt = template.Must(t.Parse(\"{{with $3 := `hello`}}{{$3}}{{end}}!\\n\"))\n\tt.Execute(os.Stdout, nil)\n\n\tt = template.Must(t.Parse(\"{{with $x3 := `hola`}}{{$x3}}{{end}}!\\n\"))\n\tt.Execute(os.Stdout, nil)\n\n\tt = template.Must(t.Parse(\"{{with $x_1 := `hey`}}{{$x_1}} {{.}} {{$x_1}}{{end}}!\\n\"))\n\tt.Execute(os.Stdout, nil)\n}\n```\n\n输出：\n\n\thello!\n\thola!\n\they hey hey!\n\n## 15.7.6 `range-end`\n\n`range-end` 结构格式为：`{{range pipeline}} T1 {{else}} T0 {{end}}`。\n\n`range` 被用于在集合上迭代：管道的值必须是数组、切片或 `map`。如果管道的值长度为零，点号的值不受影响，且执行 `T0`；否则，点号被设置为数组、切片或 `map` 内元素的值，并执行 `T1`。\n\n如果模板为：\n```html\n{{range .}}\n{{.}}\n{{end}}\n```\n那么执行代码：\n```go\ns := []int{1,2,3,4}\nt.Execute(os.Stdout, s)\n```\n会输出：\n```\n1\n2\n3\n4\n```\n\n如需更实用的示例，请参考 [20.7 节](20.7.md)，来自 App Engine 数据库的数据通过模板来显示：\n```html\n{{range .}}\n\t{{with .Author}}\n\t\t<p><b>{{html .}}</b> wrote:</p>\n\t{{else}}\n\t\t<p>An anonymous person wrote:</p>\n\t{{end}}\n\t<pre>{{html .Content}}</pre>\n\t<pre>{{html .Date}}</pre>\n{{end}}\n```\n\n这里 `range .` 在结构体切片上迭代，每次都包含 `Author`、`Content` 和 `Date` 字段。\n\n## 15.7.7 模板预定义函数\n\n也有一些可以在模板代码中使用的预定义函数，例如 `printf()` 函数工作方式类似于 `fmt.Sprintf()`：\n\n示例 15.19 [predefined_functions.go](examples/chapter_15/predefined_functions.go)\n\n```go\npackage main\n\nimport (\n\t\"os\"\n\t\"text/template\"\n)\n\nfunc main() {\n\tt := template.New(\"test\")\n\tt = template.Must(t.Parse(\"{{with $x := `hello`}}{{printf `%s %s` $x `Mary`}}{{end}}!\\n\"))\n\tt.Execute(os.Stdout, nil)\n}\n```\n输出 `hello Mary!`。\n\n预定义函数也在 [15.6 节](15.6.md)中使用：`{{ printf \"%s\" .Body|html}}`，否则字节切片 `Body` 会作为数字序列打印出来。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[用模板编写网页应用](15.6.md)\n- 下一节：[精巧的多功能网页服务器](15.8.md)\n"
  },
  {
    "path": "eBook/15.8.md",
    "content": "# 15.8 精巧的多功能网页服务器\n\n为进一步深入理解 `http` 包以及如何构建网页服务器功能，让我们来学习和体会下面的例子：先列出代码，然后给出不同功能的实现方法，程序输出显示在表格中。\n\n示例 15.20 [elaborated_webserver.go](examples/chapter_15/elaborated_webserver.go)\n\n```go\npackage main\n\nimport (\n\t\"bytes\"\n\t\"expvar\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"strconv\"\n)\n\n// hello world, the web server\nvar helloRequests = expvar.NewInt(\"hello-requests\")\n// flags:\nvar webroot = flag.String(\"root\", \"/home/user\", \"web root directory\")\n\n// simple flag server\nvar booleanflag = flag.Bool(\"boolean\", true, \"another flag for testing\")\n\n// Simple counter server. POSTing to it will set the value.\ntype Counter struct {\n\tn int\n}\n\n// a channel\ntype Chan chan int\n\nfunc main() {\n\tflag.Parse()\n\thttp.Handle(\"/\", http.HandlerFunc(Logger))\n\thttp.Handle(\"/go/hello\", http.HandlerFunc(HelloServer))\n\t// The counter is published as a variable directly.\n\tctr := new(Counter)\n\texpvar.Publish(\"counter\", ctr)\n\thttp.Handle(\"/counter\", ctr)\n\t// http.Handle(\"/go/\", http.FileServer(http.Dir(\"/tmp\"))) // uses the OS filesystem\n\thttp.Handle(\"/go/\", http.StripPrefix(\"/go/\", http.FileServer(http.Dir(*webroot))))\n\thttp.Handle(\"/flags\", http.HandlerFunc(FlagServer))\n\thttp.Handle(\"/args\", http.HandlerFunc(ArgServer))\n\thttp.Handle(\"/chan\", ChanCreate())\n\thttp.Handle(\"/date\", http.HandlerFunc(DateServer))\n\terr := http.ListenAndServe(\":12345\", nil)\n\tif err != nil {\n\t\tlog.Panicln(\"ListenAndServe:\", err)\n\t}\n}\n\nfunc Logger(w http.ResponseWriter, req *http.Request) {\n\tlog.Print(req.URL.String())\n\tw.WriteHeader(404)\n\tw.Write([]byte(\"oops\"))\n}\n\nfunc HelloServer(w http.ResponseWriter, req *http.Request) {\n\thelloRequests.Add(1)\n\tio.WriteString(w, \"hello, world!\\n\")\n}\n\n// This makes Counter satisfy the expvar.Var interface, so we can export\n// it directly.\nfunc (ctr *Counter) String() string { return fmt.Sprintf(\"%d\", ctr.n) }\n\nfunc (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) {\n\tswitch req.Method {\n\tcase \"GET\": // increment n\n\t\tctr.n++\n\tcase \"POST\": // set n to posted value\n\t\tbuf := new(bytes.Buffer)\n\t\tio.Copy(buf, req.Body)\n\t\tbody := buf.String()\n\t\tif n, err := strconv.Atoi(body); err != nil {\n\t\t\tfmt.Fprintf(w, \"bad POST: %v\\nbody: [%v]\\n\", err, body)\n\t\t} else {\n\t\t\tctr.n = n\n\t\t\tfmt.Fprint(w, \"counter reset\\n\")\n\t\t}\n\t}\n\tfmt.Fprintf(w, \"counter = %d\\n\", ctr.n)\n}\n\nfunc FlagServer(w http.ResponseWriter, req *http.Request) {\n\tw.Header().Set(\"Content-Type\", \"text/plain; charset=utf-8\")\n\tfmt.Fprint(w, \"Flags:\\n\")\n\tflag.VisitAll(func(f *flag.Flag) {\n\t\tif f.Value.String() != f.DefValue {\n\t\t\tfmt.Fprintf(w, \"%s = %s [default = %s]\\n\", f.Name, f.Value.String(), f.DefValue)\n\t\t} else {\n\t\t\tfmt.Fprintf(w, \"%s = %s\\n\", f.Name, f.Value.String())\n\t\t}\n\t})\n}\n\n// simple argument server\nfunc ArgServer(w http.ResponseWriter, req *http.Request) {\n\tfor _, s := range os.Args {\n\t\tfmt.Fprint(w, s, \" \")\n\t}\n}\n\nfunc ChanCreate() Chan {\n\tc := make(Chan)\n\tgo func(c Chan) {\n\t\tfor x := 0; ; x++ {\n\t\t\tc <- x\n\t\t}\n\t}(c)\n\treturn c\n}\n\nfunc (ch Chan) ServeHTTP(w http.ResponseWriter, req *http.Request) {\n\tio.WriteString(w, fmt.Sprintf(\"channel send #%d\\n\", <-ch))\n}\n\n// exec a program, redirecting output\nfunc DateServer(rw http.ResponseWriter, req *http.Request) {\n\trw.Header().Set(\"Content-Type\", \"text/plain; charset=utf-8\")\n\tr, w, err := os.Pipe()\n\tif err != nil {\n\t\tfmt.Fprintf(rw, \"pipe: %s\\n\", err)\n\t\treturn\n\t}\n\n\tp, err := os.StartProcess(\"/bin/date\", []string{\"date\"}, &os.ProcAttr{Files: []*os.File{nil, w, w}})\n\tdefer r.Close()\n\tw.Close()\n\tif err != nil {\n\t\tfmt.Fprintf(rw, \"fork/exec: %s\\n\", err)\n\t\treturn\n\t}\n\tdefer p.Release()\n\tio.Copy(rw, r)\n\twait, err := p.Wait()\n\tif err != nil {\n\t\tfmt.Fprintf(rw, \"wait: %s\\n\", err)\n\t\treturn\n\t}\n\tif !wait.Exited() {\n\t\tfmt.Fprintf(rw, \"date: %v\\n\", wait)\n\t\treturn\n\t}\n}\n```\n\n处理函数 | 调用 URL | 浏览器获得响应\n--------|----------|---------------\n`Logger()` | http://localhost:12345/ （根） | oops\n\n`Logger()` 处理函数用 `w.WriteHeader(404)` 来输出 “404 Not Found”头部。\n\n这项技术通常很有用，无论何时服务器执行代码产生错误，都可以应用类似这样的代码：\n\n```go\nif err != nil {\n\tw.WriteHeader(400)\n\treturn\n}\n```\n\n另外利用 `logger` 包的函数，针对每个请求在服务器端命令行打印日期、时间和 URL。\n\n处理函数 | 调用 URL | 浏览器获得响应\n--------|----------|---------------\n`HelloServer()` | http://localhost:12345/go/hello | hello, world!\n\n包 `expvar` 可以创建（Int，Float 和 String 类型）变量，并将它们发布为公共变量。它会在 HTTP URL `/debug/vars` 上以 JSON 格式公布。通常它被用于服务器操作计数。`helloRequests` 就是这样一个 `int64` 变量，该处理函数对其加 1，然后写入“hello world!”到浏览器。\n\n处理函数 | 调用 URL | 浏览器获得响应\n--------|----------|---------------\n`Counter()` | http://localhost:12345/counter | counter = 1\n`Counter()` | 刷新（GET 请求） | counter = 2\n\n计数器对象 `ctr` 有一个 `String()` 方法，所以它实现了 `expvar.Var` 接口。这使其可以被发布，尽管它是一个结构体。`ServeHTTP()` 函数使 `ctr` 成为处理器，因为它的签名正确实现了 `http.Handler` 接口。\n\n\n处理函数 | 调用 URL | 浏览器获得响应\n--------|----------|---------------\n`FileServer()` | http://localhost:12345/go/ggg.html | 404 page not found\n\n`FileServer(root FileSystem) Handler` 返回一个处理器，它以 `root` 作为根，用文件系统的内容响应 HTTP 请求。要获得操作系统的文件系统，用 `http.Dir`，例如：\n\n```go\nhttp.Handle(\"/go/\", http.FileServer(http.Dir(\"/tmp\")))\n```\n\n处理函数 | 调用 URL | 浏览器获得响应\n--------|----------|---------------\n`FlagServer()` | http://localhost:12345/flags | Flags: boolean = true root = /home/rsc\n\n该处理函数使用了 `flag` 包。`VisitAll()` 函数迭代所有的标签 (flag)，打印它们的名称、值和默认值（当不同于“值”时）。\n\n处理函数 | 调用 URL | 浏览器获得响应\n--------|----------|---------------\n`ArgServer()` | http://localhost:12345/args | ./elaborated_webserver.exe\n\n该处理函数迭代 `os.Args` 以打印出所有的命令行参数。如果没有指定则只有程序名称（可执行程序的路径）会被打印出来。\n\n处理函数 | 调用 URL | 浏览器获得响应\n--------|----------|---------------\n`Channel()` | http://localhost:12345/chan | channel send #1\n`Channel()` | 刷新 | channel send #2\n\n每当有新请求到达，通道的 `ServeHTTP()` 方法从通道获取下一个整数并显示。由此可见，网页服务器可以从通道中获取要发送的响应，它可以由另一个函数产生（甚至是客户端）。下面的代码片段正是一个这样的处理函数，但会在 30 秒后超时：\n\n```go\nfunc ChanResponse(w http.ResponseWriter, req *http.Request) {\n\ttimeout := make (chan bool)\n\tgo func () {\n\t\ttime.Sleep(30e9)\n\t\ttimeout <- true\n\t}()\n\tselect {\n\tcase msg := <-messages:\n\t\tio.WriteString(w, msg)\n\tcase stop := <-timeout:\n\t\treturn\n\t}\n}\n```\n\n处理函数 | 调用 URL | 浏览器获得响应\n--------|----------|---------------\n`DateServer()` | http://localhost:12345/date | 显示当前时间（由于是调用 /bin/date，仅在 Unix 下有效）\n\n可能的输出：`Thu Sep 8 12:41:09 CEST 2011`。\n\n`os.Pipe()` 返回一对相关联的 `File`，从 `r` 读取数据，返回已读取的字节数来自于 `w` 的写入。函数返回这两个文件和错误，如果有的话：\n\n```go\nfunc Pipe() (r *File, w *File, err error)\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[探索 template 包](15.7.md)\n- 下一节：[用 rpc 实现远程过程调用](15.9.md)\n"
  },
  {
    "path": "eBook/15.9.md",
    "content": "# 15.9 用 rpc 实现远程过程调用\n\nGo 程序之间可以使用 `net/rpc` 包实现相互通信，这是另一种客户端-服务器应用场景。它提供了一种方便的途径，通过网络连接调用远程函数。当然，仅当程序运行在不同机器上时，这项技术才实用。`rpc` 包建立在 `gob` 包之上（见 [12.11 节](12.11.md)），实现了自动编码/解码传输的跨网络方法调用。\n\n服务器端需要注册一个对象实例，与其类型名一起，使之成为一项可见的服务：它允许远程客户端跨越网络或其他 I/O 连接访问此对象已导出的方法。总之就是在网络上暴露类型的方法。\n\n`rpc` 包使用了 http 和 tcp 协议，以及用于数据传输的 `gob` 包。服务器端可以注册多个不同类型的对象（服务），但同一类型的多个对象会产生错误。\n\n我们讨论一个简单的例子：定义一个类型 `Args` 及其方法 `Multiply()`，完美地置于单独的包中。方法必须返回可能的错误。\n\n示例15.21 [rpc_objects.go](examples/chapter_15/rpc/rpc_objects.go)\n```go\npackage rpc_objects\n\nimport \"net\"\n\ntype Args struct {\n\tN, M int\n}\n\nfunc (t *Args) Multiply(args *Args, reply *int) net.Error {\n\t*reply = args.N * args.M\n\treturn nil\n}\n```\n\n（**译注：Go 当前版本要求此方法返回类型为 `error`，以上示例中返回 `net.Error` 已无法通过编译，见更新后的 [rpc_objects.go](examples/chapter_15/rpc_updated/rpc_objects/rpc_objects.go)。**）\n\n服务器端产生一个 `rpc_objects.Args` 类型的对象 `calc`，并用 `rpc.Register(object)` 注册。调用 `HandleHTTP()`，然后用 `net.Listen` 在指定的地址上启动监听。也可以按名称来注册对象，例如：`rpc.RegisterName(\"Calculator\", calc)`。\n\n以协程启动 `http.Serve(listener, nil)` 后，会为每一个进入 `listener` 的 HTTP 连接创建新的服务线程。我们必须用诸如 `time.Sleep(1000e9)` 来使服务器在一段时间内保持运行状态。\n\n示例 15.22 [rpc_server.go](examples/chapter_15/rpc/rpc_server.go)\n```go\npackage main\n\nimport (\n\t\"net/http\"\n\t\"log\"\n\t\"net\"\n\t\"net/rpc\"\n\t\"time\"\n\t\"./rpc_objects\"\n)\n\nfunc main() {\n\tcalc := new(rpc_objects.Args)\n\trpc.Register(calc)\n\trpc.HandleHTTP()\n\tlistener, e := net.Listen(\"tcp\", \"localhost:1234\")\n\tif e != nil {\n\t\tlog.Fatal(\"Starting RPC-server -listen error:\", e)\n\t}\n\tgo http.Serve(listener, nil)\n\ttime.Sleep(1000e9)\n}\n```\n\n输出：\n\n\tStarting Process E:/Go/GoBoek/code_examples/chapter_14/rpc_server.exe ...\n\t** 5 秒后： **\n\tEnd Process exit status 0\n\n客户端必须知晓对象类型及其方法的定义。执行 `rpc.DialHTTP()` 连接到服务器后，就可以用 `client.Call(\"Type.Method\", args, &reply)` 调用远程对象的方法。`Type` 是远程对象的类型名，`Method` 是要调用的方法，`args` 是用 `Args` 类型初始化的对象，`reply` 是一个必须事先声明的变量，方法调用产生的结果将存入其中。\n\n示例 15.23 [rpc_client.go](examples/chapter_15/rpc/rpc_client.go)\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/rpc\"\n\t\"./rpc_objects\"\n)\n\nconst serverAddress = \"localhost\"\n\nfunc main() {\n\tclient, err := rpc.DialHTTP(\"tcp\", serverAddress + \":1234\")\n\tif err != nil {\n\t\tlog.Fatal(\"Error dialing:\", err)\n\t}\n\t// Synchronous call\n\targs := &rpc_objects.Args{7, 8}\n\tvar reply int\n\terr = client.Call(\"Args.Multiply\", args, &reply)\n\tif err != nil {\n\t\tlog.Fatal(\"Args error:\", err)\n\t}\n\tfmt.Printf(\"Args: %d * %d = %d\", args.N, args.M, reply)\n}\n```\n\n先启动服务器，再运行客户端，然后就能得到如下输出结果：\n\n\tStarting Process E:/Go/GoBoek/code_examples/chapter_14/rpc_client.exe ...\n\t\n\tArgs: 7 * 8 = 56\n\tEnd Process exit status 0\n\n该远程调用以同步方式进行，它会等待服务器返回结果。也可使用如下方式异步地执行调用：\n```go\ncall1 := client.Go(\"Args.Multiply\", args, &reply, nil)\nreplyCall := <- call1.Done\n```\n\n如果最后一个参数值为 `nil` ，调用完成后会创建一个新的通道。\n\n如果你有一个以 root 管理员身份运行的 Go 服务器，想要以不同的用户身份运行某部分代码，Brad Fitz 利用 `rpc` 写的 `go-runas` 包可以完成任务：[https://github.com/bradfitz/go-runas](https://github.com/bradfitz/go-runas)。我们将会在 [19 章](19.0.md)看到一个完整的项目，它是一个使用了 `rpc` 的应用程序。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[精巧的多功能网页服务器](15.8.md)\n- 下一节：[基于网络的通道 netchan](15.10.md)\n"
  },
  {
    "path": "eBook/16.0.md",
    "content": "# 16.0 常见的陷阱与错误\n\n在之前的内容中，有时候使用 `!!...!!` 标记警告 Go 语言中的一些错误使用方式。当你在编程时候遇到的一个困难，可以确定本书特定的章节能找到类似的主题。为了方便起见，这里列出了一些常见陷阱，以便于你能发现更多的解释和例子：\n\n- 永远不要使用形如 `var p*a` 声明变量，这会混淆指针声明和乘法运算（参考 [4.9 小节](04.9.md)）\n- 永远不要在 `for` 循环自身中改变计数器变量（参考 [5.4 小节](05.4.md)）\n- 永远不要在 `for-range` 循环中使用一个值去改变自身的值（参考 [5.4.4 小节](05.4.md)）\n- 永远不要将 `goto` 和前置标签一起使用（参考 [5.6 小节](05.6.md)）\n- 永远不要忘记在函数名（参考[第 6 章](06.0.md)）后加括号 `()`，尤其是调用一个对象的方法或者使用匿名函数启动一个协程时\n- 永远不要使用 `new()` 一个 `map`，一直使用 `make()`（参考[第 8 章](08.0.md)）\n- 当为一个类型定义一个 `String()` 方法时，不要使用 `fmt.Print` 或者类似的代码（参考 [10.7 小节](10.7.md)）\n- 永远不要忘记当终止缓存写入时，使用 `Flush()` 函数（参考 [12.2.3 小节](12.2.md)）\n- 永远不要忽略错误提示，忽略错误会导致程序崩溃（参考 [13.1 小节](13.1.md)）\n- 不要使用全局变量或者共享内存，这会使并发执行的代码变得不安全（参考 [14.1 小节](14.1.md)）\n- `println()` 函数仅仅是用于调试的目的\n\n最佳实践：对比以下使用方式：\n\n- 使用正确的方式初始化一个元素是切片的映射，例如 `map[type]slice`（参考 [8.1.3 小节](08.1.md)）\n- 一直使用逗号 ok 模式或者 checked 形式作为类型断言（参考 [11.3 小节](11.3.md)）\n- 使用一个工厂函数创建并初始化自己定义类型（参考 [10.2 小节](10.2.md)-[18.4 小节](18.4.md)）\n- 仅当一个结构体的方法想改变结构体时，使用结构体指针作为方法的接受者，否则使用一个结构体值类型 [10.6.3 小节](10.6.md)\n\n本章主要汇总了 Go 语言使用过程中最常见的错误和注意事项。在之前的章节已经涉及到了完整的示例和解释，你应该做的不仅仅是阅读这段的标题。\n\n## 链接\n\n- [目录](directory.md)\n- 上一章：[使用SMTP（简单邮件传输协议）发送邮件](15.12.md)\n- 下一节：[误用短声明导致变量覆盖](16.1.md)\n"
  },
  {
    "path": "eBook/16.1.md",
    "content": "# 16.1 误用短声明导致变量覆盖\n\n```go\nvar remember bool = false\nif something {\n    remember := true //错误\n}\n// 使用remember\n```\n\n在此代码段中，`remember` 变量永远不会在 `if` 语句外面变成 `true`，如果 `something` 为 `true`，由于使用了短声明 `:=`，`if` 语句内部的新变量 `remember` 将覆盖外面的 `remember` 变量，并且该变量的值为 `true`，但是在 `if` 语句外面，变量 `remember` 的值变成了 `false`，所以正确的写法应该是：\n\n```go\nif something {\n    remember = true\n}\n```\n\n此类错误也容易在 `for` 循环中出现，尤其当函数返回一个具名变量时难于察觉，例如以下的代码段：\n\n```go\nfunc shadow() (err error) {\n\tx, err := check1() // x 是新创建变量，err 是被赋值\n\tif err != nil {\n\t\treturn // 正确返回 err\n\t}\n\tif y, err := check2(x); err != nil { // y 和 if 语句中 err 被创建\n\t\treturn // if 语句中的 err 覆盖外面的 err，所以错误的返回 nil ！\n\t} else {\n\t\tfmt.Println(y)\n\t}\n\treturn\n}\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[常见的陷阱与错误](16.0.md)\n- 下一节：[误用字符串](16.2.md)\n"
  },
  {
    "path": "eBook/16.10.md",
    "content": "# 16.10 糟糕的错误处理\n\n译者注：该小结关于错误处理的观点，译者并不完全赞同，关于本小结的部分想法请参考 [关于 16.10.2 小节错误处理的一些见解](Discussion_about_16.10.md)。\n\n\n依附于[第 13 章](13.0.md)模式的描述和[第 17.1 小节](17.1.md)与[第 17.2.4 小节](17.2.md)的总结。\n\n## 16.10.1 不要使用布尔值：\n\n像下面代码一样，创建一个布尔型变量用于测试错误条件是多余的：\n\n```go\nvar good bool\n    // 测试一个错误，`good` 被赋为 `true` 或者 `false`\n    if !good {\n        return errors.New(\"things aren’t good\")\n    }\n```\n\n立即检测一个错误：\n\n```go\n... err1 := api.Func1()\nif err1 != nil { … }\n```\n\n## 16.10.2 避免错误检测使代码变得混乱：\n\n避免写出这样的代码：\n\n```go\n... err1 := api.Func1()\nif err1 != nil {\n    fmt.Println(\"err: \" + err.Error())\n    return\n}\nerr2 := api.Func2()\nif err2 != nil {\n...\n    return\n}    \n```\n\n首先，包括在一个初始化的 `if` 语句中对函数的调用。但即使代码中到处都是以 `if` 语句的形式通知错误（通过打印错误信息）。通过这种方式，很难分辨什么是正常的程序逻辑，什么是错误检测或错误通知。还需注意的是，大部分代码都是致力于错误的检测。通常解决此问题的好办法是尽可能以闭包的形式封装你的错误检测，例如下面的代码：\n\n```go\nfunc httpRequestHandler(w http.ResponseWriter, req *http.Request) {\n    err := func () error {\n        if req.Method != \"GET\" {\n            return errors.New(\"expected GET\")\n        }\n        if input := parseInput(req); input != \"command\" {\n            return errors.New(\"malformed command\")\n        }\n        // 可以在此进行其他的错误检测\n    } ()\n\n        if err != nil {\n            w.WriteHeader(400)\n            io.WriteString(w, err)\n            return\n        }\n        doSomething() ...\n```\n\n这种方法可以很容易分辨出错误检测、错误通知和正常的程序逻辑（更详细的方式参考[第 13.5 小节](13.5.md)）。\n\n**在开始阅读[第 17 章](17.0.md)前，先回答下列 2 个问题：**\n\n- 问题 16.1：总结你能记住的所有关于 `, ok` 模式的情况。\n\n- 问题 16.2：总结你能记住的所有关于 `defer` 模式的情况。\n\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[闭包和协程的使用](16.9.md)\n- 下一章：[Go 语言模式](17.0.md)\n"
  },
  {
    "path": "eBook/16.2.md",
    "content": "# 16.2 误用字符串\n\n当需要对一个字符串进行频繁的操作时，谨记在 go 语言中字符串是不可变的（类似 Java 和 C#）。使用诸如 `a += b` 形式连接字符串效率低下，尤其在一个循环内部使用这种形式。这会导致大量的内存开销和拷贝。**应该使用一个字符数组代替字符串，将字符串内容写入一个缓存中。** 例如以下的代码示例：\n\n```go\nvar b bytes.Buffer\n...\nfor condition {\n    b.WriteString(str) // 将字符串str写入缓存buffer\n}\n    return b.String()\n```\n\n注意：由于编译优化和依赖于使用缓存操作的字符串大小，当循环次数大于 15 时，效率才会更佳。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[误用短声明导致变量覆盖](16.1.md)\n- 下一节：[发生错误时使用 defer 关闭一个文件](16.3.md)\n"
  },
  {
    "path": "eBook/16.3.md",
    "content": "# 16.3 发生错误时使用 defer 关闭一个文件\n\n如果你在一个 `for` 循环内部处理一系列文件，你需要使用 `defer` 确保文件在处理完毕后被关闭，例如：\n\n```go\nfor _, file := range files {\n    if f, err = os.Open(file); err != nil {\n        return\n    }\n    // 这是错误的方式，当循环结束时文件没有关闭\n    defer f.Close()\n    // 对文件进行操作\n    f.Process(data)\n}\n```\n\n但是在循环内结尾处的 `defer` 没有执行，所以文件一直没有关闭！垃圾回收机制可能会自动关闭文件，但是这会产生一个错误，更好的做法是：\n\n```go\nfor _, file := range files {\n    if f, err = os.Open(file); err != nil {\n        return\n    }\n    // 对文件进行操作\n    f.Process(data)\n    // 关闭文件\n    f.Close()\n }\n```\n\n**`defer` 仅在函数返回时才会执行，在循环内的结尾或其他一些有限范围的代码内不会执行。**\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[误用字符串](16.2.md)\n- 下一节：[何时使用 new() 和 make()](16.4.md)\n"
  },
  {
    "path": "eBook/16.4.md",
    "content": "# 16.4 何时使用 new() 和 make()\n\n在第 [7.2.1 小节](07.2.md)和第 [10.2.2 小节](10.2.md)，我们已经讨论过此问题，并使用代码进行详细说明，观点如下：\n\n    - 切片、映射和通道，使用 make()\n    - 数组、结构体和所有的值类型，使用 new()\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[发生错误时使用 defer 关闭一个文件](16.3.md)\n- 下一节：[不需要将一个指向切片的指针传递给函数](16.5.md)\n"
  },
  {
    "path": "eBook/16.5.md",
    "content": "# 16.5 不需要将一个指向切片的指针传递给函数\n\n在第 [4.9 小节](04.9.md)，我们已经知道，切片实际是一个指向潜在数组的指针。我们常常需要把切片作为一个参数传递给函数是因为：实际就是传递一个指向变量的指针，在函数内可以改变这个变量，而不是传递数据的拷贝。\n\n因此应该这样做：\n\n```go\n       func findBiggest( listOfNumbers []int ) int {}\n```\n\n而不是：\n\n```go\n       func findBiggest( listOfNumbers *[]int ) int {}\n```\n\n**当切片作为参数传递时，切记不要解引用切片。**\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[何时使用 new() 和 make()](16.4.md)\n- 下一节：[使用指针指向接口类型](16.6.md)\n"
  },
  {
    "path": "eBook/16.6.md",
    "content": "# 16.6 使用指针指向接口类型\n\n查看如下程序：`nexter` 是一个接口类型，并且定义了一个 `next()` 方法读取下一字节。函数 `nextFew1` 将 `nexter` 接口作为参数并读取接下来的 `num` 个字节，并返回一个切片：这是正确做法。但是 `nextFew2` 使用一个指向 `nexter` 接口类型的指针作为参数传递给函数：当使用 `next()` 函数时，系统会给出一个编译错误：**`n.next undefined (type *nexter has no field or method next)`** （译者注：n.next 未定义（*nexter 类型没有 next 成员或 next 方法））\n\n例 16.1 [pointer_interface.go](examples/chapter_16/pointer_interface.go) （不能通过编译）:\n\n```go\npackage main\nimport (\n    \"fmt\"\n)\ntype nexter interface {\n    next() byte\n}\nfunc nextFew1(n nexter, num int) []byte {\n    var b []byte\n    for i:=0; i < num; i++ {\n        b[i] = n.next()\n    }\n    return b\n}\nfunc nextFew2(n *nexter, num int) []byte {\n    var b []byte\n    for i:=0; i < num; i++ {\n        b[i] = n.next() // 编译错误：n.next 未定义（*nexter 类型没有 next 成员或 next 方法）\n    }\n    return b\n}\nfunc main() {\n    fmt.Println(\"Hello World!\")\n}\n```\n\n**永远不要使用一个指针指向一个接口类型，因为它已经是一个指针。**\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[不需要将一个指向切片的指针传递给函数](16.5.md)\n- 下一节：[使用值类型时误用指针](16.7.md)\n"
  },
  {
    "path": "eBook/16.7.md",
    "content": "# 16.7 使用值类型时误用指针\n\n将一个值类型作为一个参数传递给函数或者作为一个方法的接收者，似乎是对内存的滥用，因为值类型一直是传递拷贝。但是另一方面，值类型的内存是在栈上分配，内存分配快速且开销不大。如果你传递一个指针，而不是一个值类型，Go 编译器大多数情况下会认为需要创建一个对象，并将对象移动到堆上，所以会导致额外的内存分配：因此当使用指针代替值类型作为参数传递时，我们没有任何收获。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[使用指针指向接口类型](16.6.md)\n- 下一节：[误用协程和通道](16.8.md)\n\n"
  },
  {
    "path": "eBook/16.8.md",
    "content": "# 16.8 误用协程和通道\n\n由于教学需要和对协程的工作原理有一个直观的了解，在[第 14 章](14.0.md) 使用了一些简单的算法，举例说明了协程和通道的使用，例如生产者或者迭代器。在实际应用中，你不需要并发执行，或者你不需要关注协程和通道的开销，在大多数情况下，通过栈传递参数会更有效率。\n\n但是，如果你使用 `break`、`return` 或者 `panic()` 去跳出一个循环，很有可能会导致内存溢出，因为协程正处理某些事情而被阻塞。在实际代码中，通常仅需写一个简单的过程式循环即可。**当且仅当代码中并发执行非常重要，才使用协程和通道。**\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[使用值类型时误用指针](16.7.md)\n- 下一节：[闭包和协程的使用](16.9.md)\n"
  },
  {
    "path": "eBook/16.9.md",
    "content": "# 16.9 闭包和协程的使用\n\n请看下面代码：\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"time\"\n)\n\nvar values = [5]int{10, 11, 12, 13, 14}\n\nfunc main() {\n    // 版本 A：\n    for ix := range values { // ix 是索引值\n        func() {\n            fmt.Print(ix, \" \")\n        }() // 调用闭包打印每个索引值\n    }\n    fmt.Println()\n    // 版本 B：和 A 版本类似，但是通过调用闭包作为一个协程\n    for ix := range values {\n        go func() {\n            fmt.Print(ix, \" \")\n        }()\n    }\n    fmt.Println()\n    time.Sleep(5e9)\n    // 版本 C：正确的处理方式\n    for ix := range values {\n        go func(ix interface{}) {\n            fmt.Print(ix, \" \")\n        }(ix)\n    }\n    fmt.Println()\n    time.Sleep(5e9)\n    // 版本 D：输出值：\n    for ix := range values {\n        val := values[ix]\n        go func() {\n            fmt.Print(val, \" \")\n        }()\n    }\n    time.Sleep(1e9)\n}\n```\n\n输出：\n\n```\n0 1 2 3 4\n\n4 4 4 4 4\n\n1 0 3 4 2\n\n10 11 12 13 14\n```\n\n版本 A 调用闭包 5 次打印每个索引值，版本 B 也做相同的事，但是通过协程调用每个闭包。按理说这将执行得更快，因为闭包是并发执行的。如果我们阻塞足够多的时间，让所有协程执行完毕，版本 B 的输出是：`4 4 4 4 4`。为什么会这样？在版本 B 的循环中，`ix` 变量实际是一个单变量，表示每个数组元素的索引值。因为这些闭包都只绑定到一个变量，这是一个比较好的方式，当你运行这段代码时，你将看见每次循环都打印最后一个索引值 `4`，而不是每个元素的索引值。因为协程可能在循环结束后还没有开始执行，而此时 `ix` 值是 `4`。\n\n版本 C 的循环写法才是正确的：调用每个闭包时将 `ix` 作为参数传递给闭包。`ix` 在每次循环时都被重新赋值，并将每个协程的 `ix` 放置在栈中，所以当协程最终被执行时，每个索引值对协程都是可用的。注意这里的输出可能是 `0 2 1 3 4` 或者 `0 3 1 2 4` 或者其他类似的序列，这主要取决于每个协程何时开始被执行。\n\n在版本 D 中，我们输出这个数组的值，为什么版本 B 不能而版本 D 可以呢？\n\n因为版本 D 中的变量声明是在循环体内部，所以在每次循环时，这些变量相互之间是不共享的，所以这些变量可以单独的被每个闭包使用。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[误用协程和通道](16.8.md)\n- 下一节：[糟糕的错误处理](16.10.md)\n"
  },
  {
    "path": "eBook/17.0.md",
    "content": "# 17.0 模式\n\n## 链接\n\n- [目录](directory.md)\n- 上一章：[糟糕的错误处理](16.10.md)\n- 下一节：[逗号 ok 模式](17.1.md)\n"
  },
  {
    "path": "eBook/17.1.md",
    "content": "# 17.1 逗号 ok 模式\n\n在学习本书第二部分和第三部分时，我们经常在一个表达式返回 2 个参数时使用这种模式：`something, ok`，第一个参数是一个值或者 `nil`，第二个参数是 `true`/`false` 或者一个错误 `error`。在一个需要赋值的 `if` 条件语句中，使用这种模式去检测第二个参数值会让代码显得优雅简洁。这种模式在 Go 语言编码规范中非常重要。下面总结了所有使用这种模式的例子：\n\n（1）在函数返回时检测错误（参考[第 5.2 小节](05.2.md)）:\n\n```go\nvalue, err := pack1.Func1(param1)\n\nif err != nil {\n    fmt.Printf(\"Error %s in pack1.Func1 with parameter %v\", err.Error(), param1)\n    return err\n}\n\n// 函数Func1没有错误:\nProcess(value)\n\ne.g.: os.Open(file) strconv.Atoi(str)\n```\n\n这段代码中的函数将错误返回给它的调用者，当函数执行成功时，返回的错误是 `nil`，所以使用这种写法：\n\n```go\nfunc SomeFunc() error {\n    …\n    if value, err := pack1.Func1(param1); err != nil {\n        …\n        return err\n    }\n    …\n    return nil\n}\n```\n\n这种模式也常用于通过 `defer` 使程序从 `panic` 中恢复执行（参考[第 17.2(4) 小节](17.2.md)）。\n\n要实现简洁的错误检测代码，更好的方式是使用闭包，参考[第 16.10.2 小节](16.10.md)\n\n（2）检测映射中是否存在一个键值（参考[第 8.2 小节](08.2.md)）：`key1` 在映射 `map1` 中是否有值？\n\n```go\nif value, isPresent = map1[key1]; isPresent {\n        Process(value)\n}\n// key1 不存在\n…\n```\n\n（3）检测一个接口类型变量 `varI` 是否包含了类型 `T`：类型断言（参考[第 11.3 小节](11.3.md)）：\n\n```go\nif value, ok := varI.(T); ok {\n    Process(value)\n}\n// 接口类型 varI 没有包含类型 T\n```\n\n（4）检测一个通道 `ch` 是否关闭（参考[第 14.3 小节](14.3.md)）：\n\n```go\n    for input := range ch {\n        Process(input)\n    }\n```\n\n或者:\n\n```go\n    for {\n        if input, open := <-ch; !open {\n            break // 通道是关闭的\n        }\n        Process(input)\n    }\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[Go 语言模式](17.0.md)\n- 下一节：[defer 模式](17.2.md)\n"
  },
  {
    "path": "eBook/17.2.md",
    "content": "# 17.2 defer 模式\n\n使用 `defer` 可以确保资源不再需要时，都会被恰当地关闭或归还到“池子”中。更重要的一点是，它可以恢复 panic。\n\n1. 关闭一个文件流：（见 [12.7节](12.7.md)）\n```go\n// 先打开一个文件 f\ndefer f.Close()\n```\n\n2. 解锁一个被锁定的资源（`mutex`）：（见 [9.3节](09.3.md)）\n```go\nmu.Lock()\ndefer mu.Unlock()\n```\n\n3. 关闭一个通道（如有必要）：\n```\nch := make(chan float64)\ndefer close(ch)\n```\n\n也可以是两个通道：\n```go\nanswerα, answerβ := make(chan int), make(chan int)\ndefer func() { close(answerα); close(answerβ) }()\n```\n\n4. 从 panic 恢复：（见 [13.3节](13.3.md)）\n```go\ndefer func() {\n\tif err := recover(); err != nil {\n\t\tlog.Printf(\"run time panic: %v\", err)\n\t}\n}()\n```\n\n5. 停止一个计时器：（见 [14.5节](14.5.md)）\n```go\ntick1 := time.NewTicker(updateInterval)\ndefer tick1.Stop()\n```\n\n6. 释放一个进程 p：（见 [13.6节](13.6.md)）\n```go\np, err := os.StartProcess(…, …, …)\ndefer p.Release()\n```\n\n7. 停止 CPU 性能分析并立即写入：（见 [13.10节](13.10.md)）\n```go\npprof.StartCPUProfile(f)\ndefer pprof.StopCPUProfile()\n```\n\n当然 `defer` 也可以在打印报表时避免忘记输出页脚。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[逗号 ok 模式](17.1.md)\n- 下一节：[可见性模式](17.3.md)\n"
  },
  {
    "path": "eBook/17.3.md",
    "content": "# 17.3 可见性模式\n\n我们在 [4.2.1 节](04.2.md)见过简单地使用可见性规则控制对类型成员的访问，他们可以是 Go 变量或函数。[10.2.1 节](10.2.md)展示了如何在单独的包中定义类型时，强制使用工厂函数。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[defer 模式](17.2.md)\n- 下一节：[运算符模式和接口](17.4.md)\n"
  },
  {
    "path": "eBook/17.4.md",
    "content": "# 17.4 运算符模式和接口\n\n运算符是一元或二元函数，它返回一个新对象而不修改其参数，类似 C++ 中的 `+` 和 `*`，特殊的中缀运算符（`+`，`-`，`*` 等）可以被重载以支持类似数学运算的语法。但除了一些特殊情况，Go 语言并不支持运算符重载：为了克服该限制，运算符必须由函数来模拟。既然 Go 同时支持面向过程和面向对象编程，我们有两种选择：\n\n## 17.4.1 函数作为运算符\n\n运算符由包级别的函数实现，以操作一个或两个参数，并返回一个新对象。函数针对要操作的对象，在专门的包中实现。例如，假设要在包 `matrix` 中实现矩阵操作，就会包含 `Add()` 用于矩阵相加，`Mult()` 用于矩阵相乘，他们都会返回一个矩阵。这两个函数通过包名来调用，因此可以创造出如下形式的表达式：\n```go\nm := matrix.Add(m1, matrix.Mult(m2, m3))\n```\n\n如果我们想在这些运算中区分不同类型的矩阵（稀疏或稠密），由于没有函数重载，我们不得不给函数起不同的名称，例如：\n```go\nfunc addSparseToDense (a *sparseMatrix, b *denseMatrix) *denseMatrix\nfunc addDenseToDense (a *denseMatrix, b *denseMatrix) *denseMatrix\nfunc addSparseToSparse (a *sparseMatrix, b *sparseMatrix) *sparseMatrix\n```\n\n这可不怎么优雅，我们能选择的最佳方案是将它们隐藏起来，作为包的私有函数，并暴露单一的 `Add()` 函数作为公共 API。可以在嵌套的 `switch` 断言中测试类型，以便在任何支持的参数组合上执行操作：\n```go\nfunc Add(a Matrix, b Matrix) Matrix {\n\tswitch a.(type) {\n\tcase sparseMatrix:\n\t\tswitch b.(type) {\n\t\tcase sparseMatrix:\n\t\t\treturn addSparseToSparse(a.(sparseMatrix), b.(sparseMatrix))\n\t\tcase denseMatrix:\n\t\t\treturn addSparseToDense(a.(sparseMatrix), b.(denseMatrix))\n\t\t…\n\t\t}\n\tdefault:\n\t\t// 不支持的参数\n\t\t…\n\t}\n}\n```\n\n然而，更优雅和优选的方案是将运算符作为方法实现，标准库中到处都运用了这种做法。有关 Ryanne Dolan 实现的线性代数包的更详细信息，可以在 https://github.com/skelterjohn/go.matrix 找到。\n\n## 17.4.2 方法作为运算符\n\n根据接收者类型不同，可以区分不同的方法。因此我们可以为每种类型简单地定义 `Add` 方法，来代替使用多个函数名称：\n```go\nfunc (a *sparseMatrix) Add(b Matrix) Matrix\nfunc (a *denseMatrix) Add(b Matrix) Matrix\n```\n\n每个方法都返回一个新对象，成为下一个方法调用的接收者，因此我们可以使用*链式调用*表达式：\n```go\nm := m1.Mult(m2).Add(m3)\n```\n比上一节面向过程的形式更简洁。\n\n正确的实现同样可以基于类型，通过 `switch` 类型断言在运行时确定：\n```go\nfunc (a *sparseMatrix) Add(b Matrix) Matrix {\n\tswitch b.(type) {\n\tcase sparseMatrix:\n\t\treturn addSparseToSparse(a.(sparseMatrix), b.(sparseMatrix))\n\tcase denseMatrix:\n\t\treturn addSparseToDense(a.(sparseMatrix), b.(denseMatrix))\n\t…\n\tdefault:\n\t\t// 不支持的参数\n\t\t…\n\t}\n}\n```\n\n再次地，这比上一节嵌套的 `switch` 更简单。\n\n## 17.4.3 使用接口\n\n当在不同类型上执行相同的方法时，创建一个通用化的接口以实现多态的想法，就会自然产生。\n\n例如定义一个代数 `Algebraic` 接口：\n```go\ntype Algebraic interface {\n\tAdd(b Algebraic) Algebraic\n\tMin(b Algebraic) Algebraic\n\tMult(b Algebraic) Algebraic\n\t…\n\tElements()\n}\n```\n\n然后为我们的 `matrix` 类型定义 `Add()`，`Min()`，`Mult()`，……等方法。\n\n每种实现上述 `Algebraic` 接口类型的方法都可以链式调用。每个方法实现都应基于参数类型，使用 `switch` 类型断言来提供优化过的实现。另外，应该为仅依赖于接口的方法，指定一个默认处理分支：\n```go\nfunc (a *denseMatrix) Add(b Algebraic) Algebraic {\n\tswitch b.(type) {\n\tcase sparseMatrix:\n\t\treturn addDenseToSparse(a, b.(sparseMatrix))\n\t…\n\tdefault:\n\t\tfor x in range b.Elements() …\n\t}\n}\n```\n\n如果一个通用的功能无法仅使用接口方法来实现，你可能正在处理两个不怎么相似的类型，此时应该放弃这种运算符模式。例如，如果 `a` 是一个集合而 `b` 是一个矩阵，那么编写 `a.Add(b)` 没有意义。就集合和矩阵运算而言，很难实现一个通用的 `a.Add(b)` 方法。遇到这种情况，把包拆分成两个，然后提供单独的 `AlgebraicSet` 和 `AlgebraicMatrix` 接口。\n\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[可见性模式](17.3.md)\n- 下一章：[出于性能考虑的实用代码片段](18.0.md)\n"
  },
  {
    "path": "eBook/18.0.md",
    "content": "# 18.0 出于性能考虑的实用代码片段\n\n## 链接\n\n- [目录](directory.md)\n- 上一章：[运算符模式和接口](17.4.md)\n- 下一节：[字符串](18.1.md)\n"
  },
  {
    "path": "eBook/18.1.md",
    "content": "# 18.1 字符串\n\n（1）如何修改字符串中的一个字符：\n\n```go\nstr:=\"hello\"\nc:=[]byte(str)\nc[0]='c'\ns2:= string(c) // s2 == \"cello\"\n```\n\n（2）如何获取字符串的子串：\n\n```go\nsubstr := str[n:m]\n```\n\n（3）如何使用 `for` 或者 `for-range` 遍历一个字符串：\n\n```go\n// gives only the bytes:\nfor i:=0; i < len(str); i++ {\n… = str[i]\n}\n// gives the Unicode characters:\nfor ix, ch := range str {\n…\n}\n```\n\n（4）如何获取一个字符串的字节数：`len(str)`\n\n   如何获取一个字符串的字符数：\n\n   （最快速）使用 `utf8.RuneCountInString(str)` \n\n   或使用 `len([]rune(str))` \n\n（5）如何连接字符串：\n\n   （最快速）使用 `bytes.Buffer`（参考[章节 7.2](07.2.md)）\n\n   或使用 `Strings.Join()`（参考[章节 4.7](04.7.md)）\n\n   或使用 `+=`：\n\n ```go\n str1 := \"Hello \" \n str2 := \"World!\"\n str1 += str2 //str1 == \"Hello World!\"\n ```\n\n（6）如何解析命令行参数：使用 `os` 或者 `flag` 包（参考[例 12.4](examples/chapter_12/fileinput.go)）\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[出于性能考虑的实用代码片段](18.0.md)\n- 下一节：[数组和切片](18.2.md)\n"
  },
  {
    "path": "eBook/18.10.md",
    "content": "# 18.10 其他\r\n\r\n如何在程序出错时终止程序：\r\n\r\n```go\t\r\nif err != nil {\r\n   fmt.Printf(\"Program stopping with error %v\", err)\r\n   os.Exit(1)\r\n}\r\n```\r\n\r\n或者：\r\n\r\n```go\r\nif err != nil { \r\n\tpanic(\"ERROR occurred: \" + err.Error())\r\n}\r\n```\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[网络和网页应用](18.9.md)\r\n- 下一节：[出于性能考虑的最佳实践和建议](18.11.md)\n"
  },
  {
    "path": "eBook/18.11.md",
    "content": "# 18.11 出于性能考虑的最佳实践和建议\r\n\r\n（1）尽可能的使用 `:=` 去初始化声明一个变量（在函数内部）；\r\n\r\n（2）尽可能的使用字符代替字符串；\r\n\r\n（3）尽可能的使用切片代替数组；\r\n\r\n（4）尽可能的使用数组和切片代替映射（详见参考文献 15）；\r\n\r\n（5）如果只想获取切片中某项值，不需要值的索引，尽可能的使用 `for range` 去遍历切片，这比必须查询切片中的每个元素要快一些；\r\n\r\n（6）当数组元素是稀疏的（例如有很多 `0` 值或者空值 `nil`），使用映射会降低内存消耗；\r\n\r\n（7）初始化映射时指定其容量；\r\n\r\n（8）当定义一个方法时，使用指针类型作为方法的接受者；\r\n\r\n（9）在代码中使用常量或者标志提取常量的值；\r\n\r\n（10）尽可能在需要分配大量内存时使用缓存；\r\n\r\n（11）使用缓存模板（参考[章节 15.7](15.7.md)）。\r\n\r\n\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[其他](18.10.md)\r\n- 下一章：[构建一个完整的应用程序](19.0.md)\r\n"
  },
  {
    "path": "eBook/18.2.md",
    "content": "# 18.2 数组和切片\r\n\r\n- 创建：\r\n\r\n  `arr1 := new([len]type)`\r\n\r\n  `slice1 := make([]type, len)`\r\n\r\n- 初始化：\r\n\r\n  `arr1 := [...]type{i1, i2, i3, i4, i5}`\r\n\r\n  `arrKeyValue := [len]type{i1: val1, i2: val2}`\r\n\r\n  `var slice1 []type = arr1[start:end]`\r\n\r\n（1）如何截断数组或者切片的最后一个元素：\r\n\r\n​      `line = line[:len(line)-1]`\r\n\r\n（2）如何使用 `for` 或者 `for-range` 遍历一个数组（或者切片）：\r\n\r\n```go\r\nfor i:=0; i < len(arr); i++ {\r\n… = arr[i]\r\n}\r\nfor ix, value := range arr {\r\n…\r\n}\r\n```\r\n\r\n（3）如何在一个二维数组或者切片 `arr2Dim` 中查找一个指定值 `V`：\r\n\r\n```go\r\nfound := false\r\nFound: for row := range arr2Dim {\r\n    for column := range arr2Dim[row] {\r\n        if arr2Dim[row][column] == V{\r\n            found = true\r\n            break Found\r\n        }\r\n    }\r\n}\r\n```\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[字符串](18.1.md)\r\n- 下一节：[映射](18.3.md)\r\n"
  },
  {
    "path": "eBook/18.3.md",
    "content": "# 18.3 映射\r\n\r\n创建：    `map1 := make(map[keytype]valuetype)`\r\n\r\n初始化：   `map1 := map[string]int{\"one\": 1, \"two\": 2}`\r\n\r\n（1）如何使用 `for` 或者 `for-range` 遍历一个映射：\r\n\r\n```go\r\nfor key, value := range map1 {\r\n…\r\n}\r\n```\r\n\r\n（2）如何在一个映射中检测键 `key1` 是否存在：\r\n\r\n   `val1, isPresent = map1[key1]`\r\n\r\n   返回值：键 `key1` 对应的值或者 `0`，`true` 或者 `false`\r\n\r\n（3）如何在映射中删除一个键：`delete(map1, key1)`\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[数组和切片](18.2.md)\r\n- 下一节：[结构体](18.4.md)\r\n"
  },
  {
    "path": "eBook/18.4.md",
    "content": "# 18.4 结构体\r\n\r\n- 创建：\r\n\r\n```go\r\ntype struct1 struct {\r\n    field1 type1\r\n    field2 type2\r\n    …\r\n}\r\nms := new(struct1)\r\n```\r\n\r\n- 初始化：\r\n\r\n```go\r\nms := &struct1{10, 15.5, \"Chris\"}\r\n```\r\n\r\n当结构体的命名以大写字母开头时，该结构体在包外可见。\r\n通常情况下，为每个结构体定义一个构建函数，并推荐使用构建函数初始化结构体（参考[例 10.2](examples/chapter_10/person.go)）：\r\n\r\n\r\n```go    \r\nms := Newstruct1{10, 15.5, \"Chris\"}\r\nfunc Newstruct1(n int, f float32, name string) *struct1 {\r\n    return &struct1{n, f, name} \r\n}\r\n```\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[映射](18.3.md)\r\n- 下一节：[接口](18.5.md)\r\n"
  },
  {
    "path": "eBook/18.5.md",
    "content": "# 18.5 接口\r\n\r\n（1）如何检测一个值 `v` 是否实现了接口 `Stringer`：\r\n\r\n```go\r\nif v, ok := v.(Stringer); ok {\r\n    fmt.Printf(\"implements String(): %s\\n\", v.String())\r\n}\r\n```\r\n\r\n（2）如何使用接口实现一个类型分类函数：\r\n\r\n```go\r\nfunc classifier(items ...interface{}) {\r\n    for i, x := range items {\r\n        switch x.(type) {\r\n        case bool:\r\n            fmt.Printf(\"param #%d is a bool\\n\", i)\r\n        case float64:\r\n            fmt.Printf(\"param #%d is a float64\\n\", i)\r\n        case int, int64:\r\n            fmt.Printf(\"param #%d is an int\\n\", i)\r\n        case nil:\r\n            fmt.Printf(\"param #%d is nil\\n\", i)\r\n        case string:\r\n            fmt.Printf(\"param #%d is a string\\n\", i)\r\n        default:\r\n            fmt.Printf(\"param #%d’s type is unknown\\n\", i)\r\n        }\r\n    }\r\n}\r\n```\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[结构体](18.4.md)\r\n- 下一节：[函数](18.6.md)\r\n"
  },
  {
    "path": "eBook/18.6.md",
    "content": "# 18.6 函数\r\n\r\n如何使用内建函数 `recover()` 终止 `panic()` 过程（参考[章节 13.3](13.3.md)）：\r\n\r\n```go\r\nfunc protect(g func()) {\r\n    defer func() {\r\n        log.Println(\"done\")\r\n        // Println executes normally even if there is a panic\r\n        if x := recover(); x != nil {\r\n            log.Printf(\"run time panic: %v\", x)\r\n        }\r\n    }()\r\n    log.Println(\"start\")\r\n    g()\r\n}\r\n```\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[接口](18.5.md)\r\n- 下一节：[文件](18.7.md)\r\n"
  },
  {
    "path": "eBook/18.7.md",
    "content": "# 18.7 文件\r\n\r\n（1）如何打开一个文件并读取：\r\n\r\n```go    \r\nfile, err := os.Open(\"input.dat\")\r\n  if err != nil {\r\n    fmt.Printf(\"An error occurred on opening the inputfile\\n\" +\r\n      \"Does the file exist?\\n\" +\r\n      \"Have you got acces to it?\\n\")\r\n    return\r\n  }\r\n  defer file.Close()\r\n  iReader := bufio.NewReader(file)\r\n  for {\r\n    str, err := iReader.ReadString('\\n')\r\n    if err != nil {\r\n      return // error or EOF\r\n    }\r\n    fmt.Printf(\"The input was: %s\", str)\r\n  }\r\n```\r\n\r\n（2）如何通过切片读写文件：\r\n```go\r\nfunc cat(f *file.File) {\r\n  const NBUF = 512\r\n  var buf [NBUF]byte\r\n  for {\r\n    switch nr, er := f.Read(buf[:]); true {\r\n    case nr < 0:\r\n      fmt.Fprintf(os.Stderr, \"cat: error reading from %s: %s\\n\",\r\n        f.String(), er.String())\r\n      os.Exit(1)\r\n    case nr == 0: // EOF\r\n      return\r\n    case nr > 0:\r\n      if nw, ew := file.Stdout.Write(buf[0:nr]); nw != nr {\r\n        fmt.Fprintf(os.Stderr, \"cat: error writing from %s: %s\\n\",\r\n          f.String(), ew.String())\r\n      }\r\n    }\r\n  }\r\n}\r\n```\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[函数](18.6.md)\r\n- 下一节：[协程 (goroutine) 与通道 (channel)](18.8.md)\r\n"
  },
  {
    "path": "eBook/18.8.md",
    "content": "# 18.8 协程 (goroutine) 与通道 (channel)\r\n\r\n出于性能考虑的建议：\r\n\r\n实践经验表明，为了使并行运算获得高于串行运算的效率，在协程内部完成的工作量，必须远远高于协程的创建和相互来回通信的开销。\r\n\r\n- 出于性能考虑建议使用带缓存的通道：\r\n\r\n  使用带缓存的通道可以很轻易成倍提高它的吞吐量，某些场景其性能可以提高至 10 倍甚至更多。通过调整通道的容量，甚至可以尝试着更进一步的优化其性能。\r\n\r\n- 限制一个通道的数据数量并将它们封装成一个数组：\r\n\r\n  如果使用通道传递大量单独的数据，那么通道将变成性能瓶颈。然而，将数据块打包封装成数组，在接收端解压数据时，性能可以提高至 10 倍。\r\n\r\n现在创建一个带缓存的通道：`ch := make(chan type,buf)`\r\n\r\n（1）如何使用 `for` 或者 `for-range` 遍历一个通道：\r\n\r\n```go\r\nfor v := range ch {\r\n    // do something with v\r\n}\r\n```\r\n\r\n（2）如何检测一个通道 `ch` 是否关闭：\r\n\r\n```go\r\n//read channel until it closes or error-condition\r\nfor {\r\n    if input, open := <-ch; !open {\r\n        break\r\n    }\r\n    fmt.Printf(\"%s\", input)\r\n}\r\n```\r\n\r\n   或者使用（1）自动检测。\r\n\r\n（3）如何通过一个通道让主程序等待直到协程完成（信号量模式）：\r\n\r\n```go\r\nch := make(chan int) // Allocate a channel.\r\n// Start something in a goroutine; when it completes, signal on the channel.\r\ngo func() {\r\n    // doSomething\r\n    ch <- 1 // Send a signal; value does not matter.\r\n}()\r\ndoSomethingElseForAWhile()\r\n<-ch // Wait for goroutine to finish; discard sent value.\r\n```\r\n\r\n   如果希望程序一直阻塞，在匿名函数中省略 `ch <- 1` 即可。\r\n\r\n（4）通道的工厂模板：以下函数是一个通道工厂，启动一个匿名函数作为协程以生产通道：\r\n\r\n```go\r\nfunc pump() chan int {\r\n    ch := make(chan int)\r\n    go func() {\r\n        for i := 0; ; i++ {\r\n            ch <- i\r\n        }\r\n    }()\r\n    return ch\r\n}\r\n```\r\n\r\n（5）通道迭代器模板：（注：这里原书没有写东西，但是应该是参考[章节 14.2.10](14.2.md)）\r\n\r\n（6）如何限制并发处理请求的数量：参考[章节 14.11](14.11.md)\r\n\r\n（7）如何在多核CPU上实现并行计算：参考[章节 14.13](14.13.md)\r\n\r\n\r\n（8）如何终止一个协程：`runtime.Goexit()`  \r\n\r\n（9）简单的超时模板：\r\n\r\n```go  \r\ntimeout := make(chan bool, 1)\r\ngo func() {\r\n    time.Sleep(1e9) // one second  \r\n    timeout <- true\r\n}()\r\nselect {\r\n    case <-ch:\r\n    // a read from ch has occurred\r\n    case <-timeout:\r\n    // the read from ch has timed out\r\n}\r\n```\r\n\r\n（10）如何使用输入通道和输出通道代替锁：\r\n\r\n```go\r\nfunc Worker(in, out chan *Task) {\r\n    for {\r\n        t := <-in\r\n        process(t)\r\n        out <- t\r\n    }\r\n}\r\n```\r\n\r\n（11）如何在同步调用运行时间过长时将之丢弃：参考[章节 14.5](14.5.md) 第二个变体\r\n\r\n\r\n（12）如何在通道中使用计时器和定时器：参考[章节 14.5](14.5.md)\r\n\r\n\r\n（13）典型的服务器后端模型：参考[章节 14.4](14.4.md)\r\n\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[文件](18.7.md)\r\n- 下一节：[网络和网页应用](18.9.md)\r\n"
  },
  {
    "path": "eBook/18.9.md",
    "content": "# 18.9 网络和网页应用\r\n\r\n## 18.9.1 模板：\r\n\r\n制作、解析并使模板生效：\r\n\r\n```go        \r\nvar strTempl = template.Must(template.New(\"TName\").Parse(strTemplateHTML))\r\n```\r\n\r\n在网页应用中使用 HTML 过滤器过滤 HTML 特殊字符：\r\n\r\n使用 `{{html .}}` 或者通过一个字段 `FieldName {{ .FieldName |html }}`\r\n\r\n使用缓存模板（参考[章节 15.7](15.7.md)） \r\n\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 上一节：[协程 (goroutine) 与通道 (channel)](18.8.md)\r\n- 下一节：[其他](18.10.md)\r\n"
  },
  {
    "path": "eBook/19.0.md",
    "content": "# 19.0 构建一个完整的应用程序\n\n## 链接\n\n- [目录](directory.md)\n- 上一章：[出于性能考虑的最佳实践和建议](18.11.md)\n- 下一节：[简介](19.1.md)\n"
  },
  {
    "path": "eBook/19.1.md",
    "content": "# 19.1 简介\n\n由于 web 无处不在，本章我们将开发一个完整的程序：`goto`，它是一个 web 缩短网址应用程序。示例来自 Andrew Gerrand 的讲座（见参考资料 22）。我们将把项目分成 3 个阶段，每一个都会比之前阶段包含更多的功能，并逐渐展示更多 Go 语言中的特性。我们会大量使用在 [15 章](15.0.md)所学的网页应用程序的知识。\n\n**版本 1：** 利用映射和结构体，与 `sync` 包的 `Mutex` 一起使用，以及一个结构体工厂。\n\n**版本 2：** 数据以 `gob` 格式写入文件以实现持久化。\n\n**版本 3：** 利用协程和通道重写应用（见 [14 章](14.0.md)）。\n\n**版本 4：** 如果我们要使用 json 格式的文件该如何修改？\n\n**版本 5：** 用 rpc 协议实现的分布式版本。\n\n由于代码变更频繁，不会展示在此处，仅给出访问地址。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[构建一个完整的应用程序](19.0.md)\n- 下一节：[短网址项目简介](19.2.md)\n"
  },
  {
    "path": "eBook/19.10.md",
    "content": "# 19.10 总结和增强\n\n通过逐步构建 goto 应用程序，我们遇到了几乎所有的 Go 语言特性。\n\n虽然这个程序按照我们的目标行事，仍然有一些可改进的途径：\n- *审美*：用户界面可以（极大地）美化。为此可以使用 Go 的 `template` 包（见 [15.7 节](15.7.md)）。\n- *可靠性*：master/slave 之间的 RPC 连接应该可以更可靠：如果客户端到服务器之间的连接中断，客户端应该尝试重连。用一个 \"dialer\" 协程可以达成。\n- *资源减负*：由于 URL 数据库大小不断增长，内存占用可能会成为一个问题。可以通过多台 master 服务器按照键分片来解决。\n- *删除*：要支持删除短 URL，master 和 slave 之间的交互将变得更复杂。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[使用代理缓存](19.9.md)\n- 下一章：[Google App Engine 中的 Go](20.0.md)\n"
  },
  {
    "path": "eBook/19.2.md",
    "content": "# 19.2 短网址项目简介\n\n你肯定知道有些浏览器中的地址（称为 URL）非常长且/或复杂，在网上有一些将他们转换成简短 URL 来使用的服务。我们的项目与此类似：它是具有 2 个功能的 *web 服务* (web service)：\n\n## 添加 (Add)\n\n给定一个较长的 URL，会将其转换成较短的版本，例如：\n```\nhttp://maps.google.com/maps?f=q&source=s_q&hl=en&geocode=&q=tokyo&sll=37.0625,-95.677068&sspn=68.684234,65.566406&ie=UTF8&hq=&hnear=Tokyo,+Japan&t=h&z=9\n```\n- (A) 转变为：`http://goto/UrcGq`\n- (B) 并保存这对数据\n\n## 重定向 (Redirect)\n\n短网址被请求时，会把用户重定向到原始的长 URL。因此如果你在浏览器输入网址 (B)，会被重定向到页面 (A)。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[简介](19.1.md)\n- 下一节：[数据结构](19.3.md)\n"
  },
  {
    "path": "eBook/19.3.md",
    "content": "# 版本 1 - 数据结构和前端界面\n\n第 1 个版本的代码 *goto_v1* 见 [goto_v1](examples/chapter_19/goto_v1)。\n\n# 19.3 数据结构\n\n（本节代码见 [goto_v1/store.go](examples/chapter_19/goto_v1/store.go)。）\n\n当程序运行在生产环境时，会收到很多短网址的请求，同时会有一些将长 URL 转换成短 URL 的请求。我们的程序要以什么样的结构存储这些数据呢？[19.2 节](19.2.md)中 (A) 和 (B) 两种 URL 都是字符串，此外，它们相互关联：给定键 (B) 能获取到值 (A)，他们互相*映射* (map)。要将数据存储在内存中，我们需要这种结构，它们几乎存在于所有的编程语言中，只是名称有所不同，例如“哈希表”或“字典”等。\n\nGo 语言就有这种内建的映射 (map)：`map[string]string`。\n\n键的类型写在 `[` 和 `]` 之间，紧接着是值的类型。有关映射的所有知识详见 [8 章](08.0.md)。为特定类型指定一个别名在严谨的程序中非常实用。Go 语言中通过关键字 `type` 来定义，因此有定义：\n```go\ntype URLStore map[string]string\n```\n它从短 URL 映射到长 URL，两者都是字符串。\n\n要创建那种类型的变量，并命名为 `m`，使用：\n```go\nm := make(URLStore)\n```\n\n假设 *http://goto/a* 映射到 *http://google.com/* ，我们要把它们存储到 m 中，可以用如下语句：\n```go\nm[\"a\"] = \"http://google.com/\"\n```\n（键只是 *http://goto/* 的后缀，其前缀总是不变的。）\n\n要获得给定 \"a\" 对应的长 URL，可以这么写：\n```go\nurl := m[\"a\"]\n```\n此时 `url` 的值等于 `http://google.com/`。\n\n注意，使用了 `:=` 就不需要指明 url 的类型为 `string`，编译器会从右侧的值中推断出来。\n\n## 使程序线程安全\n\n这里，变量 `URLStore` 是中心化的内存存储。当收到网络流量时，会有很多 `Redirect` 服务的请求。这些请求其实只涉及读操作：以给定的短 URL 作为键，返回对应的长 URL 的值。然而，对 `Add` 服务的请求则大不相同，它们会更改 `URLStore`，添加新的键值对。当在瞬间收到大量更新请求时，可能会产生如下问题：添加操作可能被另一个同类请求打断，写入的长 URL 值可能会丢失；另外，读取和更改同时进行，导致可能读到脏数据。代码中的 `map` 并不保证当开始更新数据时，会彻底阻止另一个更新操作的启动。也就是说，`map` 不是线程安全的，goto 会并发地为很多请求提供服务。因此必须使 `URLStore` 是线程安全的，以便可以从不同的线程访问它。最简单和经典的方法是为其增加一个锁，它是 Go 标准库 `sync` 包中的 `Mutex` 类型，必须导入到我们的代码中（关于锁详见 [9.3 节](09.3.md)）。\n\n现在，我们把 `URLStore` 类型的定义更改为一个结构体（就是字段的集合，类似 C 或 Java ，[10 章](10.0.md) 介绍了结构体），它含有两个字段：`map` 和 `sync` 包的 `RWMutex`：\n```go\nimport \"sync\"\ntype URLStore struct {\n\turls map[string]string\t\t// map from short to long URLs\n\tmu sync.RWMutex\n}\n```\n\n`RWMutex` 有两种锁：分别对应读和写。多个客户端可以同时设置读锁，但只有一个客户端可以设置写锁（以排除所有的读锁），有效地串行化变更，使他们按顺序生效。\n\n我们将在 `Get()` 函数中实现 `Redirect` 服务的读请求，在 `Set` 函数中实现 `Add` 服务的写请求。`Get()` 函数类似下面这样：\n```go\nfunc (s *URLStore) Get(key string) string {\n\ts.mu.RLock()\n\turl := s.urls[key]\n\ts.mu.RUnlock()\n\treturn url\n}\n```\n\n函数按照键（短 URL）返回对应映射后的 URL。它所处理的变量是指针类型（见 [4.9 节](04.9.md)），指向 `URLStore`。但在读取值之前，先用 `s.mu.RLock()` 放置一个读锁，这样就不会有更新操作妨碍读取。数据读取后撤销锁定，以便挂起的更新操作可以开始。如果键不存在于 map 中会怎样？会返回字符串的零值（空字符串）。注意点号 (`.`) 类似面向对象的语言：在 `s` 的 `mu` 字段上调用方法 `RLock()`。\n\n`Set()` 函数同时需要 URL 的键值对，且必须放置写锁 `Lock()` 来排除同一时刻任何其他更新操作。函数返回布尔值 `true` 或 `false` 来表示 `Set()` 操作是否成功：\n\n```go\nfunc (s *URLStore) Set(key, url string) bool {\n\ts.mu.Lock()\n\t_, present := s.urls[key]\n\tif present {\n\t\ts.mu.Unlock()\n\t\treturn false\n\t}\n\ts.urls[key] = url\n\ts.mu.Unlock()\n\treturn true\n}\n```\n\n形式 `_, present := s.urls[key]` 可以测试 `map` 中是否已经包含该键，包含则 `present` 为 `true`，否则为 `false`。这种形式称为“逗号 ok 模式”，在 Go 代码中会频繁出现。如果键已存在，`Set()` 函数直接返回布尔值 `false`，`map` 不会被更新（这样可以保证短 URL 不会重复）。如果键不存在，把它加入 `map` 中并返回 `true`。左侧 `_` 是一个值的占位符，赋值给 `_` 来表明我们不会使用它。注意在更新后尽早调用 `Unlock()` 来释放对 `URLStore` 的锁定。\n\n## 使用 defer 简化代码\n\n目前代码还比较简单，容易记得操作完成后调用 `Unlock()` 解锁。然而在代码更复杂时很容易忘记解锁，或者放置在错误的位置，往往导致问题很难追踪。对于这种情况 Go 提供了一个特殊关键字 `defer`（见 [6.4 节](06.4.md)）。在本例中，可以在 `Lock()` 之后立即示意 `Unlock()`，不过其效果是 `Unlock()` 只会在函数返回之前被调用。\n\n`Get()` 可以简化成以下代码（我们消除了本地变量 `url`）：\n\n```go\nfunc (s *URLStore) Get(key string) string {\n\ts.mu.RLock()\n\tdefer s.mu.RUnlock()\n\treturn s.urls[key]\n}\n```\n\n`Set()` 的逻辑在某种程度上也变得清晰了（我们不用再考虑解锁的事了）：\n\n```go\nfunc (s *URLStore) Set(key, url string) bool {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\t_, present := s.urls[key]\n\tif present {\n\t\treturn false\n\t}\n\ts.urls[key] = url\n\treturn true\n}\n```\n\n## URLStore 工厂函数\n\n`URLStore()` 结构体中包含 `map` 类型的字段，使用前必须先用 `make()` 初始化。在 Go 中创建一个结构体实例，一般是通过定义一个前缀为 `New`，能返回该类型已初始化实例的函数（通常是指向实例的指针）。\n\n```go\nfunc NewURLStore() *URLStore {\n\treturn &URLStore{ urls: make(map[string]string) }\n}\n```\n\n在 `return` 语句中，创建了 `URLStore` 字面量实例，其中包含初始化了的 `map` 映射。锁无需特别指明初始化，这是 Go 创建结构体实例的惯例。`&` 是取址运算符，它将我们要返回的内容变成指针，因为 `NewURLStore()` 返回类型是 `*URLStore`。然后调用该函数来创建 `URLStore` 变量：\n```go\nvar store = NewURLStore()\n```\n\n## 使用 URLStore\n\n要新增一对短/长 URL 到 `map` 中，我们只需调用 `s` 上的 `Set()` 方法，由于返回布尔值，可以把它包裹在 `if` 语句中：\n```go\nif s.Set(\"a\", \"http://google.com\") {\n\t// 成功\n}\n```\n\n要获取给定短 URL 对应的长 URL，调用 `s` 上的 `Get()` 方法，将返回值放入变量 `url`：\n```go\nif url := s.Get(\"a\"); url != \"\" {\n\t// 重定向到 url\n} else {\n\t// 键未找到\n}\n```\n\n这里我们利用 Go 语言 `if` 语句的特性，可以在起始部分、条件判断前放置初始化语句。另外还需要一个 `Count()` 方法以获取 `map` 中键值对的数量，可以使用内建的 `len()` 函数：\n```go\nfunc (s *URLStore) Count() int {\n\ts.mu.RLock()\n\tdefer s.mu.RUnlock()\n\treturn len(s.urls)\n}\n```\n\n如何根据给定的长 URL 计算出短 URL 呢？为此我们创建一个函数 `genKey(n int) string {…}`，将 `s.Count()` 的当前值作为其整型参数传入。（具体算法并不重要，示例代码可以在 [key.go](examples/chapter_19/goto_v1/key.go) 找到。）\n\n现在，我们可以创建一个 `Put()` 方法，接收一个长 URL，用 `genKey()` 生成其短 URL 键，调用 `Set()` 方法在此键下存储长 URL 数据，然后返回这个键：\n```go\nfunc (s *URLStore) Put(url string) string {\n\tfor {\n\t\tkey := genKey(s.Count())\n\t\tif s.Set(key, url) {\n\t\t\treturn key\n\t\t}\n\t}\n\t// shouldn’t get here\n\treturn \"\"\n}\n```\n\n`for` 循环会一直尝试调用 `Set()` 直到成功为止（意味着生成了一个尚未存在的短网址）。现在我们定义好了数据存储，以及配套的可工作的函数（见代码 [store.go](examples/chapter_19/goto_v1/store.go)）。但这本身并不能完成任务，我们还需要开发 web 服务器以交付 `Add` 和 `Redirect` 服务。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[短网址项目简介](19.2.md)\n- 下一节：[用户界面：web 服务端](19.4.md)\n"
  },
  {
    "path": "eBook/19.4.md",
    "content": "# 19.4 用户界面：web 服务端\n\n（本节代码见 [goto_v1/main.go](examples/chapter_19/goto_v1/main.go)。）\n\n我们尚未编写启动程序的必要函数。它们（总是）类似 C，C++ 或 Java 中的 `main()` 函数，我们的 web 服务器由它启动，例如用如下命令在本地 8080 端口启动 web 服务器：\n```go\nhttp.ListenAndServe(\":8080\", nil)\n```\n\n（web 服务器的功能来自于 `http` 包，[15 章](15.0.md) 做了深入介绍）。web 服务器会在一个无限循环中监听到来的请求，但我们必须定义针对这些请求，服务器该如何响应。可以用被称为 HTTP 处理器的 `HandleFunc` 函数来办到，例如代码：\n```go\nhttp.HandleFunc(\"/add\", Add)\n```\n如此，每个以 `/add` 结尾的请求都会调用 `Add` 函数（尚未完成）。\n\n程序有两个 HTTP 处理器：\n- `Redirect`，用于对短 URL 重定向\n- `Add`，用于处理新提交的 URL\n\n示意图：\n\n![](images/19.4_fig19.1.jpg?raw=true)\n\n最简单的 `main()` 函数类似这样：\n```go\nfunc main() {\n\thttp.HandleFunc(\"/\", Redirect)\n\thttp.HandleFunc(\"/add\", Add)\n\thttp.ListenAndServe(\":8080\", nil)\n}\n```\n\n对 `/add` 的请求由 `Add` 处理器处理，所有其他请求会被 `Redirect` 处理器处理。处理函数从到来的请求（一个类型为 `*http.Request` 的变量）中获取信息，然后产生响应并写入 `http.ResponseWriter` 类型变量 `w`。\n\n`Add` 函数必须做的事有：\n1. 读取长 URL，即：用 `r.FormValue(\"url\")` 从 HTML 表单提交的 HTTP 请求中读取 URL\n2. 使用 store 上的 `Put()` 方法存储长 URL\n3. 将对应的短 URL 发送给用户\n\n每个需求都转化为一行代码：\n```go\nfunc Add(w http.ResponseWriter, r *http.Request) {\n\turl := r.FormValue(\"url\")\n\tkey := store.Put(url)\n\tfmt.Fprintf(w, \"http://localhost:8080/%s\", key)\n}\n```\n\n这里 `fmt` 包的 `Fprintf()` 函数用来替换字符串中的关键字 `%s`，然后将结果作为响应发送回客户端。注意 `Fprintf()` 把数据写到了 `ResponseWriter` 中，其实 `Fprintf()` 可以将数据写到任何实现了 `io.Writer` 的数据结构，即该结构实现了 `Write()` 方法。Go 中 `io.Writer` 称为接口，可见 `Fprintf()` 利用接口变得十分通用，可以对很多不同的类型写入数据。Go 中接口的使用十分普遍，它使代码更通用（见 [11 章](11.0.md)）。\n\n还需要一个表单，仍然可以用 `Fprintf()` 来输出，这次将常量写入 `w`。让我们来修改 `Add()`，当未指定 URL 时显示 HTML 表单：\n```go\nfunc Add(w http.ResponseWriter, r *http.Request) {\n\turl := r.FormValue(\"url\")\n\tif url == \"\" {\n\t\tfmt.Fprint(w, AddForm)\n\t\treturn\n\t}\n\tkey := store.Put(url)\n\tfmt.Fprintf(w, \"http://localhost:8080/%s\", key)\n}\n\nconst AddForm = `\n<form method=\"POST\" action=\"/add\">\nURL: <input type=\"text\" name=\"url\">\n<input type=\"submit\" value=\"Add\">\n</form>\n`\n```\n\n在那种情况下，发送字符串常量 `AddForm` 到客户端，它是 html 表单，包含一个 `url` 输入域和一个提交按钮，点击后发送 POST 请求到 `/add`。这样 `Add()` 处理函数被再次调用，此时 `url` 的值来自文本域。（` `` ` 用来创建原始字符串，否则按惯例 `\"\"` 将成为字符串边界。）\n\n`Redirect()` 函数在 HTTP 请求路径中找到键（短 URL 的键是请求路径去除首字符，在 Go 中可以写为 `[1:]`。例如请求 \"/abc\"，键就是 \"abc\"），用 `Get()` 函数从 `store` 检索到对应的长 URL，对用户发送 HTTP 重定向。如果没找到 URL，发送 404 \"Not Found\" 错误取而代之：\n\n```go\nfunc Redirect(w http.ResponseWriter, r *http.Request) {\n\tkey := r.URL.Path[1:]\n\turl := store.Get(key)\n\tif url == \"\" {\n\t\thttp.NotFound(w, r)\n\t\treturn\n\t}\n\thttp.Redirect(w, r, url, http.StatusFound)\n}\n```\n\n（`http.NotFound()` 和 `http.Redirect()` 是发送通用 HTTP 响应的工具函数。）\n\n我们已经完整地遍历了 [goto_v1](examples/chapter_19/goto_v1) 的代码。\n\n## 编译和运行\n\n可执行程序已包含在示例代码下，如果你想立即测试可以跳过本节。其中包含 3 个 go 源文件和一个 Makefile 文件，通过它应用可以被编译和链接，只须如下操作：\n- **Linux 和 OSX 平台：** 在终端窗口源码目录下启动 `make` 命令，或在 LiteIDE 中构建项目。\n- **Windows 平台：** 启动 MINGW 环境，步骤为：开始菜单，所有程序，MinGW，MinGW Shell（见 [2.5.5 节](02.5.md)），在命令行窗口输入 `make` 并回车，源代码被编译并链接为原生 exe 可执行程序。\n\n生成内容为可执行程序，Linux/OS X 下为 `goto`，Windows 下为 `goto.exe`。\n\n要启动并运行 web 服务器，那么：\n- **Linux 和 OSX 平台：** 输入命令 `./goto`。\n- **Windows 平台：** 从 Go IDE 启动程序（如果 Windows 防火墙阻止程序启动，设置允许该程序）\n\n## 测试该程序\n\n打开浏览器并请求  url：`http://localhost:8080/add`\n\n这会激活 `Add()` 处理函数。请求还未包含 url 变量，所以响应会输出 html 表单询问输入：\n\n![](images/19.4_fig19.2.png?raw=true)\n\n添加一个长 URL 以获取等价的缩短版本，例如 `http://golang.org/pkg/bufio/#Writer`，然后单击按钮。应用会为你产生一个短 URL 并打印出来，例如 `http://\nlocalhost:8080/2`。\n\n![](images/19.4_fig19.3.jpg?raw=true)\n\n复制该 URL 并在浏览器地址栏粘贴以发出请求，现在轮到 `Redirect()` 处理函数上场了，对应长 URL 的页面被显示了出来。\n\n![](images/19.4_fig19.4.jpg?raw=true)\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[数据结构](19.3.md)\n- 下一节：[持久化存储：gob](19.5.md)\n"
  },
  {
    "path": "eBook/19.5.md",
    "content": "# 版本 2 - 添加持久化存储\n\n第 2 个版本的代码 *goto_v2* 见 [goto_v2](examples/chapter_19/goto_v2)。\n\n# 19.5 持久化存储：gob\n\n（本节代码见 [goto_v2/store.go](examples/chapter_19/goto_v2/store.go) 和 [goto_v2/main.go](examples/chapter_19/goto_v2/main.go)。）\n\n当 goto 进程（监听在 8080 端口的 web 服务器）终止，这迟早会发生，内存 `map` 中缩短的 URL 就会丢失。要保留这些数据，就得将其保存到磁盘文件中。我们将修改 `URLStore()`，使它可以保存数据到文件，且在 goto 启动时还原这些数据。为此我们使用 Go 标准库的 `encoding/gob` 包：它用于序列化和反序列化，将数据结构转换为字节数组（确切地说是切片），反之亦然（见 [12.11 节](12.11.md)）。\n\n通过 `gob` 包的 `NewEncoder()` 和 `NewDecoder()` 函数，可以指定数据要写入或读取的位置。返回的 `Encoder` 和 `Decoder` 对象提供了 `Encode` 和 `Decode` 方法，用于对文件写入和从中读取 Go 数据结构。提示：`Encoder` 实现了 `Writer` 接口，同样 `Decoder` 实现了 `Reader` 接口。我们在 `URLStore` 上增加一个新的 `file` 字段（`*os.File` 类型），它是用于读写已打开文件的句柄。\n\n\n```go\ntype URLStore struct {\n\turls map[string]string\n\tmu sync.RWMutex\n\tfile *os.File\n}\n```\n\n我们把这个文件命名为 store.gob，当初始化 `URLStore` 时将其作为参数传入：\n```go\nvar store = NewURLStore(\"store.gob\")\n```\n\n接着，调整 `NewURLStore()` 函数：\n```go\nfunc NewURLStore(filename string) *URLStore {\n\ts := &URLStore{urls: make(map[string]string)}\n\tf, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)\n\tif err != nil {\n\t\tlog.Fatal(\"URLStore:\", err)\n\t}\n\ts.file = f\n\treturn s\n}\n```\n\n现在，更新后的 `NewURLStore()` 函数接受一个文件名参数，它会打开该文件（见 [12 章](12.0.md)），将返回的 `*os.File` 作为 `file` 字段的值存储在 `URLStore` 变量 `store` 中，即这里的本地变量 `s` 。\n\n对 `OpenFile()` 的调用可能会失败（例如文件可能被删除或改名）。它会返回一个错误 `err`，注意 Go 是如何处理这种情况的：\n```go\nf, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)\nif err != nil {\n\tlog.Fatal(\"URLStore:\", err)\n}\n```\n\n当 `err` 不为 `nil`，表示确实发生了错误，那么输出一条消息并停止程序执行。这是处理错误的一种方式，大多数情况下错误应该返回给调用函数，但这种检测错误的模式在 Go 代码中也很普遍。在 `}` 之后可以确定文件被成功打开了。\n\n打开该文件时启用了写入标志，更精确地说是“追加模式”。每当一对新的短/长 URL 在程序中创建后，我们通过 `gob` 把它存储到文件 \"store.gob\" 中。\n\n为达到目的，定义一个新的结构体类型 `record`：\n```go\ntype record struct {\n\tKey, URL string\n}\n```\n\n以及新的 `save()` 方法，将给定的键和 URL 组成 `record` ，以 `gob` 编码的形式写入磁盘。\n```go\nfunc (s *URLStore) save(key, url string) error {\n\te := gob.NewEncoder(s.file)\n\treturn e.Encode(record{key, url})\n}\n```\n\ngoto 程序启动时，磁盘上存储的数据必须读取到 `URLStore` 的 `map` 中。为此，我们编写 `load` 方法：\n```go\nfunc (s *URLStore) load() error {\n\tif _, err := s.file.Seek(0, 0); err != nil {\n\t\treturn err\n\t}\n\td := gob.NewDecoder(s.file)\n\tvar err error\n\tfor err == nil {\n\t\tvar r record\n\t\tif err = d.Decode(&r); err == nil {\n\t\t\ts.Set(r.Key, r.URL)\n\t\t}\n\t}\n\tif err == io.EOF {\n\t\treturn nil\n\t}\n\treturn err\n}\n```\n\n这个新的 `load()` 方法会寻址 (`Seek`) 到文件的起始位置，读取并解码 (`Decode`) 每一条记录 (`record`)，然后用 `Set` 方法将数据存储到 `map` 中。再次注意无处不在的错误处理模式。文件的解码由一个无限循环完成，只要没有错误就会一直继续：\n```go\nfor err == nil {\n\t…\n}\n```\n\n如果得到了一个错误，可能是刚解码了最后一条记录，于是产生了 `io.EOF` (EndOfFile)  错误。若并非此种错误，表示产生了解码错误，用 `return err` 来返回它。对该方法的调用必须加入到 `NewURLStore()` 中：\n```go\nfunc NewURLStore(filename string) *URLStore {\n\ts := &URLStore{urls: make(map[string]string)}\n\tf, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)\n\tif err != nil {\n\t\tlog.Fatal(\"Error opening URLStore:\", err)\n\t}\n\ts.file = f\n\tif err := s.load(); err != nil {\n\t\tlog.Println(\"Error loading data in URLStore:\", err)\n\t}\n\treturn s\n}\n```\n\n同时在 `Put()` 方法中，当新的 URL 对加入到 `map` 中，也应该立即将它们保存到数据文件中：\n```go\nfunc (s *URLStore) Put(url string) string {\n\tfor {\n\t\tkey := genKey(s.Count())\n\t\tif s.Set(key, url) {\n\t\t\tif err := s.save(key, url); err != nil {\n\t\t\t\tlog.Println(\"Error saving to URLStore:\", err)\n\t\t\t}\n\t\t\treturn key\n\t\t}\n\t}\n\tpanic(\"shouldn’t get here\")\n}\n```\n\n编译并测试这第二个版本的程序，或直接使用现有的可执行程序，验证关闭服务器（在终端窗口可以按 CTRL+C）并重启后，短 URL 仍然有效。goto 程序第一次启动时，文件 store.gob 还不存在，因此当载入数据时会得到错误：\n\n\t2011/09/11 11:08:11 Error loading URLStore: open store.gob: The system cannot find the file specified.\n\n\n结束进程并重启后，就能正常工作了。或者，可以在 goto 启动前先创建空的 store.gob 文件。\n\n**备注：** 当第二次启动 goto 时，可能会产生错误：\n\n\tError loading URLStore: extra data in buffer\n\n这是由于 `gob` 是基于流的协议，它不支持重新开始。在版本 4 中，会用 json 作为存储协议来补救此问题。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[用户界面：web 服务端](19.4.md)\n- 下一节：[用协程优化性能](19.6.md)\n"
  },
  {
    "path": "eBook/19.6.md",
    "content": "# 版本 3 - 添加协程\n\n第 3 个版本的代码 *goto_v3* 见 [goto_v3](examples/chapter_19/goto_v3)。\n\n# 19.6 用协程优化性能\n\n如果有太多客户端同时尝试添加 URL，第 2 个版本依旧存在性能问题。得益于锁机制，我们的 `map` 可以在并发访问环境下安全地更新，但每条新产生的记录都要立即写入磁盘，这种机制成为了瓶颈。写入操作可能同时发生，根据不同操作系统的特性，可能会产生数据损坏。就算不产生写入冲突，每个客户端在 `Put()` 函数返回前，必须等待数据写入磁盘。因此，在一个 I/O 负载很高的系统中，客户端为了完成 `Add()` 请求，将等待更长的不必要的时间。\n\n为缓解该问题，必须对 `Put()` 和存储进程*解耦*：我们将使用 Go 的并发机制。我们不再将记录直接写入磁盘，而是发送到一个*通道*中，它是某种形式的缓冲区，因而发送函数不必等待它完成。\n\n保存进程会从该通道读取数据并写入磁盘。它是以 `saveLoop()` 协程启动的独立线程。现在 `main()` 和 `saveLoop()` 并行地执行，不会再发生阻塞。\n\n将 `URLStore` 的 `file` 字段替换为 `record` 类型的通道：`save chan record`。\n```go\ntype URLStore struct {\n\turls map[string]string\n\tmu sync.RWMutex\n\tsave chan record\n}\n```\n\n通道和 `map` 一样，必须用 `make()` 创建。我们会以此修改 `NewURLStore()` 工厂函数，并给定缓冲区大小为 1000，例如：`save := make(chan record, saveQueueLength)`。为解决性能问题，`Put` 可以发送记录 `record` 到带缓冲的 `save` 通道：\n```go\nfunc (s *URLStore) Put(url string) string {\n\tfor {\n\t\tkey := genKey(s.Count())\n\t\tif s.Set(key, url) {\n\t\t\ts.save <- record{key, url}\n\t\t\treturn key\n\t\t}\n\t}\n\tpanic(\"shouldn't get here\")\n}\n```\n\n`save` 通道的另一端必须有一个接收者：新的 `saveLoop()` 方法在独立的协程中运行，它接收 `record` 值并将它们写入到文件。`saveLoop()` 是在 `NewURLStore()` 函数中用 `go` 关键字启动的。现在，可以移除不必要的打开文件的代码。以下是修改后的 `NewURLStore()`：\n\n```go\nconst saveQueueLength = 1000\nfunc NewURLStore(filename string) *URLStore {\n\ts := &URLStore{\n\t\turls: make(map[string]string),\n\t\tsave: make(chan record, saveQueueLength),\n\t}\n\tif err := s.load(filename); err != nil {\n\t\tlog.Println(\"Error loading URLStore:\", err)\n\t}\n\tgo s.saveLoop(filename)\n\treturn s\n}\n```\n\n以下是 `saveLoop()` 方法的代码：\n```go\nfunc (s *URLStore) saveLoop(filename string) {\n\tf, err := os.Open(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)\n\tif err != nil {\n\t\tlog.Fatal(\"URLStore:\", err)\n\t}\n\tdefer f.Close()\n\te := gob.NewEncoder(f)\n\tfor {\n\t\t// taking a record from the channel and encoding it\n\t\tr := <-s.save\n\t\tif err := e.Encode(r); err != nil {\n\t\t\tlog.Println(\"URLStore:\", err)\n\t\t}\n\t}\n}\n```\n\n在无限循环中，记录从 `save` 通道读取，然后编码到文件中。\n\n我们在 [14 章](14.0.md) 深入学习了协程和通道，但在这里我们见到了实用的案例，更好地管理程序的不同部分。注意现在 `Encoder` 对象只被创建一次，而不是每次保存时都创建，这也可以节省了一些内存和运算处理。\n\n还有一个改进可以使 goto 更灵活：我们可以将文件名、监听地址和主机名定义为标志 (flag)，来代替在程序中硬编码或定义常量。这样当程序启动时，可以在命令行中指定它们的新值，如果没有指定，将采用 flag 的默认值。该功能来自另一个包，所以需要 `import \"flag\"`（这个包的更详细信息见 [12.4 节](12.4.md)）。\n\n先创建一些全局变量来保存 flag 的值：\n```go\nvar (\n\tlistenAddr = flag.String(\"http\", \":8080\", \"http listen address\")\n\tdataFile = flag.String(\"file\", \"store.gob\", \"data store file name\")\n\thostname = flag.String(\"host\", \"localhost:8080\", \"host name and port\")\n)\n```\n\n为了处理命令行参数，必须把 `flag.Parse()` 添加到 `main()` 函数中，在 flag 解析后才能实例化 `URLStore`，一旦得知了 `dataFile` 的值（在代码中使用了 `*dataFile`，因为 flag 是指针类型必须解除引用来获取值，见 [4.9 节](04.9.md)）：\n```go\nvar store *URLStore\nfunc main() {\n\tflag.Parse()\n\tstore = NewURLStore(*dataFile)\n\thttp.HandleFunc(\"/\", Redirect)\n\thttp.HandleFunc(\"/add\", Add)\n\thttp.ListenAndServe(*listenAddr, nil)\n}\n```\n\n现在 `Add()` 处理函数中须用 `*hostname` 替换 `localhost:8080`：\n```go\nfmt.Fprintf(w, \"http://%s/%s\", *hostname, key)\n```\n\n编译或直接使用现有的可执行程序测试第 3 个版本。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[持久化存储：gob](19.5.md)\n- 下一节：[以 json 格式存储](19.7.md)\n"
  },
  {
    "path": "eBook/19.7.md",
    "content": "# 版本 4 - 用 JSON 持久化存储\n\n第 4 个版本的代码 *goto_v4* 见 [goto_v4](examples/chapter_19/goto_v4)。\n\n# 19.7 以 json 格式存储\n\n如果你是个敏锐的测试者也许已经注意到了，当 goto 程序启动 2 次，第 2 次启动后能读取短 URL 且完美地工作。然而从第 3 次开始，会得到错误：\n\n\tError loading URLStore: extra data in buffer\n\n这是由于 gob 是基于流的协议，它不支持重新开始。为补救该问题，这里我们使用 json 作为存储协议（见 [12.9 节](12.9.md)），它以纯文本形式存储数据，因此也可以被非 Go 语言编写的进程读取。同时也显示了更换一种不同的持久化协议是多么简单，因为与存储打交道的代码被清晰地隔离在 2 个方法中，即 `load()` 和 `saveLoop()`。\n\n从创建新的空文件 store.json 开始，更改 main.go 中声明文件名变量的那一行：\n```go\nvar dataFile = flag.String(\"file\", \"store.json\", \"data store file name\")\n```\n\n在 store.go 中导入 `json` 取代 `gob`。然后在 `saveLoop()` 中唯一需要被修改的行：\n```go\ne := gob.NewEncoder(f)\n```\n\n更改为：\n```go\ne := json.NewEncoder(f)\n```\n\n类似的，在 `load` 方法中：\n```go\nd := gob.NewDecoder(f)\n```\n\n修改为：\n```go\nd := json.NewDecoder(f)\n```\n\n这就是所有要改动的地方！编译，启动并测试，你会发现之前的错误不会再发生了。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[用协程优化性能](19.6.md)\n- 下一节：[多服务器处理架构](19.8.md)\n"
  },
  {
    "path": "eBook/19.8.md",
    "content": "# 版本 5 - 分布式程序\n\n第 5 个版本的代码 *goto_v5*（[19.8 节](19.8.md)和 [19.9 节](19.9.md) 讨论）见 [goto_v5](examples/chapter_19/goto_v5)。该版本仍然基于 `gob` 存储，但很容易调整为使用 json，正如版本 4 演示的那样。\n\n# 19.8 多服务器处理架构\n\n目前为止 goto 以单线程运行，但即使用协程，在一台机器上运行的单一进程，也只能为一定数量的并发请求提供服务。一个缩短网址服务，相对于 `Add()`（用 `Put()` 写入），通常 `Redirect()` 服务（用 `Get()` 读取）要多得多。因此我们应该可以创建任意数量的只读的从 (slave) 服务器，提供服务并缓存 `Get()` 方法调用的结果，将 `Put()` 请求转发给主 (master) 服务器，类似如下架构：\n\n![图 19.5 跨越主从计算机的分布式负载](images/19.8_fig19.5.jpg?raw=true)\n\n对于 slave 进程，要在网络上运行 goto 应用的一个 master 节点实例，它们必须能相互通信。Go 的 `rpc` 包为跨越网络发起函数调用提供了便捷的途径。这里将把 `URLStore` 变为 RPC 服务（[15.9 节](15.9.md) 详细讨论了 `rpc` 包）。slave 进程将应对 `Get()` 请求以交付长 URL。当一个长 URL 要被转换为缩短版本（使用 `Put()` 方法）时，它们通过 rpc 连接把任务委托给 master 进程，因此只有 master 节点会写入数据文件。\n\n截至目前 `URLStore` 上基本的 `Get()` 和 `Put()` 方法具有如下签名：\n```go\nfunc (s *URLStore) Get(key string) string\nfunc (s *URLStore) Put(url string) string\n```\n\n而 RPC 调用仅能使用如下形式的方法（`t` 是 `T` 类型的值）：\n```go\nfunc (t T) Name(args *ArgType, reply *ReplyType) error\n```\n\n要使 `URLStore` 成为 RPC 服务，需要修改 `Put()` 和 `Get()` 方法使它们符合上述函数签名。以下是修改后的签名：\n```go\nfunc (s *URLStore) Get(key, url *string) error\nfunc (s *URLStore) Put(url, key *string) error\n```\n\n`Get()` 代码变更为：\n\n```go\nfunc (s *URLStore) Get(key, url *string) error {\n\ts.mu.RLock()\n\tdefer s.mu.RUnlock()\n\tif u, ok := s.urls[*key]; ok {\n\t\t*url = u\n\t\treturn nil\n\t}\n\treturn errors.New(\"key not found\")\n}\n```\n\n现在，键和长 URL 都变成了指针，必须加上前缀 `*` 来取得它们的值，例如 `*key` 这种形式。`u` 是一个值，可以用 `*url = u` 来将其赋值给指针。\n\n接着对 `Put()` 代码做同样的改动：\n```go\nfunc (s *URLStore) Put(url, key *string) error {\n\tfor {\n\t\t*key = genKey(s.Count())\n\t\t\tif err := s.Set(key, url); err == nil {\n\t\t\tbreak\n\t\t}\n\t}\n\tif s.save != nil {\n\t\ts.save <- record{*key, *url}\n\t}\n\treturn nil\n}\n```\n\n`Put()` 调用 `Set()`，由于后者也要做调整，`key` 和 `url` 参数现在是指针类型，还必须返回 `error` 取代 `boolean`：\n```go\nfunc (s *URLStore) Set(key, url *string) error {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\tif _, present := s.urls[*key]; present {\n\t\treturn errors.New(\"key already exists\")\n\t}\n\ts.urls[*key] = *url\n\treturn nil\n}\n```\n\n同样，当从 `load()` 调用 `Set()` 时，也必须做调整：\n```go\ns.Set(&r.Key, &r.URL)\n```\n\n还必须修改 HTTP 处理函数以适应 `URLStore` 上的更改。`Redirect()` 处理函数现在返回 `URLStore` 给出错误的字符串形式：\n```go\nfunc Redirect(w http.ResponseWriter, r *http.Request) {\n\tkey := r.URL.Path[1:]\n\tvar url string\n\tif err := store.Get(&key, &url); err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\thttp.Redirect(w, r, url, http.StatusFound)\n}\n```\n\n`Add()` 处理函数也以基本相同的方式修改：\n\n```go\nfunc Add(w http.ResponseWriter, r *http.Request) {\n\turl := r.FormValue(\"url\")\n\tif url == \"\" {\n\t\tfmt.Fprint(w, AddForm)\n\t\treturn\n\t}\n\tvar key string\n\tif err := store.Put(&url, &key); err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\tfmt.Fprintf(w, \"http://%s/%s\", *hostname, key)\n}\n```\n\n要使应用程序更灵活，正如之前章节所为，可以添加一个命令行标志 (flag) 来决定是否在 `main()` 函数中启用 RPC 服务器：\n```go\nvar rpcEnabled = flag.Bool(\"rpc\", false, \"enable RPC server\")\n```\n\n要使 RPC 工作，还要用 `rpc` 包来注册 `URLStore`，并用 `HandleHTTP()` 创建基于 HTTP 的 RPC 处理器：\n```go\nfunc main() {\n\tflag.Parse()\n\tstore = NewURLStore(*dataFile)\n\tif *rpcEnabled { // flag has been set\n\t\trpc.RegisterName(\"Store\", store)\n\t\trpc.HandleHTTP()\n\t}\n\t... (set up http like before)\n}\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[以 json 格式存储](19.7.md)\n- 下一节：[使用代理缓存](19.9.md)\n"
  },
  {
    "path": "eBook/19.9.md",
    "content": "# 19.9 使用代理缓存\n\n`URLStore` 已经成为了有效的 RPC 服务，现在可以创建另一种代表 RPC 客户端的类型，它会转发请求到 RPC 服务器，我们称它为 `ProxyStore`。\n```go\ntype ProxyStore struct {\n\tclient *rpc.Client\n}\n```\n\n一个 RPC 客户端必须使用 `DialHTTP()` 方法连接到服务器，所以我们把这句加入 `NewProxyStore()` 函数，它用于创建 `ProxyStore` 对象。\n```go\nfunc NewProxyStore(addr string) *ProxyStore {\n\tclient, err := rpc.DialHTTP(\"tcp\", addr)\n\tif err != nil {\n\t\tlog.Println(\"Error constructing ProxyStore:\", err)\n\t}\n\treturn &ProxyStore{client: client}\n}\n```\n\n`ProxyStore` 有 `Get()` 和 `Put()` 方法，它们利用 RPC 客户端的 `Call()` 方法，将请求直接传递给服务器：\n\n```go\nfunc (s *ProxyStore) Get(key, url *string) error {\n\treturn s.client.Call(\"Store.Get\", key, url)\n}\n\nfunc (s *ProxyStore) Put(url, key *string) error {\n\treturn s.client.Call(\"Store.Put\", url, key)\n}\n```\n\n## 带缓存的 ProxyStore\n\n可是，如果 slave 进程只是简单地代理所有的工作到 master 节点，不会得到任何增益！我们打算用 slave 节点来应对 `Get()` 请求。要做到这点，它们必须有 `URLStore` 中 `map` 的一份副本（缓存）。因此我们对 `ProxyStore` 的定义进行扩展，将 `URLStore` 包含在其中：\n```go\ntype ProxyStore struct {\n\turls *URLStore\n\tclient *rpc.Client\n}\n```\n\n`NewProxyStore()` 也必须做修改：\n\n```go\nfunc NewProxyStore(addr string) *ProxyStore {\n\tclient, err := rpc.DialHTTP(\"tcp\", addr)\n\tif err != nil {\n\t\tlog.Println(\"ProxyStore:\", err)\n\t}\n\treturn &ProxyStore{urls: NewURLStore(\"\"), client: client}\n}\n```\n\n还必须修改 `NewURLStore()` 以便给出空文件名时，不会尝试从磁盘写入或读取文件：\n```go\nfunc NewURLStore(filename string) *URLStore {\n\ts := &URLStore{urls: make(map[string]string)}\n\tif filename != \"\" {\n\t\ts.save = make(chan record, saveQueueLength)\n\t\tif err := s.load(filename); err != nil {\n\t\t\tlog.Println(\"Error loading URLStore: \", err)\n\t\t}\n\t\tgo s.saveLoop(filename)\n\t}\n\treturn s\n}\n```\n\n`ProxyStore` 的 `Get()` 方法需要扩展：**它应该首先检查缓存中是否有对应的键**。如果有，`Get()` 返回已缓存的结果。否则，应该发起 RPC 调用，然后用返回结果更新其本地缓存：\n\n```go\nfunc (s *ProxyStore) Get(key, url *string) error {\n\tif err := s.urls.Get(key, url); err == nil { // url found in local map\n\t\treturn nil\n\t}\n\t// url not found in local map, make rpc-call:\n\tif err := s.client.Call(\"Store.Get\", key, url); err != nil {\n\t\treturn err\n\t}\n\ts.urls.Set(key, url)\n\treturn nil\n}\n```\n\n同样地，`Put()` 方法仅当成功完成了远程 RPC `Put()` 调用，才更新本地缓存：\n```go\nfunc (s *ProxyStore) Put(url, key *string) error {\n\tif err := s.client.Call(\"Store.Put\", url, key); err != nil {\n\t\treturn err\n\t}\n\ts.urls.Set(key, url)\n\treturn nil\n}\n```\n\n## 汇总\n\nslave 节点使用 `ProxyStore`，只有 master 使用 `URLStore`。有鉴于创造它们的方式，它们看上去十分一致：两者都实现了相同签名的 `Get()` 和 `Put()` 方法，因此我们可以指定一个 `Store` 接口来概括它们的行为：\n```go\ntype Store interface {\n\tPut(url, key *string) error\n\tGet(key, url *string) error\n}\n```\n\n现在全局变量 `store` 可以成为 `Store` 类型：\n```go\nvar store Store\n```\n\n最后，我们改写 `main()` 函数以便程序只作为 master 或 slave 启动（我们只能这么做，因为现在 store 是 `Store` 接口类型！）。\n\n为此我们添加一个没有默认值的新命令行标志 `masterAddr`。\n```go\nvar masterAddr = flag.String(\"master\", \"\", \"RPC master address\")\n```\n\n如果给出 master 地址，就启动一个 slave 进程并创建新的 `ProxyStore`；否则启动 master 进程并创建新的 `URLStore`：\n```go\nfunc main() {\n\tflag.Parse()\n\tif *masterAddr != \"\" { // we are a slave\n\t\tstore = NewProxyStore(*masterAddr)\n\t} else { // we are the master\n\t\tstore = NewURLStore(*dataFile)\n\t}\n\t...\n}\n```\n\n这样，我们已启用了 `ProxyStore` 作为 web 前端，以代替 `URLStore`。\n\n其余的前端代码继续和之前一样地工作，它们不必在意 `Store` 接口。只有 master 进程会写数据文件。\n\n现在可以加载一个 master 节点和数个 slave 节点，对 slave 进行压力测试。\n\n编译这个版本 4 或直接使用现有的可执行程序。\n\n要进行测试，首先在命令行用以下命令启动 master 节点：\n```bash\n./goto -http=:8081 -rpc=true\t# （Windows 平台用 goto 代替 ./goto）\n```\n这里提供了 2 个标志：master 监听 8081 端口，已启用 RPC。\n\nslave 节点用以下命令启动：\n```bash\n./goto -master=127.0.0.1:8081\n```\n\n它获取到 master 的地址，并在 8080 端口接受客户端请求。\n\n在源码目录下已包含了以下 shell 脚本 [demo.sh](examples/chapter_19/goto_v5/demo.sh)，用来在类 Unix 系统下自动启动程序：\n```bash\n#!/bin/sh\ngomake\n./goto -http=:8081 -rpc=true &\nmaster_pid=$!\nsleep 1\n./goto -master=127.0.0.1:8081 &\nslave_pid=$!\necho \"Running master on :8081, slave on :8080.\"\necho \"Visit: http://localhost:8080/add\"\necho \"Press enter to shut down\"\nread\nkill $master_pid\nkill $slave_pid\n```\n\n要在 Windows 下测试，启动 MINGW shell 并启动 master，然后每个 slave 都要单独启动新的 MINGW shell 并启动 slave 进程。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[多服务器处理架构](19.8.md)\n- 下一节：[总结和增强](19.10.md)\n"
  },
  {
    "path": "eBook/20.0.md",
    "content": "# 20.0 Google App Engine 中的 Go\n\n本章中的网站地址和原书有所出入，但并不影响，因为 Google 已经对这些网址做了重定向。自这本书出版以来，GAE 的安装方式和使用方式已经发生了变化，原书内容仅供参考，请以[文档](https://cloud.google.com/docs)或网站指引为准。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[总结与增强](19.10.md)\n- 下一节：[什么是 Google App Engine？](20.1.md)\n"
  },
  {
    "path": "eBook/20.1.md",
    "content": "# 20.1 什么是 Google App Engine？\n\nGoogle 应用引擎（Google App Engine，下简称 GAE）是 Google 用来**云编程**的方案：让你在 Google 的基础架构上运行 web 应用和存储数据，而不用担心服务器、网络、操作系统或者数据存储等等问题。这种资源的集合通常被称为云，其维护完全由谷歌本身负责。对于你这个开发者来说，只有你的应用程序和它能提供给用户的服务才是重要的。用户可以在任何可以连接到互联网的设备上使用和运行你的应用程序，你只需为你的软件真正需要的资源（CPU 处理时间、网络带宽、磁盘存储、内存等）付费。当有高峰期时，云平台会自动为你的应用程序增加资源，并在不再需要时减少资源：可扩展性是云计算的最大优势之一。协作型应用（一群人一起工作、分享数据、交流等）、提供服务的应用和进行大型计算的应用是云计算的优秀候选者。云计算应用的典型用户界面是一个浏览器环境。\n\nGAE 在 2008 年推出，并支持 Python 应用程序，并在 2009 年增加了对于 Java 的支持；自 2011 年开始，也有了对 Go 的支持。其开始页面为：https://cloud.google.com/appengine/\n\n谷歌的 App Engine 为构建和部署网络应用提供了一种可靠、可扩展和简单的方式。超过十万个应用程序被托管在 https://console.cloud.google.com/ 和使用 App Engine 基础设施的自定义域上。它是一个 \"平台即服务 \"的环境，比 Amazon EC2 这样的 \"基础云设施 \"的运行水平更高，其试图以更高的效率分享资源。\n\n<u>沙盒：</u>\n\n你的应用在一个叫做*“沙盒 (sandbox)”*的环境中运行，其提供到底层操作系统有限的访问权。这些限制能够允许 App Engine 在多个服务器上分配应用程序的网络请求，并启动和停止服务器以满足流量需求。沙盒将你的应用程序隔离在它自己的安全、可靠的环境中，它独立于 Web 服务器的硬件、操作系统和物理位置。上文中所说的限制有：\n\n- 应用程序不能写到服务器的文件系统；只有在应用程序内上传的文件可以被读取。应用必须使用 *App Engine 数据存储、记忆库 (memcahe)* 或其他服务来处理所有在请求之间持续存在的数据。\n- 代码仅在响应网络请求、排队或计划任务时运行，并且响应必须在 60 秒内；请求处理程序不能产生一个子进程或在响应发出后执行代码。\n- 它只能通过提供的 URL 获取和电子邮件服务访问互联网上的其他计算机。其他计算机只能通过在标准 HTTP 协议（或 HTTPS）下的请求来连接到应用程序。\n\n<u>服务概览：</u>\n\n1. <u>数据</u> 存储在基于谷歌 Bigtable 的 GAE *数据存储*中：这是一个分布式数据存储服务，具有查询引擎和交易功能；它随着你的数据自动增长。它不是一个传统的关系型数据库，所以不允许使用经典的 SQL 和连接；但它为你提供了一种类似 SQL 的查询语言，称为 *GQL*。数据对象，称为*实体*，有一个*类型*和一组属性。查询可以检索给定种类的实体，并根据属性值进行过滤和排序。属性值可以是任何支持的属性值类型。实体可以被分组——这就是交易 (transaction) 发生的地方：任何的交易都必须在一个组内。你的实体没有数据库模式：实体之间的任何结构必须由你的应用程序代码提供和强制执行。更新使用*乐观的并发控制 (optimistic concurrency control)*，意味着依照最后一次更新改变数据。\n\n2) <u>应用程序认证</u> 可以与谷歌账户集成\n3) <u>URL Fetch</u>：通过这项服务，你的应用程序可以访问互联网上的资源，如网络服务或其他数据。\n4) <u>邮件</u>：也是一个内置的服务，可以在应用程序中使用。\n5) <u>Memcache</u>：一个高性能的、内存内的键值对缓存；它对那些不需要数据存储的持久性和事务性功能的数据很有用，比如临时数据或从数据存储复制到缓存的高速访问数据。\n6) <u>图像操作</u>：（译者注：原文这里就没有东西）\n7) <u>预定任务和任务队列</u> (cron jobs)：一个除了响应网络请求外，还可以执行任务的应用程序；它可以按照你配置的时间表执行这些任务，比如在一个每天的或每小时的基数上运行。另外，应用程序也可以执行由应用程序本身分配到队列中的任务，例如在处理一个请求时创建的后台任务。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[Google 应用引擎中的 Go](20.0.md)\n- 下一节：[云上的 Go](20.2.md)\n"
  },
  {
    "path": "eBook/20.2.md",
    "content": "# 20.2 云上的 Go\n\n2011 年 5 月10 日，在谷歌 I/O 大会上首次宣布了 GAE 对 Go 的支持。其最初是试验性的，只针对注册的测试人员，到 2011 年 7 月 21 日才完全对每个开发者开放。在撰写本文时（2012 年 1 月），目前的 Go App Engine SDK 是 1.6.1（2011-12-13 发布）；它只存在于 Linux 和 Mac OS X（10.5 或更高版本），包括 32 和 64 位。支持的 Go 工具链是 r60.3 版本；一些变化是向后不兼容的，其 SDK 的 api_version 是 3。\n\n当 Go 应用在 App Engine上运行时，它是用 64 位 x86 编译器 (6g) 编译的。在一个给定的实例中只运行一个线程。也就是说，所有的 goroutines 都在同一个操作系统的线程中运行，所以对于一个给定的客户请求来说，没有 CPU 并行性可言。\n\nGo 是第一个在 App Engine 上运行的编译语言。它之所以能大放异彩，是因为它与其他两个语言运行时相比，表现非常出色。\n\n- 和 Java 相比：Go 有更好的实例启动时间和更多的并发可能性。\n- 和 Python 相比：Go 的执行速度要好得多。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[什么是 Google App Engine？](20.1.md)\n- 下一节：[安装 Go App Engine SDK：为 Go 部署的开发环境](20.3.md)"
  },
  {
    "path": "eBook/20.3.md",
    "content": "# 20.3 安装 Go App Engine SDK：为 Go 部署的开发环境\n\n## 20.3.1 安装\n\n从下载页面下载合适你的系统的 GAE SDK 压缩文件：https://cloud.google.com/appengine/downloads\n\n例如：你的系统是 64 位的 Linux Ubuntu 11.10 系统，则下载 go_appengine_sdk_linux_amd64-1.6.1.zip 文件。\n\n用 Archieve Manager 打开并且提取出到你选择的一个目录下（例如你的 home 目录）：它会创造一个叫做 google_appengine 的文件，其包含了整个 AppEngine for Go 的开发环境。例如在 /home/user/google_appengine 或者 \"install root\"/google_appengine/goroot 目录下。\n\n这个环境包含了您在本地开发、构建和测试您的应用程序所需的一切：它包括一个 AppEngine 服务器来测试您的应用程序，一个 DataStore，用来在这里存储数据，就像您最终在 AppEngine 服务器上托管的实时应用程序一样；以及其他 API 支持和工具，使您可以模仿真正的 AppEngine 来进行开发和测试的目的。由于这个 AppEngine 环境是针对 Go 的，它也包含了 适当的 Go 编译器、软件包和工具作为下载的一部分。\n\n<u>GAE-Go 和普通 Go 之间的区别：</u>\n\nGAE-Go 的运行时 (runtime) 提供完整的 Go 语言和几乎所有的标准库，除了一些在 App Engine 环境中没有意义的东西：\n\n- 现在没有 `unsafe` 包了，并且 `syscall` 包被修剪过了。\n- 它不支持 cgo（不与 C 库交互），甚至：你不能在 GAE 项目中使用任何二进制库（Go 或其他）。你需要回溯所有东西的源，直到 GAE 编译/链接到了所有的源代码。\n- 不支持 go install 工具\n- GAE 经常落后于主发行版一个或多个主要版本。此外，必须考虑到沙盒环境的限制（参考 [20.1 节](20.1.md)）。因此，如果尝试打开一个 socket 或写一个文件将返回一个 `os.EINVAL` 错误。\n\n因此，把你的 GAE 和非 GAE-Go 工具完全分开；如果你只做 GAE 开发，你可以完全不使用标准工具。在 google_appengine 目录下有几个 Python 脚本，是 Google App Engine 的基本工作程序。确保它们是可执行的（如果不是，请使用 `chmod +x *.py` 命令）。同时将它们的路径添加到 `PATH` 变量中，以便你在调用它们时不必包含完整的路径：例如，如果你有一个 bash shell，在你的 .bashrc 或 .profile 文件中添加一行：\n\n```bash\nexport PATH=/home/user/google_appengine:$PATH\n```\n\n<u>注意：</u>\n\n1) 如果你已经有了一个工作的 Go 环境（就像你在阅读本书时那样），这个  AppEngine 的安装是在它之外的，与它平行而不影响它；特别是您不需要改变您操作系统中的 Go 环境变量。AppEngine 上的 Go 有其自己完全独立的环境，包含自己的 Go 版本（在 \"install root\"/google_appengine/goroot 目录下）\n2) 下载文档也是一个好主意，这样你可以在离线时浏览。从官网下载 google-appengine-docs-20111011.zip 并解压。\n3) GAE 大量使用 Python，这在 Mac OS X 和 Linux 上默认安装；如果由于某种原因不是这种情况，请从 www.python.org 下载并安装 Python 2.5。\n\n4. 源代码：库和 SDK 是开源的：http://code.google.com/p/appengine-go/。用以下方法下载：\n\n  ```bash\nhg clone https://code.google.com/p/appengine-go/\n  ```\n\n5) 一个给定应用程序的所有 Go 包都内置在一个单一的可执行文件中，并且请求调度由 Go 程序本身处理。由 Go 程序本身处理；这与 Java 和 Python SDK 的情况不同。\n   在 [第 20.8 节](20.8.md)，我们将看到如何连接到 GAE 云来部署你的应用程序。但在这你将在你刚刚安装的本地GAE环境中开发、测试和运行你的应用程序，这是对开发环境最好的模拟。\n\n### 20.3.2 检查和测试\n\n<u>检查安装：</u>\n\n为了控制一切工作正常，在控制台中进入 google_appengine，通过调用 dev_appserver.py 来调用本地 AppEngine 服务器。\n\n如果你看到以下内容： \n\n```\nInvalid arguments\nRuns a development application server for an application.\ndev_appserver.py [options]\nApplication root must be …\n```\n\n则一切正常。\n\n<u>运行一个演示应用程序：</u>\n\n在 SDK 捆绑包中有一些演示应用程序。让我们运行一个以确保一切正常。\n\n- 进入 google_appengine/demos：在那里你可以看到一些文件夹，例如 helloworld、guestbook 等。\n- 在 demos 目录下，执行命令：`dev_appserver.py helloworld`\n\n注意，这将自动编译、链接和运行 Go 程序。\n\n- 有一些警告以及信息 ，但如果最后一行如下：\n\n  ```\n  Running helloworld on port 8080: http://localhost:8080\n  ```\n\n  就可以了。此时 helloworld 应用程序已经在本地 AppEngine 服务器中被实例化，并且准备好在 `8080` 端口为您机器上的用户提供服务。\n\n- 打开浏览器并访问 http://localhost:8080\n\n  如果你看到如下页面：\n\n  ​\tHello, World! 세상아 안녕!!\n\n  你就已经成功在本地的 GAE 引擎上运行了一个 Go web 应用了。\n\n  刚才运行的 Go 源代码如下：\n\n  <u>Listing 20.1 helloworld.go:</u>\n\n  ```go\n  package helloworld\n  import (\n  \t“fmt”\n  \t“net/http”\n  )\n  func init() {\n  \thttp.HandleFunc(“/”, handle)\n  }\n  func handle(w http.ResponseWriter, r *http.Request) {\n  // some Chinese characters after World!\n  \tfmt.Fprint(w, “<html><body>Hello, World! 세상아 안녕!! </body></html>”)\n  }\n  ```\n\n  这是一个简单的 web 应用 （参考 [15 章](15.0.md)），其在 `init()` 函数当中就启动了整个的 handler。注意它被它自己的包包含。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[云上的 Go](20.2.md)\n- 下一节：[建造你自己的 Hello world 应用](20.4.md)\n"
  },
  {
    "path": "eBook/20.4.md",
    "content": "# 20.4 建造你自己的 Hello world 应用\n\n现在让我们建造一个像 [20.3 节](20.3.md)中的 demo 一样的应用，但这次我们会探索得更深一些。\n\n## 20.4.1 映像结构 (map-structure)：创造一个简单的 http-handler\n\n创建一个目录，并给它起一个你的应用程序特有的名字，如：helloapp。这个应用程序的所有文件都在这个目录中。在这个目录中再创建一个名为 hello 的目录。这将包含我们的 hello 包的 Go 源代码文件。然后在 hello 目录下，创建一个名为 helloworld2.go 的文件，并赋予其以下内容（事实上与上文中的 demo 应用几乎相同）：\n\n<u>[Listing 20.2 helloworld2_version1.go](examples\\chapter_20\\helloapp\\hello\\helloworld2_version1.go)</u>:\n\n```go\npackage hello\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n)\n\nfunc init() {\n\thttp.HandleFunc(\"/\", handler)\n}\n\nfunc handler(w http.ResponseWriter, r *http.Request) {\n\tfmt.Fprint(w, \"Hello, world!\")\n}\n```\n\n注意包的名称：在编写独立的 Go 程序时，我们会把这段代码放在 `package main` 中，但 Go GAE Runtime 提供了 `main` 包和 HTTP Listener，所以你应该把你的代码放在你选择的包中，此时指的是 hello 包。其次，由于 Go App Engine 应用程序通过 Web 服务器与外部世界进行通信，所以编写这些应用程序非常像编写独立的 Go Web 应用程序（见[第 15 章](15.0.md)）。所以我们导入 `http` 包，并为我们的应用程序中使用的不同url 模式定义处理函数。我们没有 `main()` 函数，所以处理程序的设置必须移到 `init()` 函数中去。另外，网络服务器本身的启动是由 GAE 为我们完成的。我们的 Go 包 hello 对任何请求的响应是发送一个包含 \"Hello, world!\"的消息。\n\n## 20.4.2 创建配置文件 app.yaml\n\n所有的 GAE 应用程序都需要一个 yaml 配置文件 app.yaml，它包含了 GAE 的应用程序元数据（yaml 是一种文本文件格式，经常用于开源项目，更多信息见 www.yaml.org）。另外，这个文件告诉 App Engine 服务要使用哪个运行时，哪些 URL 应该由我们的 Go 程序处理。你可以从演示程序中复制一个 app.yaml 文件，把它放在映像 helloapp 里面，并删除 favicon.ico 的 handler。\n\n应用程序的映像/文件结构应该如下：\n\n```\nhelloapp\\ \t// map of the GAE application\napp.yaml \t// configuration file\nhello\\ \t\t// map containing the source files\nhelloworld2.go\n```\n\n只有app.yaml是必须的名字，映像、Go文件和包的名字可以有不同的选择，但按照惯例，它们的名字是一样的或类似的，根映像的后缀是 app。\napp.yaml 由 AppEngine 读取和解释，AppEngine 以下时间段内托管和执行你的程序：\n\n- 当您将您的应用程序上传到 AppEngine 以使其被托管。\n- 当它被执行时。\n- 当用户访问它时。\n\n它可以包含注释，前面有一个 `#`，并包含以下语句：\n\n```yaml\napplication: helloworld\nversion: 1\nruntime: go\napi_version: 3\n\n# routing-table: routing of different urls to different types of handlers\nhandlers:\n- url: /.*\n  script: _go_app\n```\n\napp.yaml 中的 `application: value helloworld` 是您的应用程序标识符。这个值在开发过程中可以是任何东西；以后在向 App Engine 注册您的应用程序时，您将选择一个唯一的标识符（在所有 GAE 应用程序中唯一）并更新这个值。\n\n`version` 表示您的应用程序正在运行的版本：事实上，GAE 可以并行地运行您的应用程序的几个版本，但其中一个必须被指定为默认版本。它可以包含字母数字字符，以及连字符。因此，你可以运行一个测试版本，如T2-31 和一个生产版本 P2-1。\n\n`runtime` 是编写应用程序的语言（其他允许的值是 Java 和 Python）。如果你在上传应用软件的新版本之前调整它，App Engine 将保留以前的版本，并让你使用管理控制台回退到以前的版本。\n\n`api_version` 是本 SDK 中使用的 Go API 的版本；它们可能与以前的版本不兼容。你可以在以前的 api_version SDK 中构建你的应用程序的早期版本；如果 GAE 仍然允许，它们可以继续运行，但通常有一个时间限制，而你应该将你的应用程序更新到新的api版本：bin map中的gofix工具可能能够完成大部分所需的更新。\n\n`handler` 部分是循环表 (routing table)：它告诉 GAE 如何将发送到服务器上的请求映射到代码中。对于每一个传入的请求 url 模式（本地开发时在 http://localhost:8080/ 之后的部分，在云端运行时在 http://appname.appspot.com/ 之后的部分）与 url 后面的正则表达式相匹配。\n\n对于第一个匹配的 url 模式，相应的脚本会被执行。在我们的例子中，每一个路径与正则表达式 `/.*` 相匹配的 URL 请求（即：所有 URL）都应该由 Go 程序处理。`_go_app` 值是 dev_appserver.py 识别的一个神奇字符串；生产的 App Engine 服务器会忽略它。\n\n如果你看一下演示的 helloworld 应用程序的 app.yaml 文件，你会发现它在处理程序中包含一个初始部分：\n\n```yaml\nhandlers:\n- url: /favicon\\.ico\n  static_files: favicon.ico\n  upload: favicon\\.ico\n- url: /.*\n  script: _go_app\n```\n\n一些文件 (`static_files`) ，如图片，不会改变（在这个例子中是图片favicon.ico）。这些文件可以放在不同的 AppEngine 服务器上的一种共同缓存中，使它们能够更快地提供给用户。如果您的应用程序有许多这样的文件，把它们放在一个单独的目录中，按惯例命名为 static。\n\n`upload` 表示当您部署应用程序时，什么必须上传到云端；例如，如果它包含 images/(\\*.ico|\\*.gif|\\*.jpg)，它将把本地 images 目录内所有这些类型的文件上传到 AppEngine 服务器。\n\n正如我们将看到的，大多数 GAE 应用程序也使用模板文件，这些文件可以存储在根应用程序地图中，或在一个特殊的目录 tmpl 中。\n\n因此，一个 GAE 应用程序的一般结构可能是：\n\n```\nyourapp\\ \t\t// map of the GAE application\n\t\tapp.yaml // configuration file\n\t\tyourpackage\\ // map containing the source files\n\t\t\tpackage1.go\n\t\t\t…\n\t\ttmpl\\ // map containing template files\n\t\t\troot.html\n\t\t\tupdate.html\n\t\t\t…\n\t\tstatic\\ // map containing static files\n\t\t\tyourapp.ico\n\t\t\t…\n```\n\n与 demo 一样，在控制台窗口中进入包含 helloapp 的映像，并发出如下命令：`dev_appserver.py helloapp`\n\n或者你可以通过任何一个映像的 console 窗口并且唤醒：\n\n```bash\ndev_appserver.py /path_to_map_helloapp/helloapp\n```\n\n在这两种情况下，网络服务器现在都在运行，并监听 8080 端口的请求。通过在你的网络浏览器中访问以下 URL 来测试该应用程序：http://localhost:8080/\n\n你应该看到： Hello, world! \n\n在服务器控制台，出现以下文字：\n\n```\n$ dev_appserver.py helloapp\nINFO 2011-10-31 08:54:29,021 appengine_rpc.py:159] Server: appengine.google.com\nINFO 2011-10-31 08:54:29,025 appcfg.py:463] Checking for updates to the SDK.\nINFO 2011-10-31 08:54:29,316 appcfg.py:481] The SDK is up to date.\nWARNING 2011-10-31 08:54:29,316 datastore_file_stub.py:512] Could not read datastore\ndata from /tmp/dev_appserver.datastore\nINFO 2011-10-31 08:54:29,317 rdbms_sqlite.py:58] Connecting to SQLite database ‘’\nwith file ‘/tmp/dev_appserver.rdbms’\nINFO 2011-10-31 08:54:29,638 dev_appserver_multiprocess.py:637] Running application\nhelloworld on port 8080: http://localhost:8080\n\t\t<-(A)\nINFO 2011-10-31 08:56:13,148 __init__.py:365] building _go_app\n\t\t<-(B)\nINFO 2011-10-31 08:56:15,073 __init__.py:351] running _go_app\nINFO 2011-10-31 08:56:15,188 dev_appserver.py:4143] “GET / HTTP/1.1” 200 -\n\t\t<-(C)\n```\n\n在 (A) 处服务器准备好了，在 (B) 处服务器编译并运行 Go 程序，在 (C) 处我们的应用程序的请求进来了，此时 HTML 输出页面被提供到服务器上。\n\n当服务器被终止或尚未启动，而客户端请求网址 http://localhost:8080/，浏览器在FireFox 中会打印出这样的信息：\n\n```\nUnable to connect Firefox can’t establish a connection to the server at localhost:8080.\n```\n\n## 20.4.3 迭代开发\n\n开发应用的服务器会观察你的文件中的变化，当你更新你的源代码时（编辑+保存），它重新编译它们并重新启动你的本地应用；不需要重新启动 dev_appserver.py \n\n现在试试：让 Web 服务器运行，然后编辑 helloworld2.go，将 \"Hello, world!\" 改为其他内容。重新加载 http://localhost:8080/，就可以看到变化了：这和编写 Rails 或 Django 应用程序一样，都是动态运行的。\n\n要关闭 Web 服务器，确保终端窗口处于活动状态，然后按 Ctrl+C（或适当的用于控制台的 \"break \"键）：\n\n```\nINFO 2011-10-31 08:56:21,420 dev_appserver.py:4143] “GET / HTTP/1.1” 200 -\nINFO 2011-10-31 08:57:59,836 __init__.py:365] building _go_app <-(D)\nINFO 2011-10-31 08:58:00,365 __init__.py:351] running _go_app\nINFO 2011-10-31 08:58:00,480 dev_appserver.py:4143] “GET / HTTP/1.1” 200 -\n^CINFO 2011-10-31 08:58:32,769 dev_appserver_main.py:665] Server interrupted by user,\nterminating <-(E)\n```\n\n这可以从上面第一个列表之后的服务器控制台输出中看到：在 (D) 处，apperver 看到 Go 的源代码被改变了，并重新编译；在 (E) 处，服务器被终止了。\n\n## 20.4.4. 与 GoClipse IDE 的集成\n\na) 窗口/首选项/Go：\n\n将所有内容指向 GAE 的 Go 根目录\n\nb) 运行/外部工具/外部工具配置/选择程序：\n\n​\t 制作新的配置：点击 New 按钮。\n​\t\t名称：GAE\n​\t\t位置：/home/user/google_appengine/dev_appserver.py \n​\t\t工作目录：/home/user/workspace/bedilly/src/pkg/helloapp\n​\t\t参数： home/user/workspace/bedilly/src/pkg/helloapp\n​\t应用/运行 \n\n通过配置一个外部工具，部署你的应用程序也很容易：http://code.google.com/p/goclipse/wiki/DeployingToGoogleAppEngineFromEclipse\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[安装 Go App Engine SDK](20.3.md)\n- 下一节：[使用用户服务和探索其 API](20.5.md)\n\n"
  },
  {
    "path": "eBook/20.5.md",
    "content": "# 20.5 使用用户服务和探索其 API\n\nGAE 提供了几个基于 Google 基础设施的有用服务。正如[第 20.1 节](20.1.md)中提到的：GAE 提供了一个 Users 服务，它可以让你的应用程序与 Google 用户账户集成。有了用户服务，您的用户可以使用他们已经拥有的谷歌账户来登录您的应用程序。用户服务使您可以轻松地对该应用程序的问候语进行个性化处理。\n\n编辑 helloworld2.go 文件，用以下 Go 代码替换它：\n\n<u>[Listing 20.3 helloworld2_version2.go](examples/chapter_20/helloapp/hello/helloworld2_version2.go)</u>:\n\n```go\npackage hello\n\nimport (\n\t\"appengine\"\n\t\"appengine/user\"\n\t\"fmt\"\n\t\"net/http\"\n)\n\nfunc init() {\n\thttp.HandleFunc(\"/\", handler)\n}\n\nfunc handler(w http.ResponseWriter, r *http.Request) {\n\tc := appengine.NewContext(r)\n\tu := user.Current(c)\n\tif u == nil {\n\t\turl, err := user.LoginURL(c, r.URL.String())\n\t\tif err != nil {\n\t\t\thttp.Error(w, err.String(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t\tw.Header().Set(\"Location\", url)\n\t\tw.WriteHeader(http.StatusFound)\n\t\treturn\n\t}\n\tfmt.Fprintf(w, \"Hello, %v!\", u)\n}\n```\n\n通过在浏览器中重新加载页面来测试它。你的应用程序会给你一个链接，当你遵循这个链接时，会把你重定向到适合测试你的应用程序的本地版本的谷歌登录页面。你可以在这个页面中输入任何你喜欢的用户名，你的应用程序将看到一个基于该用户名的假的 `user.User` 值。当你的应用程序在 App Engine 上运行时，用户将被引导到 Google 账户的登录页面，然后在成功登录或创建账户后，会被重定向到你的应用程序。\n\n<u>用户API：</u>\n\n为了访问这个，我们需要导入一些专门针对 GAE 的 Go 包，即一般的 `appengine` 和 `appengine/user`。\n\n在处理程序中，我们首先需要制作一个与当前请求r相关联的Context对象，这在一行中完成： \n\n```go\nc := appengine.NewContext(r)\n```\n\n`appengine.NewContext()` 函数在这里返回一个名为 `c` 的 `appengine.Context` 值：这是 Go App Engine SDK 中许多函数用来与 App Engine 服务通信的值。然后我们从这个上下文中测试是否已经有一个用户在此时登录，方法是：\n\n```go\n u := user.Current(c)\n```\n\n如果是这样的话，`user.Current` 会返回一个指向用户的 `user.User` 值的指针；否则会返回 `nil`。如果用户还没有登录，即 `u == nil` 时，通过调用用户的浏览器重定向到谷歌账户的登录界面。\n\n```go\nurl, err := user.LoginURL(c, r.URL.String())\n```\n\n第 2 个参数 `r.URL.String()` 是当前请求的 url，这样谷歌账户登录机制可以在成功登录后进行*重定向*：它将在用户登录或注册新账户后将其送回这里。登录界面的发送是通过设置一个 Location 数据头并返回一个 HTTP 状态代码 302“Found”来完成的。\n\n`LoginURL()` 函数返回一个 error 值作为其第二个参数。尽管这里不太可能发生错误，但检查它并在适当的时候向用户显示错误是很好的做法（在这种情况下，用 http.Error helper）：\n\n```go\nif err != nil {\n\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\treturn\n}\n```\n\n当用户登录后，我们使用与用户账户相关的名字显示一条个性化的信息： \n\n```go\nfmt.Fprintf(w, \"Hello, %v!\", u)\n```\n\n在这种情况下，`fmt.Fprintf()` 函数调用 `*user.User` 的 `String()` 方法来获得字符串形式的用户名称。更多信息可以在这个参考资料中找到：http://code.google.com/appengine/docs/go/users/\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[建造你自己的 Hello world 应用](20.4.md)\n- 下一节：[处理窗口](20.6.md)\n\n"
  },
  {
    "path": "eBook/20.6.md",
    "content": "# 20.6 处理窗口\n\n正如我们在 [15.6](15.6.md)/[7](15.7.md) 节中所看到的，`template` 包经常被用于 web 应用，所以也可以被用于 GAE 应用。下面的应用程序让用户输入一个文本。首先，一个留言簿表格显示出来（通过 `/` 根处理程序），当它被发布时，`sign()` 处理程序将这个文本替换到产生的 html 响应中。`sign()` 函数通过调用 `r.FormValue` 获得窗口数据，并将其传递给 `signTemplate.Execute()`，后者将渲染的模板写入 `http.ResponseWriter`。\n\n编辑文件 helloworld2.go，用下面的 Go 代码替换它，并试运行：\n\n<u>[Listing 20.4 helloworld2_version3.go:](examples\\chapter_20\\helloapp\\hello\\helloworld2_version3.go)</u>\n\n```go\npackage hello\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"template\"\n)\n\nconst guestbookForm = `\n<html>\n  <body>\n    <form action=\"/sign\" method=\"post\">\n      <div><textarea name=\"content\" rows=\"3\" cols=\"60\"></textarea></div>\n      <div><input type=\"submit\" value=\"Sign Guestbook\"></div>\n    </form>\n  </body>\n</html>\n`\nconst signTemplateHTML = `\n<html>\n  <body>\n    <p>You wrote:</p>\n    <pre>{{html .}}</pre>\n  </body>\n</html>\n`\n\nvar signTemplate = template.Must(template.New(\"sign\").Parse(signTemplateHTML))\n\nfunc init() {\n\thttp.HandleFunc(\"/\", root)\n\thttp.HandleFunc(\"/sign\", sign)\n}\n\nfunc root(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"Content-Type\", \"text/html\")\n\tfmt.Fprint(w, guestbookForm)\n}\n\nfunc sign(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"Content-Type\", \"text/html\")\n\terr := signTemplate.Execute(w, r.FormValue(\"content\"))\n\tif err != nil {\n\t\thttp.Error(w, err.String(), http.StatusInternalServerError)\n\t}\n}\n```\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[使用用户服务和探索其 API](20.5.md)\n- 下一节：[使用数据存储](20.7.md)\n\n"
  },
  {
    "path": "eBook/20.7.md",
    "content": "# 20.7 使用数据存储\n\n我们现在有了一种用 html 窗口来收集用户信息的方法。通常我们希望使这些信息持久化：我们需要一个地方来放置这些信息，并且需要一个方法来取回这些信息。GAE 在这里为我们提供了它的 DataStore 设施：一个非关系型 (non-relational) 数据库，它可以跨网络服务器甚至跨机器持久保存您的数据。事实上，用户的下一个请求很可能是在不同的计算机上运行的不同的网络服务器，但 GAE 的基础设施在一个简单的 API 后面处理了所有的数据分布、复制和负载平衡，你还可以得到一个强大的查询引擎。\n\n我们现在将对我们的例子进行一些扩展，制作一个问候结构，该结构可以包含问候的作者、内容和时间，我们要存储这些内容。这是你要做的第一件事：为你的程序*实体*（即你的程序所处理的对象）制作一个合适的数据结构，大多数情况下这将是一个 `struct`。在运行的程序中，这个结构的内存值将包含来自该实体的 DataStore 的数据。\n\n接下来我们程序的版本如下：\n\n​\t\t(A) `url:/`： 检索所有存储的问候语并通过 `template` 包显示它们\n\n​\t\t(B) `url:/sign`：存储一个新的问候语到数据存储里面\n\n我们现在需要导入 `appengin/datastore` 包：\n\n<u>[Listing 20.5 helloworld2_version4.go:](examples\\chapter_20\\helloapp\\hello\\helloworld2_version4.go)</u>\n\n```go\npackage hello\n\nimport (\n\t\"appengine\"\n\t\"appengine/datastore\"\n\t\"appengine/user\"\n\t\"net/http\"\n\t\"template\"\n\t\"time\"\n)\n\nconst guestbookTemplateHTML = `\n<html>\n  <body>\n    {{range .}}\n      {{with .Author}}\n        <p><b>{{html .}}</b> wrote:</p>\n      {{else}}\n        <p>An anonymous person wrote:</p>\n      {{end}}\n      <pre>{{html .Content}}</pre>\n      <pre>{{html .Date}}</pre>\n    {{end}}\n    <form action=\"/sign\" method=\"post\">\n      <div><textarea name=\"content\" rows=\"3\" cols=\"60\"></textarea></div>\n      <div><input type=\"submit\" value=\"Sign Guestbook\"></div>\n    </form>\n  </body>\n</html>\n`\n\nvar guestbookTemplate = template.Must(template.New(\"book\").Parse(guestbookTemplateHTML))\n\ntype Greeting struct {\n\tAuthor  string\n\tContent string\n\tDate    datastore.Time\n}\n\nfunc init() {\n\thttp.HandleFunc(\"/\", root)\n\thttp.HandleFunc(\"/sign\", sign)\n}\n\nfunc root(w http.ResponseWriter, r *http.Request) {\n\tc := appengine.NewContext(r)\n\tq := datastore.NewQuery(\"Greeting\").Order(\"-Date\").Limit(10)\n\tgreetings := make([]Greeting, 0, 10)\n\tif _, err := q.GetAll(c, &greetings); err != nil {\n\t\thttp.Error(w, err.String(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\tif err := guestbookTemplate.Execute(w, greetings); err != nil {\n\t\thttp.Error(w, err.String(), http.StatusInternalServerError)\n\t}\n}\n\nfunc sign(w http.ResponseWriter, r *http.Request) {\n\tc := appengine.NewContext(r)\n\tg := Greeting{\n\t\tContent: r.FormValue(\"content\"),\n\t\tDate:    datastore.SecondsToTime(time.Seconds()),\n\t}\n\tif u := user.Current(c); u != nil {\n\t\tg.Author = u.String()\n\t}\n\t_, err := datastore.Put(c, datastore.NewIncompleteKey(c, \"Greeting\", nil), &g)\n\tif err != nil {\n\t\thttp.Error(w, err.String(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\thttp.Redirect(w, r, \"/\", http.StatusFound)\n}\n```\n\n信号处理程序 (B) 从表单和上下文数据中构建了一个问候值 `g`，然后用 `datastore.Put()` 存储它。DataStore 在内部为数据记录生成自己的唯一 key；为了做到这一点，我们调用 `Put()` 函数，将\n\n```go\ndatastore.NewIncompleteKey(c, \"Greeting\", nil)\n```\n\n作为第 2 个参数（这个函数需要实体 `Greeting` 的名字）。`Put()`  的第 3 个参数 `&g` 是一个包含 value 的 `struct` （严格来说，此处应为指向这个 `struct` 的指针）。\n\n`datastore` 包提供了一个查询类型，用于查询数据存储并迭代结果。根处理程序正是通过构造一个查询 `q()` 来实现的，该查询按照日期降序从DataStore 中请求问候对象，限制为 10 个。\n\n```go\nq := datastore.NewQuery(\"Greeting\").Order(\"-Date\").Limit(10)\n```\n\n我们需要一个数据结构来存储我们的查询结果，也就是 `greetings`，一个 Greeting 值的切片。我们对 `q.GetAll(c, &greetings)` 的调用检索了数据，并将它们存储在我们的切片中；当然，我们会检查可能的错误。\n\n当一切正常时，我们通过与我们的模板合并来显示数据：\n\n```go\nguestbookTemplate.Execute(w, greetings)\n```\n\n这是由一个 `range` 结构执行的（参考 [15.7.6 节](15.7.md)）。\n\n再次通过编辑 helloworld2.go 文件进行测试，用 listing 20.5 中的代码替换它；在问候中间的间隙关闭浏览器会话，这样你可以检查它们是否被持久保存。\n\n<u>清除开发用服务器的数据存储：</u>\n\n开发用 web 服务器使用一个本地版本的数据存储来测试你的应用程序，使用临时文件。只要临时文件存在，数据就会持续存在，除非你要求，否则 Web 服务器不会重置这些文件。如果你想让开发用服务器在启动前擦除其数据存储，请在启动服务器时使用 `--clear_datastore` 选项：\n\n```bash\ndev_appserver.py --clear_datastore helloapp/\n```\n\n<u>调试：</u>\n\ngdb 调试器可以和 Go 一起使用（见 http://golang.org/doc/debugging_with_gdb.html），你可以将 gdb 附加到一个现有的进程中。因此：像往常一样启动 dev_appserver.py，并访问 `localhost:8080` 来启动你的 Go 应用程序。然后执行： `$ ps ax | grep _go_app`，找到 \\_go\\_app 的 PID 和路径。如果你把 gdb 连接到这个上面，那么你对 dev_appserver 的下一个 HTTP 请求应该会碰到你在代码中设置的任何断点。记住，如果你修改了 Go 的源代码，那么开发应用服务器将重新编译并执行不同的 \\_go\\_app。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[处理窗口](20.6.md)\n- 下一节：[上传到云端](20.8.md)\n\n"
  },
  {
    "path": "eBook/20.8.md",
    "content": "# 20.8 上传到云端\n\n我们的留言簿应用程序使用谷歌账户认证用户，让他们提交信息，并显示其他用户留下的信息，让我们认为其基本功能完成了：我们现在将把它部署在云中。如果我们的应用程序会变得非常流行，我们不需要改变任何东西，因为 GAE 会自动处理扩展。\n\n但是，首先你需要有一个谷歌账户，如 gmail 地址；你可以在 www.google.com/accounts 快速建立一个账户。\n\n创建和管理 App Engine 网络应用程序是通过 App Engine 管理控制台网站进行的：https://appengine.google.com/\n\n在快速的 SMS 验证程序之后，你会看到 \"创建一个应用程序 \"的页面。选择一个应用程序标识符 (*application identifier*)（对所有 GAE 应用程序来说是唯一的），如 ib-tutgae.appspot.com；加上前缀 http://，这将成为你的应用程序的网址。这个标识符以后不能更改，如果是私人应用程序，用你的名字缩写，如果是商业应用程序，用你的公司名称作为前缀是比较好的。然后选择一个应用程序的标题，这在您的应用程序中是可见的，并可以在之后更改，例如 \"GAE 应用程序手册\"。保留默认的谷歌认证和高复制数据存储，低于一定的配额之下，GAE 将免费运行您的应用程序。点击 \"创建应用程序 \"按钮后，将出现一个屏幕，显示 \"应用程序成功注册 \"的信息。\n\n要在云中上传您的应用程序，请执行以下操作。\n1) 编辑 app.yaml 文件，将 `application: setting` 的值从 `helloworld` 改为你注册的应用程序 `ib-tutgae`\n2) 在 GAE 中上传和配置您的应用程序，使用脚本 appcfg.py 执行命令：`appcfg.py update helloapp/`\n\n通过询问您的谷歌账户数据进行验证，如果一切成功，您的应用程序现在就可以部署在 App Engine 上了!\n\n步骤 2) 必须在你每次上传新版本的应用程序时执行。\n\n如果你看到编译错误，请修复源代码并重新运行 appcfg.py；在编译成功之前，它不会启动（或更新）你的应用程序。\n\n在云端测试它：http://`application-id`.appspot.com\n\n使用你自己独特的应用程序 ID (`application-id`)，在我们的例子中是 http://ib-tutgae.appspot.com\n\n这也可以在 Windows 平台的浏览器中使用，而不仅仅是在 Linux 或 OS X 上。\n\n<u>监控你的应用程序：</u>\n\n再次访问 https://appengine.google.com/，现在将显示一个你的应用程序的列表。点击你的应用程序的链接将显示其控制面板 (*Control Panel*)，用于监控你的应用程序。\n\n![Fig 20.1:The Application Control Panel](images/20.8_fig20.1.png)\n\n<center><u>Fig 20.1</u>:The Application Control Panel</center>\n\n这非常重要，因为你的应用程序在云中运行，而这是你访问它的唯一途径（除了用 app\\_cfg 上传新的版本）！当你的应用程序在云中运行时，你不能对它进行或调试。当你的代码在云中运行时，你不能自己对它进行剖析 (profile) 或调试。有一个图像显示你的应用程序的负载（每秒钟的请求量），它消耗了多少资源（CPU 使用量、带宽、存储、复制的数据、后端使用量）以及如何计费。还有一个负载视图：每个 URL 模式的请求数和 CPU 负载，以及非常重要的一个错误视图：关于你的应用程序中发生的错误的摘要信息。数据面板，特别是数据存储查看器，可以让你可视化和查询你的存储数据。此外，还有用于管理的特定视图和 GAE 文档的链接。Main/Logs 让您可以访问应用程序的日志，每个请求和错误/异常都会被记录下来（异常不会显示给用户）。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[使用数据存储](20.7.md)\n- 下一节：[21.0](真实世界中 Go 的使用)\n\n"
  },
  {
    "path": "eBook/21.0.md",
    "content": "# 21.0 真实世界中 Go 的使用\n\n在接下来的章节中，我们将讨论一些 Go 的实际使用案例：我们将探索一些如今在商业领域中使用的 Go 应用程序，并指出为什么选择在这些领域中使用 Go 。考虑到这一语言仅发行了 2 年时间，而使用新语言构建初始项目的老牌企业通常不希望让公众知道这一点，这是非常令人印象深刻的。\n\n- [目录](directory.md)\n- 上一节：[上传到云端](20.8.md)\n- 下一节：[Heroku：一个使用 Go 的高度可用一致数据存储](21.1.md)\n"
  },
  {
    "path": "eBook/21.1.md",
    "content": "# 21.1 Heroku：一个使用 Go 的高度可用一致数据存储\n\nhttp://www.heroku.com/（引用 39）\n\nHeroku 是一家位于美国旧金山的硅谷公司，最近被 Salesforce.com 公司（它为 Ruby 和 Rails、Java、Clojure 和 node.js 应用程序提供强大的、可扩展的、特别是非常易于管理的云主机）收购。Heroku 的两位工程师，Keith Rarick 和 Blake Mizerany 设计了一个开源的 \"分布式启动系统\"，名为 **Doozer**，用于管理跨集群机器的进程，并从实例故障和网络故障中优雅地恢复分区。其中一个需求是，他们需要可靠地同步和在许多服务器之间共享信息。\n\n系统中的每台服务器都需要有很多关于系统整体的信息（配置数据、锁 (lock) 等），以便能够进行协调，而且这些信息需要保持一致，即使在数据存储失败时也可以使用。因此他们需要一个具有坚实一致性保证的数据存储。为此，他们开发了 Doozer，一个用 Go 语言编写的、新的、一致的、高度可用的数据存储，并且仿照谷歌的（封闭源码）Chubby 程序来管理他们的后端基础设施。\n\nDoozer 以 Paxos 为基础（Paxos 是一个由不可靠节点组成的的不可靠网络中解决共识问题的协议族），虽然 Paxos 对运行容错系统至关重要，但它因难以实现而臭名昭著。即使是在网上可以找到的实例实现也很复杂，很难遵循，尽管已经为了教育目的而被简化过。而现有的生产系统以更糟糕而闻名。\n\nDoozer 是作为建立分布式系统的一个坚硬的基础而被开发的。\n\n- 一个高度可用的（在网络分区期间工作）。\n- 一致性（没有不一致的写入）。\n- 数据存储（用于少量的数据）。\n\n正如开发人员所说：\n\n> \"Doozer 是你放置家族珠宝的地方\"。\n\n它提供了一个单一的基本同步元素：比较-设置对 (compare-config)。\n\n用例：\n- 数据库主选 (Databases master election)\n- 命名服务\n- 配置\n\n<u>为什么选择 Go，Go 的特点如何使其成为一个成功的产品：</u>\n\nPaxos 是以独立的、并发的进程来定义的，这些进程通过传递消息进行通信。这正是 *Go 的并发原语*（goroutines 和 channel，见 [第 14 章](14.0.md)）所擅长的问题。在 Doozer 中，这些进程被实现为 *goroutines*，他们的通信被实现为*通道操作*。就像 Go 的*垃圾收集器*将内存使用量降到最低一样，Doozer 的开发者发现 goroutines 和通道改进了基于锁的并发方法。这些工具让他们避免了复杂的“记账” (bookkeeping) 方式，并将注意力集中在手头的问题上。他们仍然惊讶于只用了几行代码就实现了以困难著称的东西。\n\nGo 中的*标准包*是对于 Doozer 的另一个大成功，其中最值得一提的是 `websocket` 包。\n\n下面是开发人员自己的一些使用后的感受：\n\n> “……例如，我们很快就发现 `websocket` 是一个有用的包。一旦我们有了一个工作的数据存储，我们就需要一个简单的方法来反省 (introspect) 它并将活动可视化。使用 `websocket` 包，Keith 能够在回家的火车上添加 web 浏览器，而且不需要外部依赖。这就是 Go 将系统和应用编程完美结合的一个真实证明。\n>\n> “部署 Doozer 的过程简单得令人满意。Go 构建静态链接的二进制文件，这意味着 Doozer 没有外部依赖；它是一个单一的文件，可以复制到任何机器上，并立即启动，加入运行中的 Doozer 集群。\n>\n> “最后，Go 对简单性和正交性 (orthogonality) 的狂热关注与我们对软件工程的看法是一致的。和 Go 团队一样，我们对 Doozer 的特性也是固执的 (pragmatic)。我们关注细节，更倾向于改变现有的功能而不是引入新的功能。在这个意义上，Go 是一个与 Doozer 的完美匹配。我们已经有了关于 Go的未来项目。Doozer 只是一个更大的系统的开始。”\n\n他们还喜欢自动格式化工具 *gofmt*，以实现一致的代码风格和布局，从而避免了对这些话题的讨论。\n\n其他语言也提供了一些类似的并发机制——比如 Erlang 和 Scala，但 Go 的设计也是为了提供最大的效率和控制。在另一篇文章中（[引用 12]()）Keith Rarick 指出：\n\n> “Go 来自于 C 和 C++ 这样的系统编程语言，所以它让你有能力真正控制性能特性。当需要测量事物并确保其运行速度的时候，你有足够的灵活性来真正进入那里并做你需要的事情。当你发现你的程序运行缓慢的原因时，你就可以真正控制你所需要的东西来解决它。Go 给了你一个独特的组合：C 语言给了你控制权，但它并不适合于并发。它甚至没有给你提供垃圾收集。Go 为你提供了并发性和垃圾收集，但它仍然让你控制内存布局和资源使用。”\n\n在 Doozer 中，Go 主要作为一种系统编程语言使用。更多的技术描述可以在（[引用 38]()）找到；代码可在 https://github.com/ha/doozer 找到。\n\n## 链接\n\n- [目录](directory.md)\n- 上一节：[真实世界中 Go 的使用](21.0.md)\n- 下一节：[MROffice：一个使用 Go 的呼叫中心网络电话 (VOIP) 系统](21.2.md)"
  },
  {
    "path": "eBook/21.2.md",
    "content": "# 21.2 MROffice：一个使用 Go 的呼叫中心网络电话 (VOIP) 系统\n\nhttp://mroffice.org/\n\n这个例子表明，Go 也适用于简单、可靠的应用程序编程。\n\nMROffice 是一家位于新西兰的公司，专门从事市场调查软件。他们在 Freeswitch 的基础上使用 Go 为市场调查的呼叫中心建立了一个电话解决方案。Kees Varekamp 是有市场研究软件的背景的一位开发人员，他发现该领域的大多数现有软件都很糟糕，于是在 2010 年推出了 MROffice，为市场研究行业提供更好的软件。\n\n他的旗舰产品名为 [Dialer](http://mroffice.org/telephony.html)。\n\nDialer 主要做什么？\n\n- 它把呼叫中心的面试官和受访者联系起来。\n- 它在采访平台（提供脚本和收集统计数据）和 VoIP 拨号器（进行实际的电话通话）之间提供一座桥梁。\n\n<u>为什么是 Go？</u>\n\nDialer 的第一个版本是用 Python 写的，但他的经验是，Python 作为一种动态脚本语言，对于长期运行的服务器进程来说，也许不是一个好的选择：发生了很多运行时的错误，而这些错误本可以在编译时被发现。\n\n正如 Varekamp 先生在悉尼 Go 用户组（2011 年 3 月）所说：\n\n> “当 Go 出现的时候，我立刻就理解到了 (made sense to me)：类型安全，已编译，感觉像一种脚本语言。”\n\n所以他把 Python 代码移植到 Go 上。*Go 的并发模型*适合这个问题：一个 goroutine 被启动来处理每个呼叫、面试者和被面试者，他们都通过通道来进行通信。`http` 和 `websocket` 库使得编写一个用户管理界面变得容易。\n\n该产品现在已经在多个呼叫中心运行，并且正在进行使用神经网络的预测拨号器设计。\n\n- [目录](directory.md)\n- 上一节：[Heroku：一个使用 Go 的高度可用一致数据存储](21.1.md)\n- 下一节：[Atlassian：一个虚拟机群管理系统](21.3.md)"
  },
  {
    "path": "eBook/21.3.md",
    "content": "# 21.3 Atlassian：一个虚拟机群管理系统\n\nhttp://www.atlassian.com/\n\n在 Atlassian，Go 被用于支持并发的实用程序设计，事实上是用于配置和监控测试服务器。他们为软件开发人员制作开发和协作工具（主要是一个 Java 商店）。他们有一个由虚拟机 (VM) 组成的测试集群，在大量无硬盘主机上运行。它的供应和监控系统是用 Go 编写的；该系统由 3 个部分组成：\n- 在每个服务器上运行的代理进程，广播其虚拟机的状态。\n- 一个管理程序，听取代理的广播，并在一个虚拟机没有报告时采取行动。\n- 一个命令行工具，用于向管理器发布命令。\n\n代理使用协议缓冲区来编码它所读取的状态信息，并通过 UDP 广播这些信息。管理器读取配置文件并为集群中的每个虚拟机启动一个 goroutine。每个 goroutine 监听来自其相应的虚拟机的公告，并发出指令（shell 命令），使其处于正确的状态。\n\n<u>为什么 Go 在这里起作用：</u>每个虚拟机的一个 goroutine 很好地映射到它们的配置。\n\n这个系统也*很容易部署*，因为他们可以运送没有依赖性的二进制文件。\n\n正如 Atlassian 工程师 Dave Cheney 所说：\n\n> “代理程序运行在联网启动的机器上，并且完全从 RAM 中运行。与 JVM 或 Python 的运行时相比，单一的静态二进制文件是一个很大的节省。”\n\n- [目录](directory.md)\n- 上一节：[MROffice：一个使用 Go 的呼叫中心网络电话 (VOIP) 系统](21.2.md)\n- 下一节：[Camilistore：一个可寻址内容存储系统](21.4.md)"
  },
  {
    "path": "eBook/21.4.md",
    "content": "# 21.4 Camilistore：一个可寻址内容存储系统\n\nhttp://camlistore.org/\n\n在 Camlistore 中，从数据存储到用户界面，“全栈”编程都在 Go 中进行。该系统由 Brad Fitzpatrick 开发，是一个在云上存储个人数据并与朋友和公众分享这些数据的系统。它由一个内容可寻址的数据存储、一个同步器和访问控制机制、一个 API、一个用户界面以及一个个人“web 主目录”组成。\n\n它是一个语言无关的 (language-agnostic) 项目，但其最主要的部分是用 Go 编写的。它们包括一个 blob 数据服务器、一个 http 服务器、一个 http 用户界面以及一些命令行工具。\n\n它可以用于：\n\n- 自动同步远程服务器的个人备份。\n- 在机器间进行 Dropbox 式的文件同步。\n- 照片管理和共享。\n- 网站内容管理。\n\n以下是 Brad 对这个 Go 项目的一些评论：\n\n> “我在非常少的时间内，不需要太多的代码就能迸发出 (bust out) 很多快速、正确、可维护的可测试代码，我已经很久没有对一种语言如此兴奋了。我很早就有了 Camlistore 的想法，但在我学习Go之前，它总是显得太痛苦了。”\n\n- [目录](directory.md)\n- 上一节：[Atlassian：一个虚拟机群管理系统](21.3.md)\n- 下一节：[Go 语言的其他应用](21.5.md)\n"
  },
  {
    "path": "eBook/21.5.md",
    "content": "# 21.5 Go 语言的其他应用\n\n在前面的章节中，我们只讨论了 Go 在商业环境中已经使用的许多地方中的几个。其他一些使用 Go 的机构有：\n\n1. [Canonical-Ubuntu 公司](http://www.canonical.com/)：使用 Go 开发后台基础设施，主要开发者为 Gustavo Niemeyer。例如项目 Ensemble（见 [参考文献 30]()）。\n\n2. [FeedBooks](http://www.feedbooks.com/)：用 Go 发布电子书。\n   \n   FeedBooks 是一个电子书的发行商，它使用 Go 和 mgo 每天为超过一百万的图书出版提供服务。这是 Feedbooks 的研发工程师 Benoît Larroque 的一条评论：\n   \n   > “mgo（一个与 MongoDB 交流的 Go 库）使我们能够每天为超过 100 万本图书出版提供服务，同时也降低我们的服务器负载。”\n   \n3. [Anchor-Orchestra](http://www.anchor.com.au/)：一个使用 Go 的分布式执行框架。这家公司的特点是高水平的服务器支持、配置应用程序设置、缓存和解决可扩展性问题。他们还可以与其他网站托管公司合作，专业地设置负载平衡、数据库集群和虚拟环境。\n\n   为此，他们使用 Go 开发并使用 Orchestra 分布式执行框架。\n\n   （更多信息：http://www.anchor.com.au/blog/2011/08/the-automation-waltz/）\n\n4. [开放知识基金会](http://eris.okfn.org/ww/2011/03/gockan)。\n\n   这个组织使用 Go 进行（元）数据目录的聚合和数据链接。所有现有的软件都是用 Python 写的，所以开发者可以对两者进行比较。他们的结论是：\n   \n   - Go *很简单*。一旦通过了最初浅显的学习弯道，它就会像 Python 一样方便、舒适地运行。唯一的缺点是没有像 Python 那样多的库。\n   - Go 是一种*静态类型的语言*。这似乎是一个深奥的细节，但它有一些重要的影响。在 Python 中的许多编程涉及到大量的单元和功能测试，这可能是一个相当大的负担，尽管 CKAN 测试套件有了一些重大的改进，但需要相当长的时间来运行。然而你很快就会发现，许多测试基本上是在测试动态类型 (duck typing) 和可变实体 (variable existence)（例如，当你在重构中重命名一个变量时，不确定你是否正确地重命名了一切）。在像 Go 这样的语言中，这些东西都是由编译器捕获的，不需要单独的测试。这意味着*你可以少写一些测试，因为编译器本身就是一个相当强大的测试套件。*\n   - 尽管它是一种编译语言，但编译过程非常快，*写-编译-测试 的循环并不比 Python 中的 写-测试 循环慢*，因为需要运行的测试较少，如同上文所说，这个循环被进一步压缩了。\n   - Go *远比 Python 更节省内存*……差别是惊人的。\n   - 与 Python 相比，Go 作为一种经过编译和类型检查的语言，它的*速度很快*。\n   - Go 不是面向对象的，至少与 Python 的意义不同。相反，它有一个接口的概念。这使得*设计更加简洁*，因为它不鼓励复杂的多重继承类的层次结构……接口就感觉更干净一些。\n   - Go 有*内置的并发性*。在这项工作中，有很多并行的机会，这是好的。\n\n5. [Tinkercad 公司](http://tinkercad.com/)：这家由 Kai Backman 创办的芬兰公司正在设计用于在浏览器/云端进行 3D 实体建模和打印的软件，其在客户端使用 WebGL 进行渲染。观看 [视频](http://www.youtube.com/watch?v=5aY4a9QnLhw) 了解关于这个主题的技术讲座。这是 Kai 的一句评价：\n\n   > “目前（2011 年）Go 可能是编写并发服务器的最佳语言。”\n\n6. [Clarity Services Inc.](http://www.clarityservices.com)：该公司是一家实时的信用机构，其使用 Go 语言对信用申请进行基于事件的后期处理。\n\n7. [Cablenet 通信系统有限公司](http://www.cablenet.com.cy/en/)：这家塞浦路斯的 cablenet 供应商用 Go 开发了一个内部供应系统。\n\n8. [Tonika](http://pdos.csail.mit.edu/~petar/5ttt.org/)：是一个用 Go 开发的、开源安全网络社交平台。\n\n9. [Medline](http://eris.okfn.org/ww/2011/05/medline/)：使用 Go 的 XML 解析器来将Medline（医学期刊的数据）的压缩 XML 文件转化到 RDF。\n\n10. [Iron.io](www.iron.io)：构建云基础设施软件。\n\n    它用 Go 开发的第一个产品是 SimpleWorker，一个大规模的后台处理和调度系统；他们也在使用 Go 进行其他服务。\n\n11. [SmartTweets](http://www.facebook.com/apps/application.php?id=135488932982)：一个用Go开发的 Facebook 应用程序。这个应用程序将你的 Twitter 状态更新转贴到你的 Facebook 主页上，并允许过滤转发、提及、标签、回复等内容。\n\n    该应用程序现在有超过 12 万名用户。\n\n    > “这是一种稳定的语言，”Michael Hoisie 说，“它可以处理负载。”\n\n12. 在 [Sandia 国家实验室](http://www.sandia.gov/about/index.html)，一个美国开发支持国家安全的基于科学的技术的政府机构，有很多曾经从事过编程的人都在使用这种语言。很多过去使用 C、C++、Perl、Python 或其他什么 HPC 管理软件的人，已经转而使用 Go，并且不打算回头了。\n\n    > Go 在效率、语言能力和编写代码的便利性之间找到了一个好的位置。\n    >\n    > <p style=\"text-align:right\">—— Ron Minnich</p>\n\n13. [Carbon Games](http://carbongames.com/)：一家网络游戏公司，为他们的后台服务使用 Go。\n\n14. [Vaba软件公司](http://vabasoftware.com/)：用 Go 重写了他们的消息和存储引擎。\n\n15. [Institute for Systems Biology](http://systemsbiology.org/)：用 Go 开发了分布式计算分析系统 [*Golem*](http://code.google.com/p/golem/)。\n\n16. [Second Bit](http://www.secondbit.org/)：使用 Go 来驱动他们的 2cloud 服务。\n\n17. [Numerotron Inc](http://www.stathat.com/)：用 Go 开发了他们的统计和事件跟踪系统 *StatHat*。\n\n最后是谷歌公司本身，它是 Go 的（发明者）之家。\n\nGo 在谷歌内部的使用是相当保密的，但在 2010 年 5 月，Rob Pike 宣布 Google 的后端基础设施正在运行用  Go 构建的应用程序（[参考文献 27]()）。Go 被用于一些系统（网络服务器，也包括存储系统和数据库），这些系统在跨越谷歌全球数据中心网络的分布式基础设施中发挥着作用。Go 可能会在未来几年内成为谷歌的标准后端语言。Andrew Gerrand 还说，谷歌员工正在使用 Go 来简单地从服务器上抓取信息。\n\n> “谷歌有管理应用程序和服务的人，他们需要编写工具来抓取几千台机器的状态并汇总数据，”他说，“以前，这些操作人员会用 Python 写这些东西，但他们发现 Go 在性能和实际写代码的时间方面要快得多。”\n\n关于 Go 在企业中的使用情况，可以在 http://go-lang.cat-v.org/organizations-using-go 上找到一个全面的清单。\n\n- [目录](directory.md)\n- 上一节：[Camilistore：一个可寻址内容存储系统](21.4.md)\n- 下一节：[附录](Appendices.md)\n"
  },
  {
    "path": "eBook/Discussion_about_16.10.md",
    "content": "\n\n## 关于本文·16.10.2小结糟糕错误处理的一些见解\n\n本文仅表达译者对错误处理的观点，并且觉得原文说的并不很合理，希望不会误导（我个人观点）其他入门读者。\n\n### 关于16.10.2的第一个代码示例\n\n16.10.2小结中关于错误处理的第一个代码示例是标准且通用的错误处理方式。\n文中认为这种错误处理方式会使你的代码中充满`if err != nil {...}`，认为这样会令人难以分辨正常的程序逻辑与错误处理（难道错误处理不算做正常的程序逻辑么:)）。\n\n**书中代码示例一**：\n\n```Go\n... err1 := api.Func1()\nif err1 != nil {\n    fmt.Println(\"err: \" + err.Error())\n    return\n}\nerr2 := api.Func2()\nif err2 != nil {\n...\n    return\n}\n```\n\n**我的观点**：\n\n1、错误处理也是正常程序逻辑的一部分，程序逻辑不就是对一个操作可能出现的结果进行判断，\n并对每一种结果做相应的后续处理么。错误是我们已知的可能会出现的一种结果，我们也需要处理这种情况，它也是正常逻辑的一部分。显然，把错误单独拎出来，与正常逻辑并列来做对待，并不合理。\n\n\n2、在其他语言中，我们可能会用到 try... catch...语句来对可能出现的错误进行处理，难道你会说try-catch语句让你的代码一团糟，程序逻辑和错误处理混在一起很复杂，让你阅读代码困难么。绝大多数情况下，让你感觉难以阅读甚至恶心（可能形容过度了）的代码绝不会是因为错误处理相关的代码导致的，而是当时写这些代码的人逻辑不清甚至逻辑混乱造成的。\n\n\n3、这个可能和每个人的习惯（自己写代码的思路、风格）或者说适应（看其他人的代码时能很快习惯作者的代码风格）有关，我每次看代码都会先略过错误处理的部分，那么剩下的就是理想情况下的程序逻辑了，如果对某一处心存疑惑那么就再仔细看这部分的代码。毕竟我们写的代码绝大多数情况下是希望它按理想的情况跑的，\n\n_ _ _\n\n### 关于16.10.2的第二个代码示例\n\n16.10.2小结中关于错误处理的第二个代码示例是推荐给我们的错误处理方式，对于其推荐的这种方式，个人认为是有一定的适用范围的，并不适合大多数的错误处理，反而在处理某些业务逻辑时可以使用，比如将不符合业务逻辑的情况视作一种错误（自定义）来统一做处理。\n\n**书中代码示例二**：\n\n```Go\nfunc httpRequestHandler(w http.ResponseWriter, req *http.Request) {\n    err := func () error {\n        if req.Method != \"GET\" {\n            return errors.New(\"expected GET\")\n        }\n        if input := parseInput(req); input != \"command\" {\n            return errors.New(\"malformed command\")\n        }\n        // 可以在此进行其他的错误检测\n    } ()\n\n        if err != nil {\n            w.WriteHeader(400)\n            io.WriteString(w, err)\n            return\n        }\n        doSomething() ...\n```\n\n1、代码示例二中对不符合业务逻辑的两种情况做了归类，并自定义了错误，做了统一的处理。这样从业务层面来看，将不符合业务逻辑的情况视为错误，统一写到了匿名函数中，剩下了一个统一的错误处理与正常的业务逻辑。或许采用这种方式处理这类场景还不错，但是如果换作下面的这个示例可能就不是很合理了。\n\n下面的示例一是采用了作者推荐的统一处理错误方式，示例二使用的是通常的错误处理方式\n\n**示例一**：\n\n```Go\n// 目标目录下包含多种Archive格式文件，将其中的'x-msdownload'类型文件移动到其他目录下\nfunc moveEXE(files []os.FileInfo, aimPath, exePath string) {\n\tvar numExe, numOther int\n\tvar fileBuf []byte\n\tvar fileType types.Type\n\n\tfor _, file := range files {\n\t\tfileName := aimPath + file.Name()\n\t\tnewFileName := exePath + file.Name()\n\n\t\terr := func() error {  \n            \n            // 读取文件内容\n\t\t\tif buf, err := ioutil.ReadFile(fileName); err != nil {\n\t\t\t\tlog.Printf(\"Time of read file: %s occur error: %s\\n\", fileName, err)\n\t\t\t\treturn err\n\t\t\t}else {\n\t\t\t\tfileBuf = buf\n\t\t\t}\n            \n            // 判断文件是否为Archive（压缩）格式\n\t\t\tif kind, err := filetype.Archive(fileBuf); err!= nil {\n\t\t\t\tlog.Printf(\"Time of judge file type occur error: %s\\n\", err)\n\t\t\t\treturn err\n\t\t\t}else {\n\t\t\t\tfileType = kind\n\t\t\t}\n            \n            // 文件是否为'x-msdownload'类型\n\t\t\tif fileSubType := fileType.MIME.Subtype; fileSubType == \"x-msdownload\" {\n\t\t\t\tlog.Printf(\"file : %s is exe file\\n\", fileName)\n\t\t\t\tif err := os.Rename(fileName, newFileName); err != nil {\n\t\t\t\t\tlog.Printf(\"mv file: %s faile, error is: %s\\n\", fileName, err)\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tnumExe ++\n\t\t\t}else {\n\t\t\t\tlog.Println(\"no exe\")\n\t\t\t\tnumOther ++\n\t\t\t}\n\t\t\treturn nil\n\t\t}()\n\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n    }\n    log.Printf(\"exe file num is: %d, other file num is: %d\", numExe, numOther)\n}\n```\n\n1、通常来说，我们使用匿名函数是因为部分操作不值得新定义一个函数或者该函数仅使用一次，示例一中的匿名函数包含了很多操作，或许我们应该为此重新定义一个函数。其中包含了几乎全部的逻辑代码，我想这看起来并不是啥好主意，甚至如果你把更多的逻辑代码放到了匿名函数里，看起来应该会更加糟糕。\n\n**示例二**：\n\n```Go\n\n// 目标目录下包含多种Archive格式文件，将其中的'x-msdownload'类型文件移动到其他目录下\nfunc moveEXE(files []os.FileInfo, aimPath, exePath string) {\n\tvar numExe, numOther int\n\n\tfor _, file := range files {\n\t\tfileName := aimPath + file.Name()\n\t\tnewFileName := exePath + file.Name()\n\t\t\n        // 读取文件内容\n\t\tbuf, err := ioutil.ReadFile(fileName)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"read file:%s  occur error\\n\", fileName)\n\t\t\tcontinue\n\t\t}\n\t\t\n        // 判断文件是否为Archive（压缩）格式\n\t\tkind, err := filetype.Archive(buf)\n\t\tif err != nil {\n\t\t\tlog.Println(\"judge file type error\")\n\t\t\tcontinue\n\t\t}\n\n        // 获取文件具体的类型\n\t\tfileSubType := kind.MIME.Subtype\n\n        // 文件是否为'x-msdownload'类型\n\t\tif fileSubType == \"x-msdownload\" {\n\t\t\tlog.Printf(\"file : %s is exe file\\n\", fileName)\n\t\t\terr := os.Rename(fileName, newFileName)\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"mv file: %s faile\\n\", fileName)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tnumExe ++\n\t\t}else {\n\t\t\tlog.Println(\"no exe\")\n\t\t\tnumOther ++\n\t\t}\n\t}\n\tlog.Printf(\"exe file num is: %d, other file num is: %d\", numExe, numOther)\n}\n```\n\n2、示例二中的代码看起来则自然多了（我是这种感觉），或许你认为这俩个例子相差无几，但是我想通过他们表明，原文16.10.2中推荐的错误处理方式是有一定的使用场景的，并不能取代标准且通用的错误处理方式，希望大家能够注意。\n\n---\n\n\n\n### 关于错误处理的一些延伸\n\n1、除了使用Go中已经定义好的error，我们也可以根据需要自定义error。\n\n下面的示例三，我们自定义了parseError 错误，展示了发生错误的文件和具体的错误信息，在你读取目录下的多个文件时可以方便的告诉你具体在读哪个文件时发生了错误（作为示例，仅读取单个文件）。\n\n示例四中，展示了调用 parseFile 函数时，调用者可以采用的一种错误处理方式，根据错误的类型，采取对应的操作。\n\n**示例三**：\n\n```go\n\ntype parseError struct {\n\tFile *os.File\n\tErrorInfo string\n}\n\n\nfunc (e *parseError) Error() string {\n\terrInfo := fmt.Sprintf(\n\t\t\"parse file: %s occur error, error info: %s\",\n\t\te.File.Name(),\n\t\te.ErrorInfo)\n\treturn errInfo\n}\n\n\nfunc parseFile(path string) error {\n\tf, err := os.Open(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\n\tvar buf [512]byte\n\tfor {\n\t\tswitch num, err := f.Read(buf[:]); {\n\t\tcase num < 0:\n\t\t\treadError := parseError{f, err.Error()}\n             log.Println(readError.Error())\n\t\t\treturn &readError\n            \n\t\tcase num == 0:\n\t\t\treadError := parseError{f, err.Error()}\n             log.Println(readError.Error())\n\t\t\treturn &readError\n\n\t\tcase num > 0:\n\t\t\tfmt.Println(string(buf[:num]))\n             log.Printf(\"read file: %s contents normally\")\n\t\t}\n\t}\n}\n\n```\n\n**示例四**：\n\n```go\nfunc main()  {\n\terr := parseFile(\"/home/rabbit/go/test_use/test\")\n\tswitch err := err.(type) {\n        \n\tcase *parseError:\n        log.Println(\"parse error: \", err)\n        \n\tcase *os.PathError:\n        log.Println(\"path error: \", err)\n\t}\n}\n```\n\n2、如果你想在返回错误之前做一些额外的操作，比如记录日志，那你可以单独写一个额外处理错误的函数或者一个匿名函数就可以（这取决于你是否常用该函数或它的功能是否很多），类似Python中的装饰器一样。\n\n示例五中，handleError 将错误写入到了指定日志文件中；\n\n示例六中，parseFile 中使用 `defer func() {handleError(\"/home/rabbit/go/test_use/log\", err)}()`代替了多次出现的`log.Println(readError.Error())`，并将日志记录持久化到文件中。\n\n**示例五**:\n\n```go\nfunc handleError(logPath string, err error) {\n    if err == nil {\n        return\n    }\n    \n    logFile, _ := os.OpenFile(filepath, os.O_RDWR|os.O_APPEND|os.O_CREATE, 666)\n\tdefer logFile.Close()\n\n\tlog.SetOutput(logFile)\n\tlog.SetPrefix(\"[FileError]\")\n\tlog.SetFlags(log.Llongfile|log.Ldate|log.Ltime)\n\tlog.Println(err.Error())\n}\n```\n\n**示例六**:\n\n```go\nfunc parseFile(path string) (err error) {\n\tf, err := os.Open(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\tdefer func() {handleError(\"/home/rabbit/go/test_use/log\", err)}()\n\n\tvar buf [512]byte\n\tfor {\n\t\tswitch num, err := f.Read(buf[:]); {\n\n\t\tcase num < 0:\n\t\t\terr := &parseError{f, err.Error()}\n\t\t\treturn err\n\n\t\tcase num == 0:\n\t\t\terr := &parseError{f, err.Error()}\n\t\t\treturn err\n\n\t\tcase num > 0:\n\t\t\tfmt.Println(string(buf[:num]))\n\t\t}\n\t}\n}\n```\n\n"
  },
  {
    "path": "eBook/directory.md",
    "content": "# 目录\r\n- [前言](preface.md)\r\n\r\n## 第一部分：学习 Go 语言\r\n\r\n- 第 1 章：Go 语言的起源，发展与普及\r\n\t- 1.1 [起源与发展](01.1.md)\r\n\t- 1.2 [语言的主要特性与发展的环境和影响因素](01.2.md)\r\n- 第 2 章：安装与运行环境\r\n\t- 2.1 [平台与架构](02.1.md)\r\n\t- 2.2 [Go 环境变量](02.2.md)\r\n\t- 2.3 [在 Linux 上安装 Go](02.3.md)\r\n\t- 2.4 [在 Mac OS X 上安装 Go](02.4.md)\r\n\t- 2.5 [在 Windows 上安装 Go](02.5.md)\r\n\t- 2.6 [安装目录清单](02.6.md)\r\n\t- 2.7 [Go 运行时 (runtime)](02.7.md)\r\n\t- 2.8 [Go 解释器](02.8.md)\r\n- 第 3 章：[编辑器、集成开发环境与其它工具](03.0.md)\r\n\t- 3.1 [Go 开发环境的基本要求](03.1.md)\r\n\t- 3.2 [编辑器和集成开发环境](03.2.md)\r\n\t- 3.3 [调试器](03.3.md)\r\n\t- 3.4 [构建并运行 Go 程序](03.4.md)\r\n\t- 3.5 [格式化代码](03.5.md)\r\n\t- 3.6 [生成代码文档](03.6.md)\r\n\t- 3.7 [其它工具](03.7.md)\r\n\t- 3.8 [Go 性能说明](03.8.md)\r\n\t- 3.9 [与其它语言进行交互](03.9.md)\r\n\r\n## 第二部分：语言的核心结构与技术\r\n\r\n- 第 4 章：基本结构和基本数据类型\r\n\t- 4.1 [文件名、关键字与标识符](04.1.md)\r\n\t- 4.2 [Go 程序的基本结构和要素](04.2.md)\r\n\t- 4.3 [常量](04.3.md)\r\n\t- 4.4 [变量](04.4.md)\r\n\t- 4.5 [基本类型和运算符](04.5.md)\r\n\t- 4.6 [字符串](04.6.md)\r\n\t- 4.7 [strings 和 strconv 包](04.7.md)\r\n\t- 4.8 [时间和日期](04.8.md)\r\n\t- 4.9 [指针](04.9.md)\r\n- 第 5 章：[控制结构](05.0.md)\r\n\t- 5.1 [if-else 结构](05.1.md)\r\n\t- 5.2 [测试多返回值函数的错误](05.2.md)\r\n\t- 5.3 [switch 结构](05.3.md)\r\n\t- 5.4 [for 结构](05.4.md)\r\n\t- 5.5 [Break 与 continue](05.5.md)\r\n\t- 5.6 [标签与 goto](05.6.md)\r\n- 第 6 章：[函数 (function)](06.0.md)\r\n\t- 6.1 [介绍](06.1.md)\r\n\t- 6.2 [函数参数与返回值](06.2.md)\r\n\t- 6.3 [传递变长参数](06.3.md)\r\n\t- 6.4 [defer 和追踪](06.4.md)\r\n\t- 6.5 [内置函数](06.5.md)\r\n\t- 6.6 [递归函数](06.6.md)\r\n\t- 6.7 [将函数作为参数](06.7.md)\r\n\t- 6.8 [闭包](06.8.md)\r\n\t- 6.9 [应用闭包：将函数作为返回值](06.9.md)\r\n\t- 6.10 [使用闭包调试](06.10.md)\r\n\t- 6.11 [计算函数执行时间](06.11.md)\r\n\t- 6.12 [通过内存缓存来提升性能](06.12.md)\r\n- 第 7 章：[数组与切片](07.0.md)\r\n\t- 7.1 [声明和初始化](07.1.md)\r\n\t- 7.2 [切片](07.2.md)\r\n\t- 7.3 [For-range 结构](07.3.md)\r\n\t- 7.4 [切片重组 (reslice)](07.4.md)\r\n\t- 7.5 [切片的复制与追加](07.5.md)\r\n\t- 7.6 [字符串、数组和切片的应用](07.6.md)\r\n- 第 8 章：[Map](08.0.md)\r\n\t- 8.1 [声明、初始化和 make](08.1.md)\r\n\t- 8.2 [测试键值对是否存在及删除元素](08.2.md)\r\n\t- 8.3 [for-range 的配套用法](08.3.md)\r\n\t- 8.4 [map 类型的切片](08.4.md)\r\n\t- 8.5 [map 的排序](08.5.md)\r\n\t- 8.6 [将 map 的键值对调](08.6.md)\r\n- 第 9 章：[包 (package)](09.0.md)\r\n\t- 9.1 [标准库概述](09.1.md)\r\n\t- 9.2 [regexp 包](09.2.md)\r\n\t- 9.3 [锁和 sync 包](09.3.md)\r\n\t- 9.4 [精密计算和 big 包](09.4.md)\r\n\t- 9.5 [自定义包和可见性](09.5.md)\r\n\t- 9.6 [为自定义包使用 godoc](09.6.md)\r\n\t- 9.7 [使用 go install 安装自定义包](09.7.md)\r\n\t- 9.8 [自定义包的目录结构、go install 和 go test](09.8.md)\r\n\t- 9.9 [通过 Git 打包和安装](09.9.md)\r\n\t- 9.10 [Go 的外部包和项目](09.10.md)\r\n\t- 9.11 [在 Go 程序中使用外部库](09.11.md)\r\n- 第 10 章：[结构 (struct) 与方法 (method)](10.0.md)\r\n    - 10.1 [结构体定义](10.1.md)\r\n    - 10.2 [使用工厂方法创建结构体实例](10.2.md)\r\n    - 10.3 [使用自定义包中的结构体](10.3.md)\r\n    - 10.4 [带标签的结构体](10.4.md)\r\n    - 10.5 [匿名字段和内嵌结构体](10.5.md)\r\n    - 10.6 [方法](10.6.md)\r\n    - 10.7 [类型的 String() 方法和格式化描述符](10.7.md)\r\n    - 10.8 [垃圾回收和 SetFinalizer](10.8.md)\r\n- 第 11 章：[接口 (interface) 与反射 (reflection)](11.0.md)\r\n    - 11.1 [接口是什么](11.1.md)\r\n    - 11.2 [接口嵌套接口](11.2.md)\r\n    - 11.3 [类型断言：如何检测和转换接口变量的类型](11.3.md)\r\n    - 11.4 [类型判断：type-switch](11.4.md)\r\n    - 11.5 [测试一个值是否实现了某个接口](11.5.md)\r\n    - 11.6 [使用方法集与接口](11.6.md)\r\n    - 11.7 [第一个例子：使用 Sorter 接口排序](11.7.md)\r\n    - 11.8 [第二个例子：读和写](11.8.md)\r\n    - 11.9 [空接口](11.9.md)\r\n    - 11.10 [反射包](11.10.md)\r\n    - 11.11 [Printf 和反射](11.11.md)\r\n    - 11.12 [接口与动态类型](11.12.md)\r\n    - 11.13 [总结：Go 中的面向对象](11.13.md)\r\n    - 11.14 [结构体、集合和高阶函数](11.14.md)\r\n\r\n## 第三部分：Go 高级编程\r\n\r\n- 第 12 章：[读写数据](12.0.md)\r\n    - 12.1 [读取用户的输入](12.1.md)\r\n    - 12.2 [文件读写](12.2.md)\r\n    - 12.3 [文件拷贝](12.3.md)\r\n    - 12.4 [从命令行读取参数](12.4.md)\r\n    - 12.5 [用 buffer 读取文件](12.5.md)\r\n    - 12.6 [用切片读写文件](12.6.md)\r\n    - 12.7 [用 defer 关闭文件](12.7.md)\r\n    - 12.8 [使用接口的实际例子：fmt.Fprintf](12.8.md)\r\n    - 12.9 [JSON 数据格式](12.9.md)\r\n    - 12.10 [XML 数据格式](12.10.md)\r\n    - 12.11 [用 Gob 传输数据](12.11.md)\r\n    - 12.12 [Go 中的密码学](12.12.md)\r\n- 第 13 章：[错误处理与测试](13.0.md)\r\n    - 13.1 [错误处理](13.1.md)\r\n    - 13.2 [运行时异常和 panic](13.2.md)\r\n    - 13.3 [从 panic 中恢复 (recover)](13.3.md)\r\n    - 13.4 [自定义包中的错误处理和 panicking](13.4.md)\r\n    - 13.5 [一种用闭包处理错误的模式](13.5.md)\r\n    - 13.6 [启动外部命令和程序](13.6.md)\r\n    - 13.7 [Go 中的单元测试和基准测试](13.7.md)\r\n    - 13.8 [测试的具体例子](13.8.md)\r\n    - 13.9 [用（测试数据）表驱动测试](13.9.md)\r\n    - 13.10 [性能调试：分析并优化 Go 程序](13.10.md)\r\n- 第 14 章：[协程 (goroutine) 与通道 (channel)](14.0.md)\r\n    - 14.1 [并发、并行和协程](14.1.md)\r\n    - 14.2 [协程间的信道](14.2.md)\r\n    - 14.3 [协程的同步：关闭通道-测试阻塞的通道](14.3.md)\r\n    - 14.4 [使用 select 切换协程](14.4.md)\r\n    - 14.5 [通道、超时和计时器 (Ticker)](14.5.md)\r\n    - 14.6 [协程和恢复 (recover)](14.6.md)\r\n    - 14.7 [新旧模型对比：任务和worker](14.7.md)\r\n    - 14.8 [惰性生成器的实现](14.8.md)\r\n    - 14.9 [实现 Futures 模式](14.9.md)\r\n    - 14.10 [复用](14.10.md)\r\n    - 14.11 [限制同时处理的请求数](14.11.md)\r\n    - 14.12 [链式协程](14.12.md)\r\n    - 14.13 [在多核心上并行计算](14.13.md)\r\n    - 14.14 [并行化大量数据的计算](14.14.md)\r\n    - 14.15 [漏桶算法](14.15.md)\r\n    - 14.16 [对Go协程进行基准测试](14.16.md)\r\n    - 14.17 [使用通道并发访问对象](14.17.md)\r\n- 第 15 章：[网络、模板与网页应用](15.0.md)\r\n    - 15.1 [tcp 服务器](15.1.md)\r\n    - 15.2 [一个简单的 web 服务器](15.2.md)\r\n    - 15.3 [访问并读取页面数据](15.3.md)\r\n    - 15.4 [写一个简单的网页应用](15.4.md)\r\n    - 15.5 [确保网页应用健壮](15.5.md)\r\n    - 15.6 [用模板编写网页应用](15.6.md)\r\n    - 15.7 [探索 template 包](15.7.md)\r\n    - 15.8 [精巧的多功能网页服务器](15.8.md)\r\n    - 15.9 [用 rpc 实现远程过程调用](15.9.md)\r\n    - 15.10 [基于网络的通道 netchan](15.10.md)\r\n    - 15.11 [与 websocket 通信](15.11.md)\r\n    - 15.12 [用 smtp 发送邮件](15.12.md)\r\n\r\n## 第四部分：实际应用\r\n\r\n- 第 16 章：[常见的陷阱与错误](16.0.md)\r\n    - 16.1 [误用短声明导致变量覆盖](16.1.md)\r\n    - 16.2 [误用字符串](16.2.md)\r\n    - 16.3 [发生错误时使用 defer 关闭一个文件](16.3.md)\r\n    - 16.4 [何时使用 new() 和 make()](16.4.md)\r\n    - 16.5 [不需要将一个指向切片的指针传递给函数](16.5.md)\r\n    - 16.6 [使用指针指向接口类型](16.6.md)\r\n    - 16.7 [使用值类型时误用指针](16.7.md)\r\n    - 16.8 [误用协程和通道](16.8.md)\r\n    - 16.9 [闭包和协程的使用](16.9.md)\r\n    - 16.10 [糟糕的错误处理](16.10.md)\r\n- 第 17 章：[模式](17.0.md)\r\n    - 17.1 [逗号 ok 模式](17.1.md)\r\n    - 17.2 [defer 模式](17.2.md)\r\n    - 17.3 [可见性模式](17.3.md)\r\n    - 17.4 [运算符模式和接口](17.4.md)\r\n- 第 18 章：[出于性能考虑的实用代码片段](18.0.md)\r\n    - 18.1 [字符串](18.1.md)\r\n    - 18.2 [数组和切片](18.2.md)\r\n    - 18.3 [映射](18.3.md)\r\n    - 18.4 [结构体](18.4.md)\r\n    - 18.5 [接口](18.5.md)\r\n    - 18.6 [函数](18.6.md)\r\n    - 18.7 [文件](18.7.md)\r\n    - 18.8 [协程 (goroutine) 与通道 (channel)](18.8.md)\r\n    - 18.9 [网络和网页应用](18.9.md)\r\n    - 18.10 [其他](18.10.md)\r\n    - 18.11 [出于性能考虑的最佳实践和建议](18.11.md)\r\n- 第 19 章：[构建一个完整的应用程序](19.0.md)\r\n    - 19.1 [简介](19.1.md)\r\n    - 19.2 [短网址项目简介](19.2.md)\r\n    - 19.3 [数据结构](19.3.md)\r\n    - 19.4 [用户界面：web 服务端](19.4.md)\r\n    - 19.5 [持久化存储：gob](19.5.md)\r\n    - 19.6 [用协程优化性能](19.6.md)\r\n    - 19.7 [以 json 格式存储](19.7.md)\r\n    - 19.8 [多服务器处理架构](19.8.md)\r\n    - 19.9 [使用代理缓存](19.9.md)\r\n    - 19.10 [总结和增强](19.10.md)\r\n- 第 20 章：[Go 语言在 Google App Engine 的使用](20.0.md)\r\n    - 20.1 [什么是 Google App Engine？](20.1.md)\r\n    - 20.2 [云上的 Go](20.2.md)\r\n    - 20.3 [安装 Go App Engine SDK：为 Go 部署的开发环境](20.3.md)\r\n    - 20.4 [建造你自己的 Hello world 应用](20.4.md)\r\n    - 20.5 [使用用户服务和探索其 API](20.5.md)\r\n    - 20.6 [处理窗口](20.6.md)\r\n    - 20.7 [使用数据存储](20.7.md)\r\n    - 20.8 [上传到云端](20.8.md)\r\n- 第 21 章：[真实世界中 Go 的使用](21.0.md)\r\n    - 21.1 [Heroku：一个使用 Go 的高度可用一致数据存储](21.1.md)\r\n    - 21.2 [MROffice：一个使用 Go 的呼叫中心网络电话 (VOIP) 系统](21.2.md)\r\n    - 21.3 [Atlassian：一个虚拟机群管理系统](21.3.md)\r\n    - 21.4 [Camilistore：一个可寻址内容存储系统](21.4.md)\r\n    - 21.5 [Go 语言的其他应用](21.5.md)\r\n\r\n## 附录\r\n\r\n- A 代码引用\r\n- B 有趣的 Go 引用\r\n- C 代码示例列表\r\n- D 书中的包引用\r\n- E 书中的工具引用\r\n- F 常见问题解答\r\n- G 习题答案\r\n- H 参考文献\r\n\r\n## 索引\r\n"
  },
  {
    "path": "eBook/examples/chapter_10/embed_func1.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n)\n\ntype Log struct {\n\tmsg string\n}\n\ntype Customer struct {\n\tName string\n\tlog  *Log\n}\n\nfunc main() {\n\t// c := new(Customer)\n\t// c.Name = \"Barak Obama\"\n\t// c.log = new(Log)\n\t// c.log.msg = \"1 - Yes we can!\"\n\t// shorter:\n\tc := &Customer{\"Barak Obama\", &Log{\"1 - Yes we can!\"}}\n\t// fmt.Println(c)   // &{Barak Obama 1 - Yes we can!}\n\tc.Log().Add(\"2 - After me the world will be a better place!\")\n\t//fmt.Println(c.log)\n\tfmt.Println(c.Log())\n}\n\nfunc (l *Log) Add(s string) {\n\tl.msg += \"\\n\" + s\n}\n\nfunc (l *Log) String() string {\n\treturn l.msg\n}\n\nfunc (c *Customer) Log() *Log {\n\treturn c.log\n}\n\n/* Output:\n1 - Yes we can!\n2 - After me the world will be a better place!\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_10/embed_func2.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n)\n\ntype Log struct {\n\tmsg string\n}\n\ntype Customer struct {\n\tName string\n\tLog\n}\n\nfunc main() {\n\tc := &Customer{\"Barak Obama\", Log{\"1 - Yes we can!\"}}\n\tc.Add(\"2 - After me the world will be a better place!\")\n\tfmt.Println(c)\n}\n\nfunc (l *Log) Add(s string) {\n\tl.msg += \"\\n\" + s\n}\n\nfunc (c *Customer) String() string {\n\treturn c.Name + \"\\nLog:\" + fmt.Sprintln(c.Log)\n}\n\nfunc (l *Log) String() string {\n\treturn l.msg\n}\n\n/* Output:\nBarak Obama\nLog:{1 - Yes we can!\n2 - After me the world will be a better place!}\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_10/embedd_struct.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype A struct {\n\tax, ay int\n}\n\ntype B struct {\n\tA\n\tbx, by float32\n}\n\nfunc main() {\n\tb := B{A{1, 2}, 3.0, 4.0}\n\tfmt.Println(b.ax, b.ay, b.bx, b.by)\n\tfmt.Println(b.A)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_10/main.go",
    "content": "package main\n\nimport (\n\t\"./struct_pack/structPack\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tstruct1 := new(structPack.ExpStruct)\n\tstruct1.Mi1 = 10\n\tstruct1.Mf1 = 16.\n\tfmt.Printf(\"Mi1 = %d\\n\", struct1.Mi1)\n\tfmt.Printf(\"Mf1 = %f\\n\", struct1.Mf1)\n}\n\n// Mi1 = 10\n// Mf1 = 16.000000\n"
  },
  {
    "path": "eBook/examples/chapter_10/method1.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype TwoInts struct {\n\ta int\n\tb int\n}\n\nfunc main() {\n\ttwo1 := new(TwoInts)\n\ttwo1.a = 12\n\ttwo1.b = 10\n\n\tfmt.Printf(\"The sum is: %d\\n\", two1.AddThem())\n\tfmt.Printf(\"Add them to the param: %d\\n\", two1.AddToParam(20))\n\n\t// literal:\n\ttwo2 := TwoInts{3, 4}\n\tfmt.Printf(\"The sum is: %d\\n\", two2.AddThem())\n}\n\nfunc (tn *TwoInts) AddThem() int {\n\treturn tn.a + tn.b\n}\n\nfunc (tn *TwoInts) AddToParam(param int) int {\n\treturn tn.a + tn.b + param\n}\n"
  },
  {
    "path": "eBook/examples/chapter_10/method2.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype IntVector []int\n\nfunc (v IntVector) Sum() (s int) {\n\tfor _, x := range v {\n\t\ts += x\n\t}\n\treturn\n}\n\nfunc main() {\n\tfmt.Println(IntVector{1, 2, 3}.Sum()) // Output: 6\n}\n"
  },
  {
    "path": "eBook/examples/chapter_10/method3.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n)\n\ntype Point struct {\n\tx, y float64\n}\n\nfunc (p *Point) Abs() float64 {\n\treturn math.Sqrt(p.x*p.x + p.y*p.y)\n}\n\ntype NamedPoint struct {\n\tPoint\n\tname string\n}\n\nfunc main() {\n\tn := &NamedPoint{Point{3, 4}, \"Pythagoras\"}\n\tfmt.Println(n.Abs()) // prints 5\n}\n"
  },
  {
    "path": "eBook/examples/chapter_10/method4.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n)\n\ntype Point struct {\n\tx, y float64\n}\n\nfunc (p *Point) Abs() float64 {\n\treturn math.Sqrt(p.x*p.x + p.y*p.y)\n}\n\ntype NamedPoint struct {\n\tPoint\n\tname string\n}\n\nfunc (n *NamedPoint) Abs() float64 {\n\treturn n.Point.Abs() * 100.\n}\n\nfunc main() {\n\tn := &NamedPoint{Point{3, 4}, \"Pythagoras\"}\n\tfmt.Println(n.Abs()) // prints 500\n}\n"
  },
  {
    "path": "eBook/examples/chapter_10/method_on_time.go",
    "content": "// method_on_time.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\ntype myTime struct {\n\ttime.Time //anonymous field\n}\n\nfunc (t myTime) first3Chars() string {\n\treturn t.Time.String()[0:3]\n}\n\nfunc main() {\n\tm := myTime{time.Now()}\n\tfmt.Println(\"Full time now:\", m.String())      //calling existing String method on anonymous Time field\n\tfmt.Println(\"First 3 chars:\", m.first3Chars()) //calling myTime.first3Chars\n}\n\n/* Output:\nFull time now: Mon Oct 24 15:34:54 Romance Daylight Time 2011\nFirst 3 chars: Mon\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_10/method_string.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n)\n\ntype TwoInts struct {\n\ta int\n\tb int\n}\n\nfunc main() {\n\ttwo1 := new(TwoInts)\n\ttwo1.a = 12\n\ttwo1.b = 10\n\tfmt.Printf(\"two1 is: %v\\n\", two1)\n\tfmt.Println(\"two1 is:\", two1)\n\tfmt.Printf(\"two1 is: %T\\n\", two1)\n\tfmt.Printf(\"two1 is: %#v\\n\", two1)\n}\n\nfunc (tn *TwoInts) String() string {\n\treturn \"(\" + strconv.Itoa(tn.a) + \" / \" + strconv.Itoa(tn.b) + \")\"\n}\n"
  },
  {
    "path": "eBook/examples/chapter_10/methodset1.go",
    "content": "// methodset1.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\ntype List []int\n\nfunc (l List) Len() int        { return len(l) }\nfunc (l *List) Append(val int) { *l = append(*l, val) }\n\nfunc main() {\n\t// A bare value\n\tvar lst List\n\tlst.Append(1)\n\tfmt.Printf(\"%v (len: %d)\\n\", lst, lst.Len()) // [1] (len: 1)\n\n\t// A pointer value\n\tplst := new(List)\n\tplst.Append(2)\n\tfmt.Printf(\"%v (len: %d)\\n\", plst, lst.Len()) // &[2] (len: 1)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_10/mult_inheritance.go",
    "content": "// mult_inheritance.go\npackage main\n\nimport \"fmt\"\n\ntype Camera struct{}\n\nfunc (c *Camera) TakeAPicture() string {\n\treturn \"Click\"\n}\n\ntype Phone struct{}\n\nfunc (p *Phone) Call() string {\n\treturn \"Ring Ring\"\n}\n\n// multiple inheritance\ntype CameraPhone struct {\n\tCamera\n\tPhone\n}\n\nfunc main() {\n\tcp := new(CameraPhone)\n\tfmt.Println(\"Our new CameraPhone exhibits multiple behaviors ...\")\n\tfmt.Println(\"It exhibits behavior of a Camera: \", cp.TakeAPicture())\n\tfmt.Println(\"It works like a Phone too: \", cp.Call())\n}\n\n/* Output:\nOur new CameraPhone exhibits multiple behaviors ...\nIt exhibits behavior of a Camera:  Click\nIt works like a Phone too:  Ring Ring\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_10/new_make.go",
    "content": "// annoy1.go\npackage main\n\ntype Foo map[string]string\ntype Bar struct {\n\tthingOne string\n\tthingTwo int\n}\n\nfunc main() {\n\t// OK:\n\ty := new(Bar)\n\t(*y).thingOne = \"hello\"\n\t(*y).thingTwo = 1\n\t// not OK:\n\tz := make(Bar) // compile error: cannot make type Bar\n\tz.thingOne = \"hello\"\n\tz.thingTwo = 1\n\t// OK:\n\tx := make(Foo)\n\tx[\"x\"] = \"goodbye\"\n\tx[\"y\"] = \"world\"\n\t// not OK:\n\tu := new(Foo)\n\t(*u)[\"x\"] = \"goodbye\" // !! panic !!: runtime error: assignment to entry in nil map\n\t(*u)[\"y\"] = \"world\"\n}\n"
  },
  {
    "path": "eBook/examples/chapter_10/person.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\ntype Person struct {\n\tfirstName string\n\tlastName  string\n}\n\nfunc upPerson(p *Person) {\n\tp.firstName = strings.ToUpper(p.firstName)\n\tp.lastName = strings.ToUpper(p.lastName)\n}\n\nfunc main() {\n\t// 1- struct as a value type:\n\tvar pers1 Person\n\tpers1.firstName = \"Chris\"\n\tpers1.lastName = \"Woodward\"\n\tupPerson(&pers1)\n\tfmt.Printf(\"The name of the person is %s %s\\n\", pers1.firstName, pers1.lastName)\n\t// 2 - struct as a pointer:\n\tpers2 := new(Person)\n\tpers2.firstName = \"Chris\"\n\tpers2.lastName = \"Woodward\"\n\t(*pers2).lastName = \"Woodward\"\n\tupPerson(pers2)\n\tfmt.Printf(\"The name of the person is %s %s\\n\", pers2.firstName, pers2.lastName)\n\t// 3 - struct as a literal:\n\tpers3 := &Person{\"Chris\", \"Woodward\"}\n\tupPerson(pers3)\n\tfmt.Printf(\"The name of the person is %s %s\\n\", pers3.firstName, pers3.lastName)\n}\n\n/* Output:\nThe name of the person is CHRIS WOODWARD\nThe name of the person is CHRIS WOODWARD\nThe name of the person is CHRIS WOODWARD\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_10/person2.go",
    "content": "package person\n\ntype Person struct {\n\tfirstName string\n\tlastName  string\n}\n\nfunc (p *Person) FirstName() string {\n\treturn p.firstName\n}\n\nfunc (p *Person) SetFirstName(newName string) {\n\tp.firstName = newName\n}\n"
  },
  {
    "path": "eBook/examples/chapter_10/pointer_value.go",
    "content": "// pointer_value.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\ntype B struct {\n\tthing int\n}\n\nfunc (b *B) change() { b.thing = 1 }\n\nfunc (b B) write() string { return fmt.Sprint(b) }\n\nfunc main() {\n\tvar b1 B // b1 is value\n\tb1.change()\n\tfmt.Println(b1.write())\n\n\tb2 := new(B) // b2 is pointer\n\tb2.change()\n\tfmt.Println(b2.write())\n}\n\n/* Output:\n{1}\n{1}\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_10/struct_conversions.go",
    "content": "// struct_conversions.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\ntype number struct {\n\tf float32\n}\n\ntype nr number // alias type\n\nfunc main() {\n\ta := number{5.0}\n\tb := nr{5.0}\n\t// var i float32 = b   // compile-error: cannot use b (type nr) as type float32 in assignment\n\t// var i = float32(b)  // compile-error: cannot convert b (type nr) to type float32\n\t// var c number = b    // compile-error: cannot use b (type nr) as type number in assignment\n\t// needs a conversion:\n\tvar c = number(b)\n\tfmt.Println(a, b, c)\n}\n\n// output: {5} {5} {5}\n"
  },
  {
    "path": "eBook/examples/chapter_10/struct_pack/structPack.go",
    "content": "package structPack\n\ntype ExpStruct struct {\n\tMi1 int\n\tMf1 float32\n}\n"
  },
  {
    "path": "eBook/examples/chapter_10/struct_tag.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\ntype TagType struct { //  tags\n\tfield1 bool   \"An important answer\"\n\tfield2 string \"The name of the thing\"\n\tfield3 int    \"How much there are\"\n}\n\nfunc main() {\n\ttt := TagType{true, \"Barak Obama\", 1}\n\tfor i := 0; i < 3; i++ {\n\t\trefTag(tt, i)\n\t}\n}\n\nfunc refTag(tt TagType, ix int) {\n\tttType := reflect.TypeOf(tt)\n\tixField := ttType.Field(ix)\n\tfmt.Printf(\"%v\\n\", ixField.Tag)\n}\n\n/* Output:\nAn important answer\nThe name of the thing\nHow much there are\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_10/structs_anonymous_fields.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype innerS struct {\n\tin1 int\n\tin2 int\n}\n\ntype outerS struct {\n\tb      int\n\tc      float32\n\tint    // anonymous field\n\tinnerS // anonymous field\n}\n\nfunc main() {\n\touter := new(outerS)\n\touter.b = 6\n\touter.c = 7.5\n\touter.int = 60\n\touter.in1 = 5\n\touter.in2 = 10\n\n\tfmt.Printf(\"outer.b is: %d\\n\", outer.b)\n\tfmt.Printf(\"outer.c is: %f\\n\", outer.c)\n\tfmt.Printf(\"outer.int is: %d\\n\", outer.int)\n\tfmt.Printf(\"outer.in1 is: %d\\n\", outer.in1)\n\tfmt.Printf(\"outer.in2 is: %d\\n\", outer.in2)\n\t// with a struct-literal:\n\touter2 := outerS{6, 7.5, 60, innerS{5, 10}}\n\tfmt.Println(\"outer2 is: \", outer2)\n\n}\n"
  },
  {
    "path": "eBook/examples/chapter_10/structs_fields.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype struct1 struct {\n\ti1  int\n\tf1  float32\n\tstr string\n}\n\nfunc main() {\n\t// var ms *struct1 = new(struct1)\n\t// better:\n\tms := new(struct1)\n\tms.i1 = 10\n\tms.f1 = 15.5\n\tms.str = \"Chris\"\n\t// ms := &struct1{10, 15.5, \"Chris\"}\n\tfmt.Printf(\"The int is: %d\\n\", ms.i1)\n\tfmt.Printf(\"The float is: %f\\n\", ms.f1)\n\tfmt.Printf(\"The string is: %s\\n\", ms.str)\n\tfmt.Println(ms) // output: &{10 15.5 Chris}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_10/use_person2.go",
    "content": "package main\n\nimport (\n\t\"./person\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tp := new(person.Person)\n\t// error: p.firstName undefined (cannot refer to unexported field or method firstName)\n\t// p.firstName = \"Eric\"\n\tp.SetFirstName(\"Eric\")\n\tfmt.Println(p.FirstName()) // Output: Eric\n}\n"
  },
  {
    "path": "eBook/examples/chapter_10/vcard.json",
    "content": "{\"FirstName\":\"Jan\",\"LastName\":\"Kersschot\",\"Addresses\":[{\"Type\":\"private\",\"City\":\"Aartselaar\",\"Country\":\"Belgium\"},{\"Type\":\"work\",\"City\":\"Boom\",\"Country\":\"Belgium\"}],\"Remark\":\"none\"}\n"
  },
  {
    "path": "eBook/examples/chapter_11/cars.go",
    "content": "// cars.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\ntype Any interface{}\ntype Car struct {\n\tModel        string\n\tManufacturer string\n\tBuildYear    int\n\t// ...\n}\ntype Cars []*Car\n\nfunc main() {\n\t// make some cars:\n\tford := &Car{\"Fiesta\", \"Ford\", 2008}\n\tbmw := &Car{\"XL 450\", \"BMW\", 2011}\n\tmerc := &Car{\"D600\", \"Mercedes\", 2009}\n\tbmw2 := &Car{\"X 800\", \"BMW\", 2008}\n\t// query:\n\tallCars := Cars([]*Car{ford, bmw, merc, bmw2})\n\tallNewBMWs := allCars.FindAll(func(car *Car) bool {\n\t\treturn (car.Manufacturer == \"BMW\") && (car.BuildYear > 2010)\n\t})\n\tfmt.Println(\"AllCars: \", allCars)\n\tfmt.Println(\"New BMWs: \", allNewBMWs)\n\t//\n\tmanufacturers := []string{\"Ford\", \"Aston Martin\", \"Land Rover\", \"BMW\", \"Jaguar\"}\n\tsortedAppender, sortedCars := MakeSortedAppender(manufacturers)\n\tallCars.Process(sortedAppender)\n\tfmt.Println(\"Map sortedCars: \", sortedCars)\n\tBMWCount := len(sortedCars[\"BMW\"])\n\tfmt.Println(\"We have \", BMWCount, \" BMWs\")\n}\n\n// Process all cars with the given function f:\nfunc (cs Cars) Process(f func(car *Car)) {\n\tfor _, c := range cs {\n\t\tf(c)\n\t}\n}\n\n// Find all cars matching a given criteria.\nfunc (cs Cars) FindAll(f func(car *Car) bool) Cars {\n\tcars := make([]*Car, 0)\n\n\tcs.Process(func(c *Car) {\n\t\tif f(c) {\n\t\t\tcars = append(cars, c)\n\t\t}\n\t})\n\treturn cars\n}\n\n// Process cars and create new data.\nfunc (cs Cars) Map(f func(car *Car) Any) []Any {\n\tresult := make([]Any, 0)\n\tix := 0\n\tcs.Process(func(c *Car) {\n\t\tresult[ix] = f(c)\n\t\tix++\n\t})\n\treturn result\n}\n\nfunc MakeSortedAppender(manufacturers []string) (func(car *Car), map[string]Cars) {\n\t// Prepare maps of sorted cars.\n\tsortedCars := make(map[string]Cars)\n\n\tfor _, m := range manufacturers {\n\t\tsortedCars[m] = make([]*Car, 0)\n\t}\n\tsortedCars[\"Default\"] = make([]*Car, 0)\n\n\t// Prepare appender function:\n\tappender := func(c *Car) {\n\t\tif _, ok := sortedCars[c.Manufacturer]; ok {\n\t\t\tsortedCars[c.Manufacturer] = append(sortedCars[c.Manufacturer], c)\n\t\t} else {\n\t\t\tsortedCars[\"Default\"] = append(sortedCars[\"Default\"], c)\n\t\t}\n\t}\n\treturn appender, sortedCars\n}\n\n/* Output:\nAllCars:  [0xf8400038a0 0xf840003bd0 0xf840003ba0 0xf840003b70]\nNew BMWs:  [0xf840003bd0]\nMap sortedCars:  map[Default:[0xf840003ba0] Jaguar:[] Land Rover:[] BMW:[0xf840003bd0 0xf840003b70] Aston Martin:[] Ford:[0xf8400038a0]]\nWe have  2  BMWs\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_11/duck_dance.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype IDuck interface {\n\tQuack()\n\tWalk()\n}\n\nfunc DuckDance(duck IDuck) {\n\tfor i := 1; i <= 3; i++ {\n\t\tduck.Quack()\n\t\tduck.Walk()\n\t}\n}\n\ntype Bird struct {\n\t// ...\n}\n\nfunc (b *Bird) Quack() {\n\tfmt.Println(\"I am quacking!\")\n}\n\nfunc (b *Bird) Walk() {\n\tfmt.Println(\"I am walking!\")\n}\n\nfunc main() {\n\tb := new(Bird)\n\tDuckDance(b)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_11/empty_interface.go",
    "content": "package main\n\nimport \"fmt\"\n\nvar i = 5\nvar str = \"ABC\"\n\ntype Person struct {\n\tname string\n\tage  int\n}\n\ntype Any interface{}\n\nfunc main() {\n\tvar val Any\n\tval = 5\n\tfmt.Printf(\"val has the value: %v\\n\", val)\n\tval = str\n\tfmt.Printf(\"val has the value: %v\\n\", val)\n\tpers1 := new(Person)\n\tpers1.name = \"Rob Pike\"\n\tpers1.age = 55\n\tval = pers1\n\tfmt.Printf(\"val has the value: %v\\n\", val)\n\tswitch t := val.(type) {\n\tcase int:\n\t\tfmt.Printf(\"Type int %T\\n\", t)\n\tcase string:\n\t\tfmt.Printf(\"Type string %T\\n\", t)\n\tcase bool:\n\t\tfmt.Printf(\"Type boolean %T\\n\", t)\n\tcase *Person:\n\t\tfmt.Printf(\"Type pointer to Person %T\\n\", *t)\n\tdefault:\n\t\tfmt.Printf(\"Unexpected type %T\", t)\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_11/emptyint_switch.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype specialString string\n\nvar whatIsThis specialString = \"hello\"\n\nfunc TypeSwitch() {\n\ttestFunc := func(any interface{}) {\n\t\tswitch v := any.(type) {\n\t\tcase bool:\n\t\t\tfmt.Printf(\"any %v is a bool type\", v)\n\t\tcase int:\n\t\t\tfmt.Printf(\"any %v is an int type\", v)\n\t\tcase float32:\n\t\t\tfmt.Printf(\"any %v is a float32 type\", v)\n\t\tcase string:\n\t\t\tfmt.Printf(\"any %v is a string type\", v)\n\t\tcase specialString:\n\t\t\tfmt.Printf(\"any %v is a special String!\", v)\n\t\tdefault:\n\t\t\tfmt.Println(\"unknown type!\")\n\t\t}\n\t}\n\ttestFunc(whatIsThis)\n}\n\nfunc main() {\n\tTypeSwitch()\n}\n\n// Output:  any hello is a special String!\n"
  },
  {
    "path": "eBook/examples/chapter_11/interfaces.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype Shaper interface {\n\tArea() float32\n\t// Perimeter() float32\n}\n\ntype Square struct {\n\tside float32\n}\n\nfunc (sq *Square) Area() float32 {\n\treturn sq.side * sq.side\n}\n\nfunc main() {\n\tsq1 := new(Square)\n\tsq1.side = 5\n\n\tvar areaIntf Shaper\n\tareaIntf = sq1\n\t// shorter, without separate declaration:\n\t// areaIntf := Shaper(sq1)\n\t// or even:\n\t// areaIntf := sq1\n\tfmt.Printf(\"The square has area: %f\\n\", areaIntf.Area())\n}\n\n// The square has area: 25.000000\n"
  },
  {
    "path": "eBook/examples/chapter_11/interfaces_poly.go",
    "content": "// interfaces_poly.go\npackage main\n\nimport \"fmt\"\n\ntype Shaper interface {\n\tArea() float32\n}\n\ntype Square struct {\n\tside float32\n}\n\nfunc (sq *Square) Area() float32 {\n\treturn sq.side * sq.side\n}\n\ntype Rectangle struct {\n\tlength, width float32\n}\n\nfunc (r Rectangle) Area() float32 {\n\treturn r.length * r.width\n}\n\nfunc main() {\n\tr := Rectangle{5, 3} // Area() of Rectangle needs a value\n\tq := &Square{5}      // Area() of Square needs a pointer\n\tshapes := []Shaper{r, q}\n\tfmt.Println(\"Looping through shapes for area ...\")\n\tfor n := range shapes {\n\t\tfmt.Println(\"Shape details: \", shapes[n])\n\t\tfmt.Println(\"Area of this shape is: \", shapes[n].Area())\n\t}\n}\n\n/* Output:\nLooping through shapes for area ...\nShape details:  {5 3}\nArea of this shape is:  15\nShape details:  &{5}\nArea of this shape is:  25\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_11/methodset2.go",
    "content": "// methodset2.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\ntype List []int\n\nfunc (l List) Len() int        { return len(l) }\nfunc (l *List) Append(val int) { *l = append(*l, val) }\n\ntype Appender interface {\n\tAppend(int)\n}\n\nfunc CountInto(a Appender, start, end int) {\n\tfor i := start; i <= end; i++ {\n\t\ta.Append(i)\n\t}\n}\n\ntype Lener interface {\n\tLen() int\n}\n\nfunc LongEnough(l Lener) bool {\n\treturn l.Len()*10 > 42\n}\n\nfunc main() {\n\t// A bare value\n\tvar lst List\n\t// compiler error:\n\t// cannot use lst (type List) as type Appender in function argument:\n\t// List does not implement Appender (Append method requires pointer receiver)\n\t// CountInto(lst, 1, 10) // INVALID: Append has a pointer receiver\n\n\tif LongEnough(lst) { // VALID: Identical receiver type\n\t\tfmt.Printf(\" - lst is long enough\")\n\t}\n\n\t// A pointer value\n\tplst := new(List)\n\tCountInto(plst, 1, 10) // VALID: Identical receiver type\n\tif LongEnough(plst) {  // VALID: a *List can be dereferenced for the receiver\n\t\tfmt.Printf(\" - plst is long enough\") //  - plst is long enoug\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_11/multi_interfaces_poly.go",
    "content": "//multi_interfaces_poly.go\npackage main\n\nimport \"fmt\"\n\ntype Shaper interface {\n\tArea() float32\n}\n\ntype TopologicalGenus interface {\n\tRank() int\n}\n\ntype Square struct {\n\tside float32\n}\n\nfunc (sq *Square) Area() float32 {\n\treturn sq.side * sq.side\n}\n\nfunc (sq *Square) Rank() int {\n\treturn 1\n}\n\ntype Rectangle struct {\n\tlength, width float32\n}\n\nfunc (r Rectangle) Area() float32 {\n\treturn r.length * r.width\n}\n\nfunc (r Rectangle) Rank() int {\n\treturn 2\n}\n\nfunc main() {\n\tr := Rectangle{5, 3} // Area() of Rectangle needs a value\n\tq := &Square{5}      // Area() of Square needs a pointer\n\tshapes := []Shaper{r, q}\n\tfmt.Println(\"Looping through shapes for area ...\")\n\tfor n := range shapes {\n\t\tfmt.Println(\"Shape details: \", shapes[n])\n\t\tfmt.Println(\"Area of this shape is: \", shapes[n].Area())\n\t}\n\ttopgen := []TopologicalGenus{r, q}\n\tfmt.Println(\"Looping through topgen for rank ...\")\n\tfor n := range topgen {\n\t\tfmt.Println(\"Shape details: \", topgen[n])\n\t\tfmt.Println(\"Topological Genus of this shape is: \", topgen[n].Rank())\n\t}\n}\n\n/* Output:\nLooping through shapes for area ...\nShape details:  {5 3}\nArea of this shape is:  15\nShape details:  &{5}\nArea of this shape is:  25\nLooping through topgen for rank ...\nShape details:  {5 3}\nTopological Genus of this shape is:  2\nShape details:  &{5}\nTopological Genus of this shape is:  1\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_11/node_structures.go",
    "content": "// node_structures.go\npackage main\n\nimport \"fmt\"\n\ntype Node struct {\n\tle   *Node\n\tdata interface{}\n\tri   *Node\n}\n\nfunc NewNode(left, right *Node) *Node {\n\treturn &Node{left, nil, right}\n}\n\nfunc (n *Node) SetData(data interface{}) {\n\tn.data = data\n}\n\nfunc main() {\n\troot := NewNode(nil, nil)\n\troot.SetData(\"root node\")\n\t// make child (leaf) nodes:\n\ta := NewNode(nil, nil)\n\ta.SetData(\"left node\")\n\tb := NewNode(nil, nil)\n\tb.SetData(\"right node\")\n\troot.le = a\n\troot.ri = b\n\tfmt.Printf(\"%v\\n\", root) // Output: &{0x125275f0 root node 0x125275e0}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_11/print.go",
    "content": "// print.go\npackage main\n\nimport (\n\t\"os\"\n\t\"strconv\"\n)\n\ntype Stringer interface {\n\tString() string\n}\n\ntype Celsius float64\n\nfunc (c Celsius) String() string {\n\treturn strconv.FormatFloat(float64(c), 'f', 1, 64) + \" °C\"\n}\n\ntype Day int\n\nvar dayName = []string{\"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\", \"Sunday\"}\n\nfunc (day Day) String() string {\n\treturn dayName[day]\n}\n\nfunc print(args ...interface{}) {\n\tfor i, arg := range args {\n\t\tif i > 0 {\n\t\t\tos.Stdout.WriteString(\" \")\n\t\t}\n\t\tswitch a := arg.(type) { // type switch\n\t\tcase Stringer:\n\t\t\tos.Stdout.WriteString(a.String())\n\t\tcase int:\n\t\t\tos.Stdout.WriteString(strconv.Itoa(a))\n\t\tcase string:\n\t\t\tos.Stdout.WriteString(a)\n\t\t// more types\n\t\tdefault:\n\t\t\tos.Stdout.WriteString(\"???\")\n\t\t}\n\t}\n}\n\nfunc main() {\n\tprint(Day(1), \"was\", Celsius(18.36)) // Tuesday was 18.4 °C\n}\n\n// Tuesday was 18.4 °C\n"
  },
  {
    "path": "eBook/examples/chapter_11/reflect1.go",
    "content": "// reflect1.go\n// blog: Laws of Reflection\npackage main\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\nfunc main() {\n\tvar x float64 = 3.4\n\tfmt.Println(\"type:\", reflect.TypeOf(x))\n\tv := reflect.ValueOf(x)\n\tfmt.Println(\"value:\", v)\n\tfmt.Println(\"type:\", v.Type())\n\tfmt.Println(\"kind:\", v.Kind())\n\tfmt.Println(\"value:\", v.Float())\n\tfmt.Println(v.Interface())\n\tfmt.Printf(\"value is %5.2e\\n\", v.Interface())\n\ty := v.Interface().(float64)\n\tfmt.Println(y)\n}\n\n/* output:\ntype: float64\nvalue: <float64 Value>\ntype: float64\nkind: float64\nvalue: 3.4\n3.4\nvalue is 3.40e+00\n3.4\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_11/reflect2.go",
    "content": "// reflect2.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\nfunc main() {\n\tvar x float64 = 3.4\n\tv := reflect.ValueOf(x)\n\t// setting a value:\n\t// v.SetFloat(3.1415) // Error: will panic: reflect.Value.SetFloat using unaddressable value\n\tfmt.Println(\"settability of v:\", v.CanSet())\n\tv = reflect.ValueOf(&x) // Note: take the address of x.\n\tfmt.Println(\"type of v:\", v.Type())\n\tfmt.Println(\"settability of v:\", v.CanSet())\n\tv = v.Elem()\n\tfmt.Println(\"The Elem of v is: \", v)\n\tfmt.Println(\"settability of v:\", v.CanSet())\n\tv.SetFloat(3.1415) // this works!\n\tfmt.Println(v.Interface())\n\tfmt.Println(v)\n}\n\n/* Output:\nsettability of v: false\ntype of v: *float64\nsettability of v: false\nThe Elem of v is:  <float64 Value>\nsettability of v: true\n3.1415\n<float64 Value>\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_11/reflect_struct.go",
    "content": "// reflect.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\ntype NotknownType struct {\n\ts1, s2, s3 string\n}\n\nfunc (n NotknownType) String() string {\n\treturn n.s1 + \" - \" + n.s2 + \" - \" + n.s3\n}\n\n// variable to investigate:\nvar secret interface{} = NotknownType{\"Ada\", \"Go\", \"Oberon\"}\n\nfunc main() {\n\tvalue := reflect.ValueOf(secret) // <main.NotknownType Value>\n\ttyp := reflect.TypeOf(secret)    // main.NotknownType\n\t// alternative:\n\t//typ := value.Type()  // main.NotknownType\n\tfmt.Println(typ)\n\tknd := value.Kind() // struct\n\tfmt.Println(knd)\n\n\t// iterate through the fields of the struct:\n\tfor i := 0; i < value.NumField(); i++ {\n\t\tfmt.Printf(\"Field %d: %v\\n\", i, value.Field(i))\n\t\t// error: panic: reflect.Value.SetString using value obtained using unexported field\n\t\t//value.Field(i).SetString(\"C#\")\n\t}\n\n\t// call the first method, which is String():\n\tresults := value.Method(0).Call(nil)\n\tfmt.Println(results) // [Ada - Go - Oberon]\n}\n\n/* Output:\nmain.NotknownType\nstruct\nField 0: Ada\nField 1: Go\nField 2: Oberon\n[Ada - Go - Oberon]\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_11/reflect_struct2.go",
    "content": "// reflect_struct2.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\ntype T struct {\n\tA int\n\tB string\n}\n\nfunc main() {\n\tt := T{23, \"skidoo\"}\n\ts := reflect.ValueOf(&t).Elem()\n\ttypeOfT := s.Type()\n\tfor i := 0; i < s.NumField(); i++ {\n\t\tf := s.Field(i)\n\t\tfmt.Printf(\"%d: %s %s = %v\\n\", i,\n\t\t\ttypeOfT.Field(i).Name, f.Type(), f.Interface())\n\t}\n\ts.Field(0).SetInt(77)\n\ts.Field(1).SetString(\"Sunset Strip\")\n\tfmt.Println(\"t is now\", t)\n}\n\n/* Output:\n0: A int = 23\n1: B string = skidoo\nt is now {77 Sunset Strip}\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_11/sort/sort.go",
    "content": "// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n// Sorting using a general interface\npackage sort\n\n// Sorting interface\ntype Interface interface {\n\tLen() int\n\tLess(i, j int) bool\n\tSwap(i, j int)\n}\n\n// General sort function\nfunc Sort(data Interface) {\n\tfor i := 1; i < data.Len(); i++ {\n\t\tfor j := i; j > 0 && data.Less(j, j-1); j-- {\n\t\t\tdata.Swap(j, j-1)\n\t\t}\n\t}\n}\n\n// Test if data is sorted\nfunc IsSorted(data Interface) bool {\n\tn := data.Len()\n\tfor i := n - 1; i > 0; i-- {\n\t\tif data.Less(i, i-1) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// Convenience types for common cases: IntArray\ntype IntArray []int\n\nfunc (p IntArray) Len() int           { return len(p) }\nfunc (p IntArray) Less(i, j int) bool { return p[i] < p[j] }\nfunc (p IntArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }\n\ntype Float64Array []float64\n\nfunc (p Float64Array) Len() int           { return len(p) }\nfunc (p Float64Array) Less(i, j int) bool { return p[i] < p[j] }\nfunc (p Float64Array) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }\n\ntype StringArray []string\n\nfunc (p StringArray) Len() int           { return len(p) }\nfunc (p StringArray) Less(i, j int) bool { return p[i] < p[j] }\nfunc (p StringArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }\n\n// Convenience wrappers for common cases\nfunc SortInts(a []int)         { Sort(IntArray(a)) }\nfunc SortFloat64s(a []float64) { Sort(Float64Array(a)) }\nfunc SortStrings(a []string)   { Sort(StringArray(a)) }\n\nfunc IntsAreSorted(a []int) bool         { return IsSorted(IntArray(a)) }\nfunc Float64sAreSorted(a []float64) bool { return IsSorted(Float64Array(a)) }\nfunc StringsAreSorted(a []string) bool   { return IsSorted(StringArray(a)) }\n"
  },
  {
    "path": "eBook/examples/chapter_11/sortmain.go",
    "content": "// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// This package gives an example of how to use a custom package with interfaces\npackage main\n\nimport (\n\t\"./sort\"\n\t\"fmt\"\n)\n\n// sorting of slice of integers\nfunc ints() {\n\tdata := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}\n\ta := sort.IntArray(data) //conversion to type IntArray\n\tsort.Sort(a)\n\tif !sort.IsSorted(a) {\n\t\tpanic(\"fail\")\n\t}\n\tfmt.Printf(\"The sorted array is: %v\\n\", a)\n}\n\n// sorting of slice of strings\nfunc strings() {\n\tdata := []string{\"monday\", \"friday\", \"tuesday\", \"wednesday\", \"sunday\", \"thursday\", \"\", \"saturday\"}\n\ta := sort.StringArray(data)\n\tsort.Sort(a)\n\tif !sort.IsSorted(a) {\n\t\tpanic(\"fail\")\n\t}\n\tfmt.Printf(\"The sorted array is: %v\\n\", a)\n}\n\n// a type which describes a day of the week\ntype day struct {\n\tnum       int\n\tshortName string\n\tlongName  string\n}\n\ntype dayArray struct {\n\tdata []*day\n}\n\nfunc (p *dayArray) Len() int           { return len(p.data) }\nfunc (p *dayArray) Less(i, j int) bool { return p.data[i].num < p.data[j].num }\nfunc (p *dayArray) Swap(i, j int)      { p.data[i], p.data[j] = p.data[j], p.data[i] }\n\n// sorting of custom type day\nfunc days() {\n\tSunday := day{0, \"SUN\", \"Sunday\"}\n\tMonday := day{1, \"MON\", \"Monday\"}\n\tTuesday := day{2, \"TUE\", \"Tuesday\"}\n\tWednesday := day{3, \"WED\", \"Wednesday\"}\n\tThursday := day{4, \"THU\", \"Thursday\"}\n\tFriday := day{5, \"FRI\", \"Friday\"}\n\tSaturday := day{6, \"SAT\", \"Saturday\"}\n\tdata := []*day{&Tuesday, &Thursday, &Wednesday, &Sunday, &Monday, &Friday, &Saturday}\n\ta := dayArray{data}\n\tsort.Sort(&a)\n\tif !sort.IsSorted(&a) {\n\t\tpanic(\"fail\")\n\t}\n\tfor _, d := range data {\n\t\tfmt.Printf(\"%s \", d.longName)\n\t}\n\tfmt.Printf(\"\\n\")\n}\n\nfunc main() {\n\tints()\n\tstrings()\n\tdays()\n}\n\n/* Output:\nThe sorted array is: [-5467984 -784 0 0 42 59 74 238 905 959 7586 7586 9845]\nThe sorted array is: [ friday monday saturday sunday thursday tuesday wednesday]\nSunday Monday Tuesday Wednesday Thursday Friday Saturday\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_11/static.go",
    "content": "// static.go\npackage main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n)\n\nvar r io.Reader\n\nfunc main() {\n\tr = os.Stdin\n\tr = bufio.NewReader(r)\n\tr = new(bytes.Buffer)\n\tf, _ := os.Open(\"test.txt\")\n\tdefer f.Close()\n\tr = bufio.NewReader(f)\n\tvar s *bytes.Buffer = new(bytes.Buffer)\n\tr = s\n\tfmt.Println(s)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_11/tideland-cgl.googlecode.com/hg/.hg/branch",
    "content": "default\n"
  },
  {
    "path": "eBook/examples/chapter_11/tideland-cgl.googlecode.com/hg/.hg/branchheads.cache",
    "content": "0e0559350cc3c90b3a51dd98ac3ba577db027317 95\n0e0559350cc3c90b3a51dd98ac3ba577db027317 default\n"
  },
  {
    "path": "eBook/examples/chapter_11/tideland-cgl.googlecode.com/hg/.hg/hgrc",
    "content": "[paths]\ndefault = https://tideland-cgl.googlecode.com/hg\n"
  },
  {
    "path": "eBook/examples/chapter_11/tideland-cgl.googlecode.com/hg/.hg/requires",
    "content": "revlogv1\nstore\nfncache\ndotencode\n"
  },
  {
    "path": "eBook/examples/chapter_11/tideland-cgl.googlecode.com/hg/.hg/store/fncache",
    "content": "data/cgldoc/LICENSE.txt.i\ndata/cgl_test.go.i\ndata/cglfsm/cglfsm.go.i\ndata/cglnum/Makefile.i\ndata/cglfsm.go.i\ndata/cglsup.go.i\ndata/Makefile.i\ndata/cglctb/cglctb_test.go.i\ndata/cglmrp.go.i\ndata/cglsup/cglsup_test.go.i\ndata/cglutl/cglutluid.go.i\ndata/cglsml/cglsml.go.i\ndata/cglred/cglred.go.i\ndata/cglmon/Makefile.i\ndata/cglsup/cglsup.go.i\ndata/cglmon/cglmon_test.go.i\ndata/cglmon/cglmonssi.go.i\ndata/LICENSE.txt.i\ndata/cglmon/cglmon.go.i\ndata/cglutl/cglutlmrp.go.i\ndata/cglsrt.go.i\ndata/cglutl/cglutlsup.go.i\ndata/cglred/cglred_test.go.i\ndata/cgleca/cglecauep.go.i\ndata/.hgtags.i\ndata/cglnum/cglnum_test.go.i\ndata/cgleca/cglecaucb.go.i\ndata/cglsml/cglsmlpap.go.i\ndata/cglutl/cglutltim.go.i\ndata/cgleca/Makefile.i\ndata/cglred/Makefile.i\ndata/cglred/cglredres.go.i\ndata/cglmon/cglmonetm.go.i\ndata/cglutl/cglutl.go.i\ndata/cgleca/cglecautl.go.i\ndata/cglnum/cglnumsta.go.i\ndata/cglnum.go.i\ndata/cglsml/Makefile.i\ndata/cglctb/cglctb.go.i\ndata/cglnum/cglnumgra.go.i\ndata/cglutl/Makefile.i\ndata/cglctb.go.i\ndata/cglsmr.go.i\ndata/cglutl/cglutl_test.go.i\ndata/cglfsm/cglfsm_test.go.i\ndata/cgl.go.i\ndata/cglutl/cglutlrre.go.i\ndata/cglred/cglredcmd.go.i\ndata/cglutl/cglutlsrt.go.i\ndata/cglmon.go.i\ndata/cglctb/Makefile.i\ndata/cglfsm/Makefile.i\ndata/cglsml/cglsml_test.go.i\ndata/cgltim.go.i\ndata/cgleca/cgleca_test.go.i\ndata/cgleca/cgleca.go.i\ndata/cglnum/cglnum.go.i\ndata/cglsup/Makefile.i\ndata/cglred/cglredurp.go.i\ndata/cglsml.go.i\n"
  },
  {
    "path": "eBook/examples/chapter_11/tideland-cgl.googlecode.com/hg/.hg/tags.cache",
    "content": "95 0e0559350cc3c90b3a51dd98ac3ba577db027317 d11eb24f9c4089a5d83f315625407b7bf1811bb4\n\n23ee9664f914f12079d78fdb6e8d5745387b0394 release\n440721520a9cc613e0999c70c40e8e2bb7c70cfb release.2011-06-23\n5353419f1ba694ba7a425e6a6fcd45ea97884560 release.2011-07-28\n23ee9664f914f12079d78fdb6e8d5745387b0394 release.2011-08-02\n"
  },
  {
    "path": "eBook/examples/chapter_11/tideland-cgl.googlecode.com/hg/.hg/undo.branch",
    "content": "default"
  },
  {
    "path": "eBook/examples/chapter_11/tideland-cgl.googlecode.com/hg/.hg/undo.desc",
    "content": "0\npull\nhttps://tideland-cgl.googlecode.com/hg\n"
  },
  {
    "path": "eBook/examples/chapter_11/tideland-cgl.googlecode.com/hg/.hg/undo.dirstate",
    "content": ""
  },
  {
    "path": "eBook/examples/chapter_11/tideland-cgl.googlecode.com/hg/.hgtags",
    "content": "440721520a9cc613e0999c70c40e8e2bb7c70cfb release.2011-06-23\n440721520a9cc613e0999c70c40e8e2bb7c70cfb release\n5353419f1ba694ba7a425e6a6fcd45ea97884560 release.2011-07-28\n440721520a9cc613e0999c70c40e8e2bb7c70cfb release\n5353419f1ba694ba7a425e6a6fcd45ea97884560 release\n"
  },
  {
    "path": "eBook/examples/chapter_11/tideland-cgl.googlecode.com/hg/LICENSE.txt",
    "content": "Tideland Common Go Library\n\nRedistribution and use in source and binary forms, with or\nmodification, are permitted provided that the following conditions are\nmet:\n\nRedistributions of source code must retain the above copyright notice, this\nlist of conditions and the following disclaimer.\n\nRedistributions in binary form must reproduce the above copyright notice,\nthis list of conditions and the following disclaimer in the documentation\nand/or other materials provided with the distribution.\n\nNeither the name of Tideland nor the names of its contributors may be\nused to endorse or promote products derived from this software without\nspecific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\nTHE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "eBook/examples/chapter_11/tideland-cgl.googlecode.com/hg/Makefile",
    "content": "# Tideland Common Go Library\n#\n# Copyright 2009-2011 Frank Mueller / Oldenburg / Germany. \n#\n# All rights reserved. Use of this source code is \n# governed by a BSD-style license that can be found\n# in the LICENSE file.\n\ninclude $(GOROOT)/src/Make.inc\n\nTARG=tideland-cgl.googlecode.com/hg\nGOFMT=gofmt -w -tabindent -tabwidth=8\n\nGOFILES=\\\n\tcgl.go\\\n\tcglsup.go\\\n\tcglfsm.go\\\n\tcglmon.go\\\n\tcglsmr.go\\\n\tcgltim.go\\\n\tcglsml.go\\\n\ninclude $(GOROOT)/src/Make.pkg\n\nformat:\n\t${GOFMT} ${GOFILES}\n\t${GOFMT} cgl_test.go\n\n#\n# EOF\n#\n"
  },
  {
    "path": "eBook/examples/chapter_11/tideland-cgl.googlecode.com/hg/cgl.go",
    "content": "/*\n\tTideland Common Go Library\n\n\tCopyright (C) 2009-2011 Frank Mueller / Oldenburg / Germany\n\n\tRedistribution and use in source and binary forms, with or\n\tmodification, are permitted provided that the following conditions are\n\tmet:\n\n\tRedistributions of source code must retain the above copyright notice, this\n\tlist of conditions and the following disclaimer.\n\n\tRedistributions in binary form must reproduce the above copyright notice,\n\tthis list of conditions and the following disclaimer in the documentation\n\tand/or other materials provided with the distribution.\n\n\tNeither the name of Tideland nor the names of its contributors may be\n\tused to endorse or promote products derived from this software without\n\tspecific prior written permission.\n\n\tTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n\tAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n\tIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n\tARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n\tLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n\tCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n\tSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n\tINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n\tCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n\tARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\n\tTHE POSSIBILITY OF SUCH DAMAGE.\n*/\n\n// The package 'cgl' contains a lot of helpful stuff for many kinds of software. Those\n// are a map/reduce, sorting, time handling, UUIDs, lazy evaluation, chronological jobs,\n// state machines, monitoring, suervision, a simple markup language and much more.\npackage cgl\n\n//--------------------\n// IMPORTS\n//--------------------\n\nimport (\n\t\"bytes\"\n\t\"crypto/rand\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"strings\"\n\t\"unicode\"\n)\n\n//--------------------\n// CONST\n//--------------------\n\nconst RELEASE = \"Tideland Common Go Library Release 2011-08-02\"\n\n//--------------------\n// DEBUGGING\n//--------------------\n\n// Debug prints a debug information to the log with file and line.\nfunc Debug(format string, a ...interface{}) {\n\t_, file, line, _ := runtime.Caller(1)\n\tinfo := fmt.Sprintf(format, a...)\n\n\tlog.Printf(\"[cgl] debug %s:%d %v\", file, line, info)\n}\n\n//--------------------\n// UUID\n//--------------------\n\n// UUID represent a universal identifier with 16 bytes.\ntype UUID []byte\n\n// NewUUID generates a new UUID based on version 4.\nfunc NewUUID() UUID {\n\tuuid := make([]byte, 16)\n\n\t_, err := io.ReadFull(rand.Reader, uuid)\n\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Set version (4) and variant (2).\n\n\tvar version byte = 4 << 4\n\tvar variant byte = 2 << 4\n\n\tuuid[6] = version | (uuid[6] & 15)\n\tuuid[8] = variant | (uuid[8] & 15)\n\n\treturn uuid\n}\n\n// Raw returns a copy of the UUID bytes.\nfunc (uuid UUID) Raw() []byte {\n\traw := make([]byte, 16)\n\n\tcopy(raw, uuid[0:16])\n\n\treturn raw\n}\n\n// String returns a hexadecimal string representation with\n// standardized separators.\nfunc (uuid UUID) String() string {\n\tbase := hex.EncodeToString(uuid.Raw())\n\n\treturn base[0:8] + \"-\" + base[8:12] + \"-\" + base[12:16] + \"-\" + base[16:20] + \"-\" + base[20:32]\n}\n\n//--------------------\n// MORE ID FUNCTIONS\n//--------------------\n\n// LimitedSepIdentifier builds an identifier out of multiple parts,\n// all as lowercase strings and concatenated with the separator\n// Non letters and digits are exchanged with dashes and\n// reduced to a maximum of one each. If limit is true only\n// 'a' to 'z' and '0' to '9' are allowed.\nfunc LimitedSepIdentifier(sep string, limit bool, parts ...interface{}) string {\n\tiparts := make([]string, 0)\n\n\tfor _, p := range parts {\n\t\ttmp := strings.Map(func(r int) int {\n\t\t\t// Check letter and digit.\n\t\t\tif unicode.IsLetter(r) || unicode.IsDigit(r) {\n\t\t\t\tlcr := unicode.ToLower(r)\n\n\t\t\t\tif limit {\n\t\t\t\t\t// Only 'a' to 'z' and '0' to '9'.\n\t\t\t\t\tif lcr <= unicode.MaxASCII {\n\t\t\t\t\t\treturn lcr\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn ' '\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Every char is allowed.\n\t\t\t\t\treturn lcr\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn ' '\n\t\t}, fmt.Sprintf(\"%v\", p))\n\n\t\t// Only use non-empty identifier parts.\n\t\tif ipart := strings.Join(strings.Fields(tmp), \"-\"); len(ipart) > 0 {\n\t\t\tiparts = append(iparts, ipart)\n\t\t}\n\t}\n\n\treturn strings.Join(iparts, sep)\n}\n\n// SepIdentifier builds an identifier out of multiple parts, all\n// as lowercase strings and concatenated with the separator\n// Non letters and digits are exchanged with dashes and\n// reduced to a maximum of one each.\nfunc SepIdentifier(sep string, parts ...interface{}) string {\n\treturn LimitedSepIdentifier(sep, false, parts...)\n}\n\n// Identifier works like SepIdentifier but the seperator\n// is set to be a colon.\nfunc Identifier(parts ...interface{}) string {\n\treturn SepIdentifier(\":\", parts...)\n}\n\n// TypeAsIdentifierPart transforms the name of the arguments type into\n// a part for identifiers. It's splitted at each uppercase char,\n// concatenated with dashes and transferred to lowercase.\nfunc TypeAsIdentifierPart(i interface{}) string {\n\tvar buf bytes.Buffer\n\n\tfullTypeName := reflect.TypeOf(i).String()\n\tlastDot := strings.LastIndex(fullTypeName, \".\")\n\ttypeName := fullTypeName[lastDot+1:]\n\n\tfor i, r := range typeName {\n\t\tif unicode.IsUpper(r) {\n\t\t\tif i > 0 {\n\t\t\t\tbuf.WriteRune('-')\n\t\t\t}\n\t\t}\n\n\t\tbuf.WriteRune(r)\n\t}\n\n\treturn strings.ToLower(buf.String())\n}\n\n//--------------------\n// METHOD DISPATCHING\n//--------------------\n\n// Dispatch a string to a method of a type.\nfunc Dispatch(variable interface{}, name string, args ...interface{}) ([]interface{}, bool) {\n\tnumArgs := len(args)\n\tvalue := reflect.ValueOf(variable)\n\tvalueType := value.Type()\n\tnumMethods := valueType.NumMethod()\n\n\tfor i := 0; i < numMethods; i++ {\n\t\tmethod := valueType.Method(i)\n\n\t\tif (method.PkgPath == \"\") && (method.Type.NumIn() == numArgs+1) {\n\n\t\t\tif method.Name == name {\n\t\t\t\t// Prepare all args with variable and args.\n\n\t\t\t\tcallArgs := make([]reflect.Value, numArgs+1)\n\n\t\t\t\tcallArgs[0] = value\n\n\t\t\t\tfor i, a := range args {\n\t\t\t\t\tcallArgs[i+1] = reflect.ValueOf(a)\n\t\t\t\t}\n\n\t\t\t\t// Make the function call.\n\n\t\t\t\tresults := method.Func.Call(callArgs)\n\n\t\t\t\t// Transfer results into slice of interfaces.\n\n\t\t\t\tallResults := make([]interface{}, len(results))\n\n\t\t\t\tfor i, v := range results {\n\t\t\t\t\tallResults[i] = v.Interface()\n\t\t\t\t}\n\n\t\t\t\treturn allResults, true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil, false\n}\n\n//--------------------\n// LAZY EVALUATOR BUILDERS\n//--------------------\n\n// Function to evaluate.\ntype EvalFunc func(interface{}) (interface{}, interface{})\n\n// Generic builder for lazy evaluators.\nfunc BuildLazyEvaluator(evalFunc EvalFunc, initState interface{}) func() interface{} {\n\tretValChan := make(chan interface{})\n\n\tloopFunc := func() {\n\t\tvar actState interface{} = initState\n\t\tvar retVal interface{}\n\n\t\tfor {\n\t\t\tretVal, actState = evalFunc(actState)\n\n\t\t\tretValChan <- retVal\n\t\t}\n\t}\n\n\tretFunc := func() interface{} {\n\t\treturn <-retValChan\n\t}\n\n\tgo loopFunc()\n\n\treturn retFunc\n}\n\n// Builder for lazy evaluators with ints as result.\nfunc BuildLazyIntEvaluator(evalFunc EvalFunc, initState interface{}) func() int {\n\tef := BuildLazyEvaluator(evalFunc, initState)\n\n\treturn func() int {\n\t\treturn ef().(int)\n\t}\n}\n\n/*\n\tEOF\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_11/tideland-cgl.googlecode.com/hg/cgl_test.go",
    "content": "/*\n\tTideland Common Go Library - Unit Tests\n\n\tCopyright (C) 2009-2011 Frank Mueller / Oldenburg / Germany\n\n\tRedistribution and use in source and binary forms, with or\n\tmodification, are permitted provided that the following conditions are\n\tmet:\n\n\tRedistributions of source code must retain the above copyright notice, this\n\tlist of conditions and the following disclaimer.\n\n\tRedistributions in binary form must reproduce the above copyright notice,\n\tthis list of conditions and the following disclaimer in the documentation\n\tand/or other materials provided with the distribution.\n\n\tNeither the name of Tideland nor the names of its contributors may be\n\tused to endorse or promote products derived from this software without\n\tspecific prior written permission.\n\n\tTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n\tAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n\tIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n\tARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n\tLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n\tCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n\tSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n\tINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n\tCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n\tARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\n\tTHE POSSIBILITY OF SUCH DAMAGE.\n*/\n\npackage cgl\n\n//--------------------\n// IMPORTS\n//--------------------\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"log\"\n\t\"rand\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\n//--------------------\n// TESTS\n//--------------------\n\n// Test single recovering.\nfunc TestSingleRecovering(t *testing.T) {\n\ts := NewSupervisor(nil)\n\tra := NewRecoverableAction(s)\n\n\tpos := ra.Action(PositiveAction)\n\n\tif pos == \"OK\" {\n\t\tt.Logf(\"Single recovering (a) OK.\")\n\t} else {\n\t\tt.Errorf(\"Single recovering (a) failed! Reply is '%v'.\", pos)\n\t}\n\n\tneg := ra.Action(FailAction)\n\n\tif neg == \"Recovered\" {\n\t\tt.Logf(\"Single recovering (b) OK.\")\n\t} else {\n\t\tt.Errorf(\"Single recovering (b) failed! Reply is '%v.\", neg)\n\t}\n}\n\n// Test multiple recovering.\nfunc TestMultipleRecovering(t *testing.T) {\n\ts := NewSupervisor(nil)\n\traa := NewRecoverableAction(s)\n\trab := NewRecoverableAction(s)\n\trac := NewRecoverableAction(s)\n\n\ts.AddRecoverable(\"A\", raa)\n\ts.AddRecoverable(\"B\", rab)\n\ts.AddRecoverable(\"C\", rac)\n\n\tt.Logf(\"(A) is '%v'.\", raa.Action(FailAction))\n\tt.Logf(\"(B) is '%v'.\", rab.Action(PositiveAction))\n\tt.Logf(\"(C) is '%v'.\", rac.Action(PositiveAction))\n}\n\n// Test single heartbeat timeout.\nfunc TestSingleHeartbeatTimeout(t *testing.T) {\n\tra := NewRecoverableAction(nil)\n\treply := ra.Action(TimeConsumingAction)\n\n\tif reply == \"Recovered\" {\n\t\tt.Logf(\"Heartbeat timeout recovering OK.\")\n\t} else {\n\t\tt.Errorf(\"Heartbeat timeout recovering failed! Reply is '%v.\", reply)\n\t}\n}\n\n// Test multiple heartbeat timeout.\nfunc TestMultipleHeartbeatTimeout(t *testing.T) {\n\ts := NewSupervisor(nil)\n\traa := NewRecoverableAction(s)\n\trab := NewRecoverableAction(s)\n\trac := NewRecoverableAction(s)\n\n\ts.AddRecoverable(\"A\", raa)\n\ts.AddRecoverable(\"B\", rab)\n\ts.AddRecoverable(\"C\", rac)\n\n\tt.Logf(\"(A) is '%v'.\", raa.Action(TimeConsumingAction))\n\tt.Logf(\"(B) is '%v'.\", rab.Action(PositiveAction))\n\tt.Logf(\"(C) is '%v'.\", rac.Action(PositiveAction))\n}\n\n// Test the finite state machine successfully.\nfunc TestFsmSuccess(t *testing.T) {\n\tfsm := NewFSM(NewLoginHandler(), -1)\n\n\tfsm.Send(&LoginPayload{\"yadda\"})\n\tfsm.Send(&PreparePayload{\"foo\", \"bar\"})\n\tfsm.Send(&LoginPayload{\"yaddaA\"})\n\tfsm.Send(&LoginPayload{\"yaddaB\"})\n\tfsm.Send(&LoginPayload{\"yaddaC\"})\n\tfsm.Send(&LoginPayload{\"yaddaD\"})\n\tfsm.Send(&UnlockPayload{})\n\tfsm.Send(&LoginPayload{\"bar\"})\n\n\ttime.Sleep(1e7)\n\n\tt.Logf(\"Status: '%v'.\", fsm.State())\n}\n\n// Test the finite state machine with timeout.\nfunc TestFsmTimeout(t *testing.T) {\n\tfsm := NewFSM(NewLoginHandler(), 1e5)\n\n\tfsm.Send(&LoginPayload{\"yadda\"})\n\tfsm.Send(&PreparePayload{\"foo\", \"bar\"})\n\tfsm.Send(&LoginPayload{\"yaddaA\"})\n\tfsm.Send(&LoginPayload{\"yaddaB\"})\n\n\ttime.Sleep(1e8)\n\n\tfsm.Send(&LoginPayload{\"yaddaC\"})\n\tfsm.Send(&LoginPayload{\"yaddaD\"})\n\tfsm.Send(&UnlockPayload{})\n\tfsm.Send(&LoginPayload{\"bar\"})\n\n\ttime.Sleep(1e7)\n\n\tt.Logf(\"Status: '%v'.\", fsm.State())\n}\n\n// Test dispatching.\nfunc TestDispatching(t *testing.T) {\n\ttt := new(TT)\n\n\tv1, ok1 := Dispatch(tt, \"Add\", 4, 5)\n\tv2, ok2 := Dispatch(tt, \"Add\", 4, 5, 6)\n\tv3, ok3 := Dispatch(tt, \"Mul\", 4, 5, 6)\n\tv4, ok4 := Dispatch(tt, \"Mul\", 4, 5, 6, 7, 8)\n\n\tt.Logf(\"Add 1: %v / %v\\n\", v1, ok1)\n\tt.Logf(\"Add 2: %v / %v\\n\", v2, ok2)\n\tt.Logf(\"Mul 1: %v / %v\\n\", v3, ok3)\n\tt.Logf(\"Mul 2: %v / %v\\n\", v4, ok4)\n}\n\n// Test debug statement.\nfunc TestDebug(t *testing.T) {\n\tDebug(\"Hello, I'm debugging %v!\", \"here\")\n}\n\n// Test nanoseconds calculation.\nfunc TestNanoseconds(t *testing.T) {\n\tt.Logf(\"Microseconds: %v\\n\", NsMicroseconds(4711))\n\tt.Logf(\"Milliseconds: %v\\n\", NsMilliseconds(4711))\n\tt.Logf(\"Seconds     : %v\\n\", NsSeconds(4711))\n\tt.Logf(\"Minutes     : %v\\n\", NsMinutes(4711))\n\tt.Logf(\"Hours       : %v\\n\", NsHours(4711))\n\tt.Logf(\"Days        : %v\\n\", NsDays(4711))\n\tt.Logf(\"Weeks       : %v\\n\", NsWeeks(4711))\n}\n\n// Test time containments.\nfunc TestTimeContainments(t *testing.T) {\n\tnow := time.UTC()\n\tyears := []int64{2008, 2009, 2010}\n\tmonths := []int{3, 6, 9, 12}\n\tdays := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}\n\thours := []int{20, 21, 22, 23}\n\tminutes := []int{5, 10, 15, 20, 25, 30}\n\tseconds := []int{0, 15, 30, 45}\n\tweekdays := []int{time.Saturday, time.Sunday}\n\n\tt.Logf(\"Time is %s\\n\", now.Format(time.RFC822))\n\tt.Logf(\"Year in list    : %t\\n\", YearInList(now, years))\n\tt.Logf(\"Year in range   : %t\\n\", YearInRange(now, 2000, 2005))\n\tt.Logf(\"Month in list   : %t\\n\", MonthInList(now, months))\n\tt.Logf(\"Month in range  : %t\\n\", MonthInRange(now, 1, 6))\n\tt.Logf(\"Day in list     : %t\\n\", DayInList(now, days))\n\tt.Logf(\"Day in range    : %t\\n\", DayInRange(now, 15, 25))\n\tt.Logf(\"Hour in list    : %t\\n\", HourInList(now, hours))\n\tt.Logf(\"Hour in range   : %t\\n\", HourInRange(now, 9, 17))\n\tt.Logf(\"Minute in list  : %t\\n\", MinuteInList(now, minutes))\n\tt.Logf(\"Minute in range : %t\\n\", MinuteInRange(now, 0, 29))\n\tt.Logf(\"Second in list  : %t\\n\", SecondInList(now, seconds))\n\tt.Logf(\"Second in range : %t\\n\", SecondInRange(now, 30, 59))\n\tt.Logf(\"Weekday in list : %t\\n\", WeekdayInList(now, weekdays))\n\tt.Logf(\"Weekday in range: %t\\n\", WeekdayInRange(now, time.Monday, time.Friday))\n}\n\n// Test the UUID.\nfunc TestUuid(t *testing.T) {\n\tuuids := make(map[string]bool)\n\n\tt.Logf(\"Start generating UUIDs ...\")\n\n\tfor i := 0; i < 1000000; i++ {\n\t\tuuid := NewUUID().String()\n\n\t\tif uuids[uuid] {\n\t\t\tt.Fatalf(\"UUID collision\")\n\t\t}\n\n\t\tuuids[uuid] = true\n\t}\n\n\tt.Logf(\"Done generating UUIDs!\")\n}\n\n// Test the creation of an identifier.\nfunc TestIdentifier(t *testing.T) {\n\t// Type as identifier.\n\tvar kvlf KeyValueLessFunc\n\n\tidp := TypeAsIdentifierPart(kvlf)\n\n\tif idp != \"key-value-less-func\" {\n\t\tt.Errorf(\"Identifier part for KeyValueLessFunc is wrong, returned '%v'!\", idp)\n\t}\n\n\tidp = TypeAsIdentifierPart(NewUUID())\n\n\tif idp != \"u-u-i-d\" {\n\t\tt.Errorf(\"Identifier part for UUID is wrong, returned '%v'!\", idp)\n\t}\n\n\t// Identifier.\n\tid := Identifier(\"One\", 2, \"three four\")\n\n\tif id != \"one:2:three-four\" {\n\t\tt.Errorf(\"First identifier is wrong! Id: %v\", id)\n\t}\n\n\tid = Identifier(2011, 6, 22, \"One, two, or  three things.\")\n\n\tif id != \"2011:6:22:one-two-or-three-things\" {\n\t\tt.Errorf(\"Second identifier is wrong! Id: %v\", id)\n\t}\n\n\tid = SepIdentifier(\"+\", 1, \"oNe\", 2, \"TWO\", \"3\", \"ÄÖÜ\")\n\n\tif id != \"1+one+2+two+3+äöü\" {\n\t\tt.Errorf(\"Third identifier is wrong! Id: %v\", id)\n\t}\n\n\tid = LimitedSepIdentifier(\"+\", true, \"     \", 1, \"oNe\", 2, \"TWO\", \"3\", \"ÄÖÜ\", \"Four\", \"+#-:,\")\n\n\tif id != \"1+one+2+two+3+four\" {\n\t\tt.Errorf(\"Fourth identifier is wrong! Id: %v\", id)\n\t}\n}\n\n// Test the integer generator.\nfunc TestLazyIntEvaluator(t *testing.T) {\n\tfibFunc := func(s interface{}) (interface{}, interface{}) {\n\t\tos := s.([]int)\n\t\tv1 := os[0]\n\t\tv2 := os[1]\n\t\tns := []int{v2, v1 + v2}\n\n\t\treturn v1, ns\n\t}\n\n\tfib := BuildLazyIntEvaluator(fibFunc, []int{0, 1})\n\n\tvar fibs [25]int\n\n\tfor i := 0; i < 25; i++ {\n\t\tfibs[i] = fib()\n\t}\n\n\tt.Logf(\"FIBS: %v\", fibs)\n}\n\n// Test pivot.\nfunc TestPivot(t *testing.T) {\n\ta := make(sort.IntSlice, 15)\n\n\tfor i := 0; i < len(a); i++ {\n\t\ta[i] = rand.Intn(99)\n\t}\n\n\tplo, phi := partition(a, 0, len(a)-1)\n\n\tt.Logf(\"PLO  : %v\", plo)\n\tt.Logf(\"PHI  : %v\", phi)\n\tt.Logf(\"PDATA: %v\", a[phi-1])\n\tt.Logf(\"PIVOT: %v\", a)\n}\n\n// Test sort shootout.\nfunc TestSort(t *testing.T) {\n\tola := generateTestOrdersList(25000)\n\tolb := generateTestOrdersList(25000)\n\tolc := generateTestOrdersList(25000)\n\told := generateTestOrdersList(25000)\n\n\tta := time.Nanoseconds()\n\tSort(ola)\n\ttb := time.Nanoseconds()\n\tsort.Sort(olb)\n\ttc := time.Nanoseconds()\n\tinsertionSort(olc, 0, len(olc)-1)\n\ttd := time.Nanoseconds()\n\tsequentialQuickSort(old, 0, len(olc)-1)\n\tte := time.Nanoseconds()\n\n\tt.Logf(\"PQS: %v\", tb-ta)\n\tt.Logf(\" QS: %v\", tc-tb)\n\tt.Logf(\" IS: %v\", td-tc)\n\tt.Logf(\"SQS: %v\", te-td)\n}\n\n// Test the parallel quicksort function.\nfunc TestParallelQuickSort(t *testing.T) {\n\tol := generateTestOrdersList(10000)\n\n\tSort(ol)\n\n\tcn := 0\n\n\tfor _, o := range ol {\n\t\tif cn > o.CustomerNo {\n\t\t\tt.Errorf(\"Customer No %v in wrong order!\", o.CustomerNo)\n\n\t\t\tcn = o.CustomerNo\n\t\t} else {\n\t\t\tcn = o.CustomerNo\n\t\t}\n\t}\n}\n\n// Test the MapReduce function.\nfunc TestMapReduce(t *testing.T) {\n\t// Start data producer.\n\n\torderChan := generateTestOrders(2000)\n\n\t// Define map and reduce functions.\n\n\tmapFunc := func(in *KeyValue, mapEmitChan KeyValueChan) {\n\t\to := in.Value.(*Order)\n\n\t\t// Emit analysis data for each item.\n\n\t\tfor _, i := range o.Items {\n\t\t\tunitDiscount := (i.UnitPrice / 100.0) * i.DiscountPerc\n\t\t\ttotalDiscount := unitDiscount * float64(i.Count)\n\t\t\ttotalAmount := (i.UnitPrice - unitDiscount) * float64(i.Count)\n\t\t\tanalysis := &OrderItemAnalysis{i.ArticleNo, i.Count, totalAmount, totalDiscount}\n\t\t\tarticleNo := strconv.Itoa(i.ArticleNo)\n\n\t\t\tmapEmitChan <- &KeyValue{articleNo, analysis}\n\t\t}\n\t}\n\n\treduceFunc := func(inChan KeyValueChan, reduceEmitChan KeyValueChan) {\n\t\tmemory := make(map[string]*OrderItemAnalysis)\n\n\t\t// Collect emitted analysis data.\n\n\t\tfor kv := range inChan {\n\t\t\tanalysis := kv.Value.(*OrderItemAnalysis)\n\n\t\t\tif existing, ok := memory[kv.Key]; ok {\n\t\t\t\texisting.Quantity += analysis.Quantity\n\t\t\t\texisting.Amount += analysis.Amount\n\t\t\t\texisting.Discount += analysis.Discount\n\t\t\t} else {\n\t\t\t\tmemory[kv.Key] = analysis\n\t\t\t}\n\t\t}\n\n\t\t// Emit it to map/reduce caller.\n\n\t\tfor articleNo, analysis := range memory {\n\t\t\treduceEmitChan <- &KeyValue{articleNo, analysis}\n\t\t}\n\t}\n\n\t// Now call MapReduce.\n\n\tfor result := range SortedMapReduce(orderChan, mapFunc, 100, reduceFunc, 20, KeyLessFunc) {\n\t\tt.Logf(\"%v\\n\", result.Value)\n\t}\n}\n\n// Test job.\nfunc TestJob(t *testing.T) {\n\t// Check function.\n\n\tcf := func(now *time.Time) (perform, delete bool) {\n\t\tperform = now.Day == 1 &&\n\t\t\tnow.Hour == 22 &&\n\t\t\tnow.Minute == 0 &&\n\t\t\tSecondInList(now, []int{0, 10, 20, 30, 40, 50})\n\t\tdelete = false\n\n\t\treturn perform, delete\n\t}\n\n\t// Task function.\n\n\ttf := func(id string) { t.Logf(\"Performed job %s\\n\", id) }\n\n\t// Job and time.\n\n\tjob := NewJob(\"test-job-a\", cf, tf)\n\ttime := time.LocalTime()\n\n\t// Test with non-matching time.\n\n\ttime.Second = 1\n\n\tjob.checkAndPerform(time)\n\n\t// Test with matching time\n\n\ttime.Day = 1\n\ttime.Hour = 22\n\ttime.Minute = 0\n\ttime.Second = 0\n\n\tjob.checkAndPerform(time)\n}\n\n// Test crontab keeping the job.\nfunc TestCrontabKeep(t *testing.T) {\n\tctb := NewCrontab()\n\tjob := createJob(t, \"keep\", false)\n\n\tctb.AddJob(job)\n\ttime.Sleep(10 * 1e9)\n\tctb.Stop()\n}\n\n// Test crontab deleting the job.\nfunc TestCrontabDelete(t *testing.T) {\n\tctb := NewCrontab()\n\tjob := createJob(t, \"delete\", true)\n\n\tctb.AddJob(job)\n\ttime.Sleep(10 * 1e9)\n\tctb.Stop()\n}\n\n// Test creating.\nfunc TestSmlCreating(t *testing.T) {\n\troot := createSmlStructure()\n\n\tt.Logf(\"Root: %v\", root)\n}\n\n// Test SML writer processing.\nfunc TestSmlWriterProcessing(t *testing.T) {\n\troot := createSmlStructure()\n\tbufA := bytes.NewBufferString(\"\")\n\tbufB := bytes.NewBufferString(\"\")\n\tsppA := NewSmlWriterProcessor(bufA, true)\n\tsppB := NewSmlWriterProcessor(bufB, false)\n\n\troot.ProcessWith(sppA)\n\troot.ProcessWith(sppB)\n\n\tt.Logf(\"Print A: %v\", bufA)\n\tt.Logf(\"Print B: %v\", bufB)\n}\n\n// Test positive reading.\nfunc TestSmlPositiveReading(t *testing.T) {\n\tsml := \"Before!   {foo {bar:1:first Yadda ^{Test^} 1}  {inbetween}  {bar:2:last Yadda {Test ^^} 2}}   After!\"\n\treader := NewSmlReader(strings.NewReader(sml))\n\n\troot, err := reader.RootTagNode()\n\n\tif err == nil {\n\t\tt.Logf(\"Root:%v\", root)\n\t} else {\n\t\tt.Errorf(\"Error: %v\", err)\n\t}\n}\n\n// Test negative reading.\nfunc TestSmlNegativeReading(t *testing.T) {\n\tsml := \"{Foo {bar:1 Yadda {test} {} 1} {bar:2 Yadda 2}}\"\n\treader := NewSmlReader(strings.NewReader(sml))\n\n\troot, err := reader.RootTagNode()\n\n\tif err == nil {\n\t\tt.Errorf(\"Root: %v\", root)\n\t} else {\n\t\tt.Logf(\"Error: %v\", err)\n\t}\n}\n\n// Test of the ETM monitor.\nfunc TestEtmMonitor(t *testing.T) {\n\tmon := Monitor()\n\n\t// Generate measurings.\n\tfor i := 0; i < 500; i++ {\n\t\tn := rand.Intn(10)\n\t\tid := fmt.Sprintf(\"Work %d\", n)\n\t\tm := mon.BeginMeasuring(id)\n\n\t\twork(n * 5000)\n\n\t\tm.EndMeasuring()\n\t}\n\n\t// Print, process with error, and print again.\n\tmon.MeasuringPointsPrintAll()\n\n\tmon.MeasuringPointsDo(func(mp *MeasuringPoint) {\n\t\tif mp.Count >= 25 {\n\t\t\t// Divide by zero.\n\t\t\tmp.Count = mp.Count / (mp.Count - mp.Count)\n\t\t}\n\t})\n\n\tmon.MeasuringPointsPrintAll()\n}\n\n// Test of the SSI monitor.\nfunc TestSsiMonitor(t *testing.T) {\n\tmon := Monitor()\n\n\t// Generate values.\n\tfor i := 0; i < 500; i++ {\n\t\tn := rand.Intn(10)\n\t\tid := fmt.Sprintf(\"Work %d\", n)\n\n\t\tmon.SetValue(id, rand.Int63n(2001)-1000)\n\t}\n\n\t// Print, process with error, and print again.\n\tmon.StaySetVariablesPrintAll()\n\n\tmon.StaySetVariablesDo(func(ssv *StaySetVariable) {\n\t\tif ssv.Count >= 25 {\n\t\t\t// Divide by zero.\n\t\t\tssv.Count = ssv.Count / (ssv.Count - ssv.Count)\n\t\t}\n\t})\n\n\tmon.StaySetVariablesPrintAll()\n}\n\n// Test of the DSR monitor.\nfunc TestDsrMonitor(t *testing.T) {\n\tmon := Monitor()\n\n\tmon.Register(\"monitor:dsr:a\", func() string { return \"A\" })\n\tmon.Register(\"monitor:dsr:b\", func() string { return \"4711\" })\n\tmon.Register(\"monitor:dsr:c\", func() string { return \"2011-05-07\" })\n\n\tmon.DynamicStatusValuesPrintAll()\n}\n\n//--------------------\n// HELPERS\n//--------------------\n\n// Test type.\ntype TT struct{}\n\nfunc (tt *TT) Add(a, b int) int { return a + b }\n\nfunc (tt *TT) Mul(a, b, c, d, e int) int { return a * b * c * d * e }\n\n// Order item type.\ntype OrderItem struct {\n\tArticleNo    int\n\tCount        int\n\tUnitPrice    float64\n\tDiscountPerc float64\n}\n\n// Order type.\ntype Order struct {\n\tOrderNo    UUID\n\tCustomerNo int\n\tItems      []*OrderItem\n}\n\nfunc (o *Order) String() string {\n\tmsg := \"ON: %v / CN: %4v / I: %v\"\n\n\treturn fmt.Sprintf(msg, o.OrderNo, o.CustomerNo, len(o.Items))\n}\n\n// Order item analysis type.\ntype OrderItemAnalysis struct {\n\tArticleNo int\n\tQuantity  int\n\tAmount    float64\n\tDiscount  float64\n}\n\nfunc (oia *OrderItemAnalysis) String() string {\n\tmsg := \"AN: %5v / Q: %4v / A: %10.2f € / D: %10.2f €\"\n\n\treturn fmt.Sprintf(msg, oia.ArticleNo, oia.Quantity, oia.Amount, oia.Discount)\n}\n\n// Order list.\ntype OrderList []*Order\n\nfunc (l OrderList) Len() int {\n\treturn len(l)\n}\n\nfunc (l OrderList) Less(i, j int) bool {\n\treturn l[i].CustomerNo < l[j].CustomerNo\n}\n\nfunc (l OrderList) Swap(i, j int) {\n\tl[i], l[j] = l[j], l[i]\n}\n\n// Action function.\ntype Action func() string\n\n// A positive action.\nfunc PositiveAction() string {\n\tlog.Printf(\"Perform positive action.\")\n\n\treturn \"OK\"\n}\n\n// A failing action.\nfunc FailAction() string {\n\tlog.Printf(\"Perform failing action!\")\n\n\tpanic(\"Fail!\")\n\n\treturn \"Fail!\"\n}\n\n// A time consuming action.\nfunc TimeConsumingAction() string {\n\tlog.Printf(\"Perform time consuming action!\")\n\n\ttime.Sleep(3e8)\n\n\treturn \"Time consumed\"\n}\n\n// Recoverable action type.\ntype RecoverableAction struct {\n\tactionChan chan Action\n\treplyChan  chan string\n\tsupervisor *Supervisor\n\theartbeat  *Heartbeat\n}\n\n// Create a new recoverable action.\nfunc NewRecoverableAction(supervisor *Supervisor) *RecoverableAction {\n\tra := &RecoverableAction{\n\t\tactionChan: make(chan Action),\n\t\treplyChan:  make(chan string, 5),\n\t\tsupervisor: supervisor,\n\t}\n\n\tra.heartbeat = NewHeartbeat(ra, 1e8)\n\n\tgo ra.backend()\n\n\treturn ra\n}\n\n// Send an action to perform.\nfunc (ra *RecoverableAction) Action(action Action) string {\n\tra.actionChan <- action\n\n\treturn <-ra.replyChan\n}\n\n// Implement Supervisor() of the recoverable interface.\nfunc (ra *RecoverableAction) Supervisor() *Supervisor {\n\treturn ra.supervisor\n}\n\n// Implement Recover() of the recoverable interface.\nfunc (ra *RecoverableAction) Recover(r Recoverable, err interface{}) {\n\tif ra == r {\n\t\tlog.Printf(\"Recovering error '%v'!\", err)\n\n\t\tra.replyChan <- \"Recovered\"\n\n\t\tgo ra.backend()\n\t}\n}\n\n// Backend of the recoverable action.\nfunc (ra *RecoverableAction) backend() {\n\tdefer func() {\n\t\tHelpIfNeeded(ra, recover())\n\t}()\n\n\tfor {\n\t\tselect {\n\t\tcase action := <-ra.actionChan:\n\t\t\tra.replyChan <- action()\n\t\tcase h := <-ra.heartbeat.HeartbeatChan:\n\t\t\tImAlive(h)\n\t\t}\n\t}\n}\n\n// Generate a test order.\nfunc generateTestOrders(count int) KeyValueChan {\n\tarticleMaxNo := 9999\n\tunitPrices := make([]float64, articleMaxNo+1)\n\n\tfor i := 0; i < articleMaxNo+1; i++ {\n\t\tunitPrices[i] = rand.Float64() * 100.0\n\t}\n\n\tkvc := make(KeyValueChan)\n\n\tgo func() {\n\t\tfor i := 0; i < count; i++ {\n\t\t\torder := new(Order)\n\n\t\t\torder.OrderNo = NewUUID()\n\t\t\torder.CustomerNo = rand.Intn(999) + 1\n\t\t\torder.Items = make([]*OrderItem, rand.Intn(9)+1)\n\n\t\t\tfor j := 0; j < len(order.Items); j++ {\n\t\t\t\tarticleNo := rand.Intn(articleMaxNo)\n\n\t\t\t\torder.Items[j] = &OrderItem{\n\t\t\t\t\tArticleNo:    articleNo,\n\t\t\t\t\tCount:        rand.Intn(9) + 1,\n\t\t\t\t\tUnitPrice:    unitPrices[articleNo],\n\t\t\t\t\tDiscountPerc: rand.Float64() * 4.0,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tkvc <- &KeyValue{order.OrderNo.String(), order}\n\t\t}\n\n\t\tclose(kvc)\n\t}()\n\n\treturn kvc\n}\n\n// Generate a list with test orders.\nfunc generateTestOrdersList(count int) OrderList {\n\tl := make(OrderList, count)\n\tidx := 0\n\n\tfor kv := range generateTestOrders(count) {\n\t\tl[idx] = kv.Value.(*Order)\n\n\t\tidx++\n\t}\n\n\treturn l\n}\n\n// Create a job that leads to a an event every 2 seconds.\nfunc createJob(t *testing.T, descr string, delete bool) *Job {\n\tcf := func(now *time.Time) (bool, bool) { return now.Seconds()%2 == 0, delete }\n\ttf := func(id string) { t.Logf(\"Performed job %s\\n\", id) }\n\n\treturn NewJob(\"test-server-\"+descr, cf, tf)\n}\n\n// Create a SML structure.\nfunc createSmlStructure() *TagNode {\n\troot := NewTagNode(\"root\")\n\n\troot.AppendText(\"Text A\")\n\troot.AppendText(\"Text B\")\n\n\troot.AppendTaggedText(\"comment\", \"A first comment.\")\n\n\tsubA := root.AppendTag(\"sub-a:1st:important\")\n\n\tsubA.AppendText(\"Text A.A\")\n\n\troot.AppendTaggedText(\"comment\", \"A second comment.\")\n\n\tsubB := root.AppendTag(\"sub-b:2nd\")\n\n\tsubB.AppendText(\"Text B.A\")\n\tsubB.AppendTaggedText(\"raw\", \"Any raw text with {, }, and ^.\")\n\n\treturn root\n}\n\n// Do some work.\nfunc work(n int) int {\n\tif n < 0 {\n\t\treturn 0\n\t}\n\n\treturn n * work(n-1)\n}\n\n//--------------------\n// TEST LOGIN EVENT HANDLER\n//--------------------\n\n// Prepare payload.\ntype PreparePayload struct {\n\tuserId   string\n\tpassword string\n}\n\n// Login payload.\ntype LoginPayload struct {\n\tpassword string\n}\n\n// Reset payload.\ntype ResetPayload struct{}\n\n// Unlock payload.\ntype UnlockPayload struct{}\n\n// Login handler tyoe.\ntype LoginHandler struct {\n\tuserId              string\n\tpassword            string\n\tillegalLoginCounter int\n\tlocked              bool\n}\n\n// Create a new login handler.\nfunc NewLoginHandler() *LoginHandler {\n\treturn new(LoginHandler)\n}\n\n// Return the initial state.\nfunc (lh *LoginHandler) Init() string {\n\treturn \"New\"\n}\n\n// Terminate the handler.\nfunc (lh *LoginHandler) Terminate(string, interface{}) string {\n\treturn \"LoggedIn\"\n}\n\n// Handler for state: \"New\".\nfunc (lh *LoginHandler) HandleStateNew(c *Condition) (string, interface{}) {\n\tswitch pld := c.Payload.(type) {\n\tcase *PreparePayload:\n\t\tlh.userId = pld.userId\n\t\tlh.password = pld.password\n\t\tlh.illegalLoginCounter = 0\n\t\tlh.locked = false\n\n\t\tlog.Printf(\"User '%v' prepared.\", lh.userId)\n\n\t\treturn \"Authenticating\", nil\n\tcase *LoginPayload:\n\t\tlog.Printf(\"Illegal login, handler not initialized!\")\n\n\t\treturn \"New\", false\n\tcase Timeout:\n\t\tlog.Printf(\"Timeout, terminate handler!\")\n\n\t\treturn \"Terminate\", nil\n\t}\n\n\tlog.Printf(\"Illegal payload '%v' during state 'new'!\", c.Payload)\n\n\treturn \"New\", nil\n}\n\n// Handler for state: \"Authenticating\".\nfunc (lh *LoginHandler) HandleStateAuthenticating(c *Condition) (string, interface{}) {\n\tswitch pld := c.Payload.(type) {\n\tcase *LoginPayload:\n\t\tif pld.password == lh.password {\n\t\t\tlh.illegalLoginCounter = 0\n\t\t\tlh.locked = false\n\n\t\t\tlog.Printf(\"User '%v' logged in.\", lh.userId)\n\n\t\t\treturn \"Terminate\", true\n\t\t}\n\n\t\tlog.Printf(\"User '%v' used illegal password.\", lh.userId)\n\n\t\tlh.illegalLoginCounter++\n\n\t\tif lh.illegalLoginCounter == 3 {\n\t\t\tlh.locked = true\n\n\t\t\tlog.Printf(\"User '%v' locked!\", lh.userId)\n\n\t\t\treturn \"Locked\", false\n\t\t}\n\n\t\treturn \"Authenticating\", false\n\tcase *UnlockPayload:\n\t\tlog.Printf(\"No need to unlock user '%v'!\", lh.userId)\n\n\t\treturn \"Authenticating\", nil\n\tcase *ResetPayload, Timeout:\n\t\tlh.illegalLoginCounter = 0\n\t\tlh.locked = false\n\n\t\tlog.Printf(\"User '%v' resetted.\", lh.userId)\n\n\t\treturn \"Authenticating\", nil\n\t}\n\n\tlog.Printf(\"Illegal payload '%v' during state 'authenticating'!\", c.Payload)\n\n\treturn \"Authenticating\", nil\n}\n\n// Handler for state: \"Locked\".\nfunc (lh *LoginHandler) HandleStateLocked(c *Condition) (string, interface{}) {\n\tswitch pld := c.Payload.(type) {\n\tcase *LoginPayload:\n\t\tlog.Printf(\"User '%v' login rejected, user is locked!\", lh.userId)\n\n\t\treturn \"Locked\", false\n\tcase *ResetPayload, *UnlockPayload, Timeout:\n\t\tlh.illegalLoginCounter = 0\n\t\tlh.locked = false\n\n\t\tlog.Printf(\"User '%v' resetted / unlocked.\", lh.userId)\n\n\t\treturn \"Authenticating\", nil\n\t}\n\n\tlog.Printf(\"Illegal payload '%v' during state 'loacked'!\", c.Payload)\n\n\treturn \"Locked\", nil\n}\n\n/*\n\tEOF\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_11/tideland-cgl.googlecode.com/hg/cglfsm.go",
    "content": "/*\n\tTideland Common Go Library - Finite State Machine\n\n\tCopyright (C) 2010-2011 Frank Mueller / Oldenburg / Germany\n\n\tRedistribution and use in source and binary forms, with or\n\tmodification, are permitted provided that the following conditions are\n\tmet:\n\n\tRedistributions of source code must retain the above copyright notice, this\n\tlist of conditions and the following disclaimer.\n\n\tRedistributions in binary form must reproduce the above copyright notice,\n\tthis list of conditions and the following disclaimer in the documentation\n\tand/or other materials provided with the distribution.\n\n\tNeither the name of Tideland nor the names of its contributors may be\n\tused to endorse or promote products derived from this software without\n\tspecific prior written permission.\n\n\tTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n\tAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n\tIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n\tARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n\tLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n\tCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n\tSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n\tINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n\tCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n\tARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\n\tTHE POSSIBILITY OF SUCH DAMAGE.\n*/\n\npackage cgl\n\n//--------------------\n// IMPORTS\n//--------------------\n\nimport (\n\t\"log\"\n\t\"reflect\"\n\t\"strings\"\n\t\"time\"\n)\n\n//--------------------\n// HELPER TYPES\n//--------------------\n\n// Condition type.\ntype Condition struct {\n\tNow     int64\n\tPayload interface{}\n}\n\n// Transition type.\ntype transition struct {\n\tpayload    interface{}\n\tresultChan chan interface{}\n}\n\n// Timeout type.\ntype Timeout int64\n\n//--------------------\n// FINITE STATE MACHINE\n//--------------------\n\n// Handler interface.\ntype Handler interface {\n\tInit() string\n\tTerminate(string, interface{}) string\n}\n\n// State machine type.\ntype FSM struct {\n\tHandler        Handler\n\thandlerValue   reflect.Value\n\thandlerFuncs   map[string]reflect.Value\n\tstate          string\n\ttransitionChan chan *transition\n\ttimeoutChan    <-chan int64\n}\n\n// Create a new finite state machine.\nfunc NewFSM(h Handler, timeout int64) *FSM {\n\tvar bufferSize int\n\n\tif timeout > 0 {\n\t\tbufferSize = int(timeout / 1e3)\n\t} else {\n\t\tbufferSize = 10\n\t}\n\n\tfsm := &FSM{\n\t\tHandler:        h,\n\t\thandlerFuncs:   make(map[string]reflect.Value),\n\t\tstate:          h.Init(),\n\t\ttransitionChan: make(chan *transition, bufferSize),\n\t}\n\n\tif timeout > 0 {\n\t\tfsm.timeoutChan = time.After(timeout)\n\t}\n\n\tfsm.analyze()\n\n\tgo fsm.backend()\n\n\treturn fsm\n}\n\n// Send a payload to handle and return the result.\nfunc (fsm *FSM) SendWithResult(payload interface{}) interface{} {\n\tt := &transition{payload, make(chan interface{})}\n\n\tfsm.transitionChan <- t\n\n\treturn <-t.resultChan\n}\n\n// Send a payload with no result.\nfunc (fsm *FSM) Send(payload interface{}) {\n\tt := &transition{payload, nil}\n\n\tfsm.transitionChan <- t\n}\n\n// Send a payload with no result after a given time.\nfunc (fsm *FSM) SendAfter(payload interface{}, ns int64) {\n\tsaf := func() {\n\t\ttime.Sleep(ns)\n\n\t\tfsm.Send(payload)\n\t}\n\n\tgo saf()\n}\n\n// Return the current state.\nfunc (fsm *FSM) State() string {\n\treturn fsm.state\n}\n\n// Return the supervisor.\nfunc (fsm *FSM) Supervisor() *Supervisor {\n\treturn GlobalSupervisor()\n}\n\n// Recover after an error.\nfunc (fsm *FSM) Recover(recoverable Recoverable, err interface{}) {\n\tlog.Printf(\"[cgl] recovering finite state machine server backend after error '%v'!\", err)\n\n\tgo fsm.backend()\n}\n\n// Analyze the event handler and prepare the state table.\nfunc (fsm *FSM) analyze() {\n\tprefix := \"HandleState\"\n\n\tfsm.handlerValue = reflect.ValueOf(fsm.Handler)\n\n\tnum := fsm.handlerValue.Type().NumMethod()\n\n\tfor i := 0; i < num; i++ {\n\t\tmeth := fsm.handlerValue.Type().Method(i)\n\n\t\tif (meth.PkgPath == \"\") && (strings.HasPrefix(meth.Name, prefix)) {\n\t\t\tif (meth.Type.NumIn() == 2) && (meth.Type.NumOut() == 2) {\n\t\t\t\tstate := meth.Name[len(prefix):len(meth.Name)]\n\n\t\t\t\tfsm.handlerFuncs[state] = meth.Func\n\t\t\t}\n\t\t}\n\t}\n}\n\n// State machine backend.\nfunc (fsm *FSM) backend() {\n\tdefer func() {\n\t\tHelpIfNeeded(fsm, recover())\n\t}()\n\n\t// Message loop.\n\n\tfor {\n\t\tselect {\n\t\tcase t := <-fsm.transitionChan:\n\t\t\t// Regular transition.\n\n\t\t\tif nextState, ok := fsm.handle(t); ok {\n\t\t\t\t// Continue.\n\n\t\t\t\tfsm.state = nextState\n\t\t\t} else {\n\t\t\t\t// Stop processing.\n\n\t\t\t\tfsm.state = fsm.Handler.Terminate(fsm.state, nextState)\n\n\t\t\t\treturn\n\t\t\t}\n\t\tcase to := <-fsm.timeoutChan:\n\t\t\t// Timeout signal resent to let it be handled.\n\n\t\t\tt := &transition{Timeout(to), nil}\n\n\t\t\tfsm.transitionChan <- t\n\t\t}\n\t}\n}\n\n// Handle a transition.\nfunc (fsm *FSM) handle(t *transition) (string, bool) {\n\tcondition := &Condition{time.Nanoseconds(), t.payload}\n\thandlerFunc := fsm.handlerFuncs[fsm.state]\n\thandlerArgs := make([]reflect.Value, 2)\n\n\thandlerArgs[0] = fsm.handlerValue\n\thandlerArgs[1] = reflect.ValueOf(condition)\n\n\tresults := handlerFunc.Call(handlerArgs)\n\n\tnextState := results[0].Interface().(string)\n\tresult := results[1].Interface()\n\n\t// Return a result if wanted.\n\n\tif t.resultChan != nil {\n\t\tt.resultChan <- result\n\t}\n\n\t// Check for termination.\n\n\tif nextState == \"Terminate\" {\n\t\treturn nextState, false\n\t}\n\n\treturn nextState, true\n}\n\n/*\n\tEOF\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_11/tideland-cgl.googlecode.com/hg/cglmon.go",
    "content": "/*\n\tTideland Common Go Library - Monitoring\n\n\tCopyright (C) 2009-2011 Frank Mueller / Oldenburg / Germany\n\n\tRedistribution and use in source and binary forms, with or\n\tmodification, are permitted provided that the following conditions are\n\tmet:\n\n\tRedistributions of source code must retain the above copyright notice, this\n\tlist of conditions and the following disclaimer.\n\n\tRedistributions in binary form must reproduce the above copyright notice,\n\tthis list of conditions and the following disclaimer in the documentation\n\tand/or other materials provided with the distribution.\n\n\tNeither the name of Tideland nor the names of its contributors may be\n\tused to endorse or promote products derived from this software without\n\tspecific prior written permission.\n\n\tTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n\tAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n\tIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n\tARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n\tLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n\tCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n\tSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n\tINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n\tCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n\tARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\n\tTHE POSSIBILITY OF SUCH DAMAGE.\n*/\n\npackage cgl\n\n//--------------------\n// IMPORTS\n//--------------------\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n)\n\n//--------------------\n// GLOBAL VARIABLES\n//--------------------\n\nvar monitor *SystemMonitor\n\n//--------------------\n// CONSTANTS\n//--------------------\n\nconst (\n\tetmTLine  = \"+------------------------------------------+-----------+-----------+-----------+-----------+---------------+-----------+\\n\"\n\tetmHeader = \"| Name                                     | Count     | Min Time  | Max Time  | Avg Time  | Total Time    | Op/Sec    |\\n\"\n\tetmFormat = \"| %-40s | %9d | %9.3f | %9.3f | %9.3f | %13.3f | %9d |\\n\"\n\tetmFooter = \"| All times in milliseconds.                                                                                           |\\n\"\n\tetmELine  = \"+----------------------------------------------------------------------------------------------------------------------+\\n\"\n\n\tssiTLine  = \"+------------------------------------------+-----------+---------------+---------------+---------------+---------------+\\n\"\n\tssiHeader = \"| Name                                     | Count     | Act Value     | Min Value     | Max Value     | Avg Value     |\\n\"\n\tssiFormat = \"| %-40s | %9d | %13d | %13d | %13d | %13d |\\n\"\n\n\tdsrTLine  = \"+------------------------------------------+---------------------------------------------------------------------------+\\n\"\n\tdsrHeader = \"| Name                                     | Value                                                                     |\\n\"\n\tdsrFormat = \"| %-40s | %-73s |\\n\"\n)\n\nconst (\n\tcmdMeasuringPointsMap = iota\n\tcmdMeasuringPointsDo\n\tcmdStaySetVariablesMap\n\tcmdStaySetVariablesDo\n\tcmdDynamicStatusRetrieversMap\n\tcmdDynamicStatusRetrieversDo\n)\n\n//--------------------\n// MONITORING\n//--------------------\n\n// Command encapsulated the data for any command.\ntype command struct {\n\topCode   int\n\targs     interface{}\n\trespChan chan interface{}\n}\n\n// The system monitor type.\ntype SystemMonitor struct {\n\tetmData                   map[string]*MeasuringPoint\n\tssiData                   map[string]*StaySetVariable\n\tdsrData                   map[string]DynamicStatusRetriever\n\tmeasuringChan             chan *Measuring\n\tvalueChan                 chan *value\n\tretrieverRegistrationChan chan *retrieverRegistration\n\tcommandChan               chan *command\n}\n\n// Monitor returns the system monitor if it exists.\n// Otherwise it creates it first.\nfunc Monitor() *SystemMonitor {\n\tif monitor == nil {\n\t\t// Create system monitor.\n\t\tmonitor = &SystemMonitor{\n\t\t\tetmData:                   make(map[string]*MeasuringPoint),\n\t\t\tssiData:                   make(map[string]*StaySetVariable),\n\t\t\tdsrData:                   make(map[string]DynamicStatusRetriever),\n\t\t\tmeasuringChan:             make(chan *Measuring, 1000),\n\t\t\tvalueChan:                 make(chan *value, 1000),\n\t\t\tretrieverRegistrationChan: make(chan *retrieverRegistration, 10),\n\t\t\tcommandChan:               make(chan *command),\n\t\t}\n\n\t\tgo monitor.backend()\n\t}\n\n\treturn monitor\n}\n\n// BeginMeasuring starts a new measuring with a given id.\n// All measurings with the same id will be aggregated.\nfunc (sm *SystemMonitor) BeginMeasuring(id string) *Measuring {\n\treturn &Measuring{sm, id, time.Nanoseconds(), 0}\n}\n\n// Measure the execution of a function.\nfunc (sm *SystemMonitor) Measure(id string, f func()) {\n\tm := sm.BeginMeasuring(id)\n\n\tf()\n\n\tm.EndMeasuring()\n}\n\n// MeasuringPointsMap performs the function f for all measuring points\n// and returns a slice with the return values of the function that are\n// not nil.\nfunc (sm *SystemMonitor) MeasuringPointsMap(f func(*MeasuringPoint) interface{}) []interface{} {\n\tcmd := &command{cmdMeasuringPointsMap, f, make(chan interface{})}\n\n\tsm.commandChan <- cmd\n\n\tresp := <-cmd.respChan\n\n\treturn resp.([]interface{})\n}\n\n// MeasuringPointsDo performs the function f for\n// all measuring points.\nfunc (sm *SystemMonitor) MeasuringPointsDo(f func(*MeasuringPoint)) {\n\tcmd := &command{cmdMeasuringPointsDo, f, nil}\n\n\tsm.commandChan <- cmd\n}\n\n// MeasuringPointsWrite prints the measuring points for which\n// the passed function returns true to the passed writer.\nfunc (sm *SystemMonitor) MeasuringPointsWrite(w io.Writer, ff func(*MeasuringPoint) bool) {\n\tpf := func(t int64) float64 { return float64(t) / 1000000.0 }\n\n\tfmt.Fprint(w, etmTLine)\n\tfmt.Fprint(w, etmHeader)\n\tfmt.Fprint(w, etmTLine)\n\n\tlines := sm.MeasuringPointsMap(func(mp *MeasuringPoint) interface{} {\n\t\tif ff(mp) {\n\t\t\tops := 1e9 / mp.AvgTime\n\n\t\t\treturn fmt.Sprintf(etmFormat, mp.Id, mp.Count, pf(mp.MinTime), pf(mp.MaxTime), pf(mp.AvgTime), pf(mp.TtlTime), ops)\n\t\t}\n\n\t\treturn nil\n\t})\n\n\tfor _, line := range lines {\n\t\tfmt.Fprint(w, line)\n\t}\n\n\tfmt.Fprint(w, etmTLine)\n\tfmt.Fprint(w, etmFooter)\n\tfmt.Fprint(w, etmELine)\n}\n\n// MeasuringPointsPrintAll prints all measuring points\n// to STDOUT.\nfunc (sm *SystemMonitor) MeasuringPointsPrintAll() {\n\tsm.MeasuringPointsWrite(os.Stdout, func(mp *MeasuringPoint) bool { return true })\n}\n\n// SetValue sets a value of a stay-set variable.\nfunc (sm *SystemMonitor) SetValue(id string, v int64) {\n\tsm.valueChan <- &value{id, v}\n}\n\n// StaySetVariablesMap performs the function f for all variables\n// and returns a slice with the return values of the function that are\n// not nil.\nfunc (sm *SystemMonitor) StaySetVariablesMap(f func(*StaySetVariable) interface{}) []interface{} {\n\tcmd := &command{cmdStaySetVariablesMap, f, make(chan interface{})}\n\n\tsm.commandChan <- cmd\n\n\tresp := <-cmd.respChan\n\n\treturn resp.([]interface{})\n}\n\n// StaySetVariablesDo performs the function f for all\n// variables.\nfunc (sm *SystemMonitor) StaySetVariablesDo(f func(*StaySetVariable)) {\n\tcmd := &command{cmdStaySetVariablesDo, f, nil}\n\n\tsm.commandChan <- cmd\n}\n\n// StaySetVariablesWrite prints the stay-set variables for which\n// the passed function returns true to the passed writer.\nfunc (sm *SystemMonitor) StaySetVariablesWrite(w io.Writer, ff func(*StaySetVariable) bool) {\n\tfmt.Fprint(w, ssiTLine)\n\tfmt.Fprint(w, ssiHeader)\n\tfmt.Fprint(w, ssiTLine)\n\n\tlines := sm.StaySetVariablesMap(func(ssv *StaySetVariable) interface{} {\n\t\tif ff(ssv) {\n\t\t\treturn fmt.Sprintf(ssiFormat, ssv.Id, ssv.Count, ssv.ActValue, ssv.MinValue, ssv.MaxValue, ssv.AvgValue)\n\t\t}\n\n\t\treturn nil\n\t})\n\n\tfor _, line := range lines {\n\t\tfmt.Fprint(w, line)\n\t}\n\n\tfmt.Fprint(w, ssiTLine)\n}\n\n// StaySetVariablesPrintAll prints all stay-set variables\n// to STDOUT.\nfunc (sm *SystemMonitor) StaySetVariablesPrintAll() {\n\tsm.StaySetVariablesWrite(os.Stdout, func(ssv *StaySetVariable) bool { return true })\n}\n\n// Register registers a new dynamic status retriever function.\nfunc (sm *SystemMonitor) Register(id string, rf DynamicStatusRetriever) {\n\tsm.retrieverRegistrationChan <- &retrieverRegistration{id, rf}\n}\n\n// Unregister unregisters a dynamic status retriever function.\nfunc (sm *SystemMonitor) Unregister(id string) {\n\tsm.retrieverRegistrationChan <- &retrieverRegistration{id, nil}\n}\n\n// DynamicStatusValuesMap performs the function f for all status values\n// and returns a slice with the return values of the function that are\n// not nil.\nfunc (sm *SystemMonitor) DynamicStatusValuesMap(f func(string, string) interface{}) []interface{} {\n\tcmd := &command{cmdDynamicStatusRetrieversMap, f, make(chan interface{})}\n\n\tsm.commandChan <- cmd\n\n\tresp := <-cmd.respChan\n\n\treturn resp.([]interface{})\n}\n\n// DynamicStatusValuesDo performs the function f for all\n// status values.\nfunc (sm *SystemMonitor) DynamicStatusValuesDo(f func(string, string)) {\n\tcmd := &command{cmdDynamicStatusRetrieversDo, f, nil}\n\n\tsm.commandChan <- cmd\n}\n\n// DynamicStatusValuesWrite prints the status values for which\n// the passed function returns true to the passed writer.\nfunc (sm *SystemMonitor) DynamicStatusValuesWrite(w io.Writer, ff func(string, string) bool) {\n\tfmt.Fprint(w, dsrTLine)\n\tfmt.Fprint(w, dsrHeader)\n\tfmt.Fprint(w, dsrTLine)\n\n\tlines := sm.DynamicStatusValuesMap(func(id, dsv string) interface{} {\n\t\tif ff(id, dsv) {\n\t\t\treturn fmt.Sprintf(dsrFormat, id, dsv)\n\t\t}\n\n\t\treturn nil\n\t})\n\n\tfor _, line := range lines {\n\t\tfmt.Fprint(w, line)\n\t}\n\n\tfmt.Fprint(w, dsrTLine)\n}\n\n// DynamicStatusValuesPrintAll prints all status values to STDOUT.\nfunc (sm *SystemMonitor) DynamicStatusValuesPrintAll() {\n\tsm.DynamicStatusValuesWrite(os.Stdout, func(id, dsv string) bool { return true })\n}\n\n// Return the supervisor.\nfunc (sm *SystemMonitor) Supervisor() *Supervisor {\n\treturn GlobalSupervisor()\n}\n\n// Recover after an error.\nfunc (sm *SystemMonitor) Recover(recoverable Recoverable, err interface{}) {\n\tlog.Printf(\"[cgl] recovering system monitor backend after error '%v'!\", err)\n\n\tgo sm.backend()\n}\n\n// Backend of the system monitor.\nfunc (sm *SystemMonitor) backend() {\n\tdefer func() {\n\t\tHelpIfNeeded(sm, recover())\n\t}()\n\n\tfor {\n\t\tselect {\n\t\tcase measuring := <-sm.measuringChan:\n\t\t\t// Received a new measuring.\n\t\t\tif mp, ok := sm.etmData[measuring.id]; ok {\n\t\t\t\t// Measuring point found.\n\t\t\t\tmp.update(measuring)\n\t\t\t} else {\n\t\t\t\t// New measuring point.\n\t\t\t\tsm.etmData[measuring.id] = newMeasuringPoint(measuring)\n\t\t\t}\n\t\tcase value := <-sm.valueChan:\n\t\t\t// Received a new value.\n\t\t\tif ssv, ok := sm.ssiData[value.id]; ok {\n\t\t\t\t// Variable found.\n\t\t\t\tssv.update(value)\n\t\t\t} else {\n\t\t\t\t// New stay-set variable.\n\t\t\t\tsm.ssiData[value.id] = newStaySetVariable(value)\n\t\t\t}\n\t\tcase registration := <-sm.retrieverRegistrationChan:\n\t\t\t// Received a new retriever for registration.\n\t\t\tif registration.dsr != nil {\n\t\t\t\t// Register a new retriever.\n\t\t\t\tsm.dsrData[registration.id] = registration.dsr\n\t\t\t} else {\n\t\t\t\t// Deregister a retriever.\n\t\t\t\tif dsr, ok := sm.dsrData[registration.id]; ok {\n\t\t\t\t\tsm.dsrData[registration.id] = dsr, false\n\t\t\t\t}\n\t\t\t}\n\t\tcase cmd := <-sm.commandChan:\n\t\t\t// Receivedd a command to process.\n\t\t\tsm.processCommand(cmd)\n\t\t}\n\t}\n}\n\n// Process a command.\nfunc (sm *SystemMonitor) processCommand(cmd *command) {\n\tswitch cmd.opCode {\n\tcase cmdMeasuringPointsMap:\n\t\t// Map the measuring points.\n\t\tvar resp []interface{}\n\n\t\tf := cmd.args.(func(*MeasuringPoint) interface{})\n\n\t\tfor _, mp := range sm.etmData {\n\t\t\tv := f(mp)\n\n\t\t\tif v != nil {\n\t\t\t\tresp = append(resp, v)\n\t\t\t}\n\t\t}\n\n\t\tcmd.respChan <- resp\n\tcase cmdMeasuringPointsDo:\n\t\t// Iterate over the measurings.\n\t\tf := cmd.args.(func(*MeasuringPoint))\n\n\t\tfor _, mp := range sm.etmData {\n\t\t\tf(mp)\n\t\t}\n\tcase cmdStaySetVariablesMap:\n\t\t// Map the stay-set variables.\n\t\tvar resp []interface{}\n\n\t\tf := cmd.args.(func(*StaySetVariable) interface{})\n\n\t\tfor _, ssv := range sm.ssiData {\n\t\t\tv := f(ssv)\n\n\t\t\tif v != nil {\n\t\t\t\tresp = append(resp, v)\n\t\t\t}\n\t\t}\n\n\t\tcmd.respChan <- resp\n\tcase cmdStaySetVariablesDo:\n\t\t// Iterate over the stay-set variables.\n\t\tf := cmd.args.(func(*StaySetVariable))\n\n\t\tfor _, ssv := range sm.ssiData {\n\t\t\tf(ssv)\n\t\t}\n\tcase cmdDynamicStatusRetrieversMap:\n\t\t// Map the return values of the dynamic status\n\t\t// retriever functions.\n\t\tvar resp []interface{}\n\n\t\tf := cmd.args.(func(string, string) interface{})\n\n\t\tfor id, dsr := range sm.dsrData {\n\t\t\tdsv := dsr()\n\t\t\tv := f(id, dsv)\n\n\t\t\tif v != nil {\n\t\t\t\tresp = append(resp, v)\n\t\t\t}\n\t\t}\n\n\t\tcmd.respChan <- resp\n\tcase cmdDynamicStatusRetrieversDo:\n\t\t// Iterate over the return values of the\n\t\t// dynamic status retriever functions.\n\t\tf := cmd.args.(func(string, string))\n\n\t\tfor id, dsr := range sm.dsrData {\n\t\t\tdsv := dsr()\n\n\t\t\tf(id, dsv)\n\t\t}\n\t}\n}\n\n//--------------------\n// ADDITIONAL MEASURING TYPES\n//--------------------\n\n// Measuring contains one measuring.\ntype Measuring struct {\n\tsystemMonitor *SystemMonitor\n\tid            string\n\tstartTime     int64\n\tendTime       int64\n}\n\n// EndMEasuring ends a measuring and passes it to the\n// measuring server in the background.\nfunc (m *Measuring) EndMeasuring() int64 {\n\tm.endTime = time.Nanoseconds()\n\n\tm.systemMonitor.measuringChan <- m\n\n\treturn m.endTime - m.startTime\n}\n\n// MeasuringPoint contains the cumulated measuring\n// data of one measuring point.\ntype MeasuringPoint struct {\n\tId      string\n\tCount   int64\n\tMinTime int64\n\tMaxTime int64\n\tTtlTime int64\n\tAvgTime int64\n}\n\n// Create a new measuring point out of a measuring.\nfunc newMeasuringPoint(m *Measuring) *MeasuringPoint {\n\ttime := m.endTime - m.startTime\n\tmp := &MeasuringPoint{\n\t\tId:      m.id,\n\t\tCount:   1,\n\t\tMinTime: time,\n\t\tMaxTime: time,\n\t\tTtlTime: time,\n\t\tAvgTime: time,\n\t}\n\n\treturn mp\n}\n\n// Update a measuring point with a measuring.\nfunc (mp *MeasuringPoint) update(m *Measuring) {\n\ttime := m.endTime - m.startTime\n\n\tmp.Count++\n\n\tif mp.MinTime > time {\n\t\tmp.MinTime = time\n\t}\n\n\tif mp.MaxTime < time {\n\t\tmp.MaxTime = time\n\t}\n\n\tmp.TtlTime += time\n\tmp.AvgTime = mp.TtlTime / mp.Count\n}\n\n// New value for a stay-set variable.\ntype value struct {\n\tid    string\n\tvalue int64\n}\n\n// StaySetVariable contains the cumulated values\n// for one stay-set variable.\ntype StaySetVariable struct {\n\tId       string\n\tCount    int64\n\tActValue int64\n\tMinValue int64\n\tMaxValue int64\n\tAvgValue int64\n\ttotal    int64\n}\n\n// Create a new stay-set variable out of a value.\nfunc newStaySetVariable(v *value) *StaySetVariable {\n\tssv := &StaySetVariable{\n\t\tId:       v.id,\n\t\tCount:    1,\n\t\tActValue: v.value,\n\t\tMinValue: v.value,\n\t\tMaxValue: v.value,\n\t\tAvgValue: v.value,\n\t}\n\n\treturn ssv\n}\n\n// Update a stay-set variable with a value.\nfunc (ssv *StaySetVariable) update(v *value) {\n\tssv.Count++\n\n\tssv.ActValue = v.value\n\tssv.total += v.value\n\n\tif ssv.MinValue > ssv.ActValue {\n\t\tssv.MinValue = ssv.ActValue\n\t}\n\n\tif ssv.MaxValue < ssv.ActValue {\n\t\tssv.MaxValue = ssv.ActValue\n\t}\n\n\tssv.AvgValue = ssv.total / ssv.Count\n}\n\n// DynamicStatusRetriever is called by the server and\n// returns a current status as string.\ntype DynamicStatusRetriever func() string\n\n// New registration of a retriever function.\ntype retrieverRegistration struct {\n\tid  string\n\tdsr DynamicStatusRetriever\n}\n\n/*\n\tEOF\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_11/tideland-cgl.googlecode.com/hg/cglsml.go",
    "content": "/*\n\tTideland Common Go Library - Simple Markup Language\n\n\tCopyright (C) 2011 Frank Mueller / Oldenburg / Germany\n\n\tRedistribution and use in source and binary forms, with or\n\tmodification, are permitted provided that the following conditions are\n\tmet:\n\n\tRedistributions of source code must retain the above copyright notice, this\n\tlist of conditions and the following disclaimer.\n\n\tRedistributions in binary form must reproduce the above copyright notice,\n\tthis list of conditions and the following disclaimer in the documentation\n\tand/or other materials provided with the distribution.\n\n\tNeither the name of Tideland nor the names of its contributors may be\n\tused to endorse or promote products derived from this software without\n\tspecific prior written permission.\n\n\tTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n\tAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n\tIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n\tARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n\tLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n\tCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n\tSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n\tINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n\tCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n\tARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\n\tTHE POSSIBILITY OF SUCH DAMAGE.\n*/\n\npackage cgl\n\n//--------------------\n// IMPORTS\n//--------------------\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\t\"unicode\"\n)\n\n//--------------------\n// PROCESSOR\n//--------------------\n\n// Processor interface.\ntype Processor interface {\n\tOpenTag(tag []string)\n\tCloseTag(tag []string)\n\tText(text string)\n}\n\n//--------------------\n// NODE\n//--------------------\n\n// The node.\ntype Node interface {\n\tLen() int\n\tProcessWith(p Processor)\n}\n\n//--------------------\n// TAG NODE\n//--------------------\n\n// The tag node.\ntype TagNode struct {\n\ttag      []string\n\tchildren []Node\n}\n\n// Create a new tag node.\nfunc NewTagNode(tag string) *TagNode {\n\ttmp := strings.ToLower(tag)\n\n\tif !validIdentifier(tmp) {\n\t\treturn nil\n\t}\n\n\ttn := &TagNode{\n\t\ttag:      strings.Split(tmp, \":\"),\n\t\tchildren: make([]Node, 0),\n\t}\n\n\treturn tn\n}\n\n// Append a new tag.\nfunc (tn *TagNode) AppendTag(tag string) *TagNode {\n\tn := NewTagNode(tag)\n\n\tif n != nil {\n\t\ttn.children = append(tn.children, n)\n\t}\n\n\treturn n\n}\n\n// Append a tag node.\nfunc (tn *TagNode) AppendTagNode(n *TagNode) *TagNode {\n\ttn.children = append(tn.children, n)\n\n\treturn n\n}\n\n// Append a text node.\nfunc (tn *TagNode) AppendText(text string) *TextNode {\n\tn := NewTextNode(text)\n\n\ttn.children = append(tn.children, n)\n\n\treturn n\n}\n\n// Append a tagged text node.\nfunc (tn *TagNode) AppendTaggedText(tag, text string) *TagNode {\n\tn := NewTagNode(tag)\n\n\tif n != nil {\n\t\tn.AppendText(text)\n\n\t\ttn.children = append(tn.children, n)\n\t}\n\n\treturn n\n}\n\n// Append a text node.\nfunc (tn *TagNode) AppendTextNode(n *TextNode) *TextNode {\n\ttn.children = append(tn.children, n)\n\n\treturn n\n}\n\n// Return the len of the tag node (aka number of children).\nfunc (tn *TagNode) Len() int {\n\treturn len(tn.children)\n}\n\n// Process the node.\nfunc (tn *TagNode) ProcessWith(p Processor) {\n\tp.OpenTag(tn.tag)\n\n\tfor _, child := range tn.children {\n\t\tchild.ProcessWith(p)\n\t}\n\n\tp.CloseTag(tn.tag)\n}\n\n// Return the node as a string.\nfunc (tn *TagNode) String() string {\n\tbuf := bytes.NewBufferString(\"\")\n\tspp := NewSmlWriterProcessor(buf, true)\n\n\ttn.ProcessWith(spp)\n\n\treturn buf.String()\n}\n\n//--------------------\n// TEXT NODE\n//--------------------\n\n// The text node.\ntype TextNode struct {\n\ttext string\n}\n\n// Create a new text node.\nfunc NewTextNode(text string) *TextNode {\n\treturn &TextNode{text}\n}\n\n// Return the len of the text node.\nfunc (tn *TextNode) Len() int {\n\treturn len(tn.text)\n}\n\n// Process the node.\nfunc (tn *TextNode) ProcessWith(p Processor) {\n\tp.Text(tn.text)\n}\n\n// Return the node as a string.\nfunc (tn *TextNode) String() string {\n\treturn tn.text\n}\n\n//--------------------\n// PRIVATE FUNCTIONS\n//--------------------\n\n// Check an identifier (tag or id).\nfunc validIdentifier(id string) bool {\n\tfor _, c := range id {\n\t\tif c < 'a' || c > 'z' {\n\t\t\tif c < '0' || c > '9' {\n\t\t\t\tif c != '-' && c != ':' {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn true\n}\n\n//--------------------\n// SML READER\n//--------------------\n\n// Control values.\nconst (\n\tctrlText = iota\n\tctrlSpace\n\tctrlOpen\n\tctrlClose\n\tctrlEscape\n\tctrlTag\n\tctrlEOF\n\tctrlInvalid\n)\n\n// Node read modes.\nconst (\n\tmodeInit = iota\n\tmodeTag\n\tmodeText\n)\n\n// Reader for SML.\ntype SmlReader struct {\n\treader *bufio.Reader\n\tindex  int\n\troot   *TagNode\n\terror  os.Error\n}\n\n// Create the reader.\nfunc NewSmlReader(reader io.Reader) *SmlReader {\n\t// Init the reader.\n\n\tsr := &SmlReader{\n\t\treader: bufio.NewReader(reader),\n\t\tindex:  -1,\n\t}\n\n\tnode, ctrl := sr.readNode()\n\n\tswitch ctrl {\n\tcase ctrlClose:\n\t\tsr.root = node\n\t\tsr.error = nil\n\tcase ctrlEOF:\n\t\tmsg := fmt.Sprintf(\"eof too early at index %v\", sr.index)\n\n\t\tsr.error = os.NewError(msg)\n\tcase ctrlInvalid:\n\t\tmsg := fmt.Sprintf(\"invalid rune at index %v\", sr.index)\n\n\t\tsr.error = os.NewError(msg)\n\t}\n\n\treturn sr\n}\n\n// Return the root tag node.\nfunc (sr *SmlReader) RootTagNode() (*TagNode, os.Error) {\n\treturn sr.root, sr.error\n}\n\n// Read a node.\nfunc (sr *SmlReader) readNode() (*TagNode, int) {\n\tvar node *TagNode\n\tvar buffer *bytes.Buffer\n\n\tmode := modeInit\n\n\tfor {\n\t\trune, ctrl := sr.readRune()\n\n\t\tsr.index++\n\n\t\tswitch mode {\n\t\tcase modeInit:\n\t\t\t// Before the first opening bracket.\n\t\t\tswitch ctrl {\n\t\t\tcase ctrlEOF:\n\t\t\t\treturn nil, ctrlEOF\n\t\t\tcase ctrlOpen:\n\t\t\t\tmode = modeTag\n\t\t\t\tbuffer = bytes.NewBufferString(\"\")\n\t\t\t}\n\t\tcase modeTag:\n\t\t\t// Reading a tag.\n\t\t\tswitch ctrl {\n\t\t\tcase ctrlEOF:\n\t\t\t\treturn nil, ctrlEOF\n\t\t\tcase ctrlTag:\n\t\t\t\tbuffer.WriteRune(rune)\n\t\t\tcase ctrlSpace:\n\t\t\t\tif buffer.Len() == 0 {\n\t\t\t\t\treturn nil, ctrlInvalid\n\t\t\t\t}\n\n\t\t\t\tnode = NewTagNode(buffer.String())\n\t\t\t\tbuffer = bytes.NewBufferString(\"\")\n\t\t\t\tmode = modeText\n\t\t\tcase ctrlClose:\n\t\t\t\tif buffer.Len() == 0 {\n\t\t\t\t\treturn nil, ctrlInvalid\n\t\t\t\t}\n\n\t\t\t\tnode = NewTagNode(buffer.String())\n\n\t\t\t\treturn node, ctrlClose\n\t\t\tdefault:\n\t\t\t\treturn nil, ctrlInvalid\n\t\t\t}\n\t\tcase modeText:\n\t\t\t// Reading the text including the subnodes following\n\t\t\t// the space after the tag or id.\n\t\t\tswitch ctrl {\n\t\t\tcase ctrlEOF:\n\t\t\t\treturn nil, ctrlEOF\n\t\t\tcase ctrlOpen:\n\t\t\t\ttext := strings.TrimSpace(buffer.String())\n\n\t\t\t\tif len(text) > 0 {\n\t\t\t\t\tnode.AppendText(text)\n\t\t\t\t}\n\n\t\t\t\tbuffer = bytes.NewBufferString(\"\")\n\n\t\t\t\tsr.reader.UnreadRune()\n\n\t\t\t\tsubnode, subctrl := sr.readNode()\n\n\t\t\t\tif subctrl == ctrlClose {\n\t\t\t\t\t// Correct closed subnode.\n\n\t\t\t\t\tnode.AppendTagNode(subnode)\n\t\t\t\t} else {\n\t\t\t\t\t// Error while reading the subnode.\n\n\t\t\t\t\treturn nil, subctrl\n\t\t\t\t}\n\t\t\tcase ctrlClose:\n\t\t\t\ttext := strings.TrimSpace(buffer.String())\n\n\t\t\t\tif len(text) > 0 {\n\t\t\t\t\tnode.AppendText(text)\n\t\t\t\t}\n\n\t\t\t\treturn node, ctrlClose\n\t\t\tcase ctrlEscape:\n\t\t\t\trune, ctrl = sr.readRune()\n\n\t\t\t\tif ctrl == ctrlOpen || ctrl == ctrlClose || ctrl == ctrlEscape {\n\t\t\t\t\tbuffer.WriteRune(rune)\n\n\t\t\t\t\tsr.index++\n\t\t\t\t} else {\n\t\t\t\t\treturn nil, ctrlInvalid\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\tbuffer.WriteRune(rune)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil, ctrlEOF\n}\n\n// Read a rune.\nfunc (sr *SmlReader) readRune() (rune, control int) {\n\tvar size int\n\n\trune, size, sr.error = sr.reader.ReadRune()\n\n\tswitch {\n\tcase size == 0:\n\t\treturn rune, ctrlEOF\n\tcase rune == '{':\n\t\treturn rune, ctrlOpen\n\tcase rune == '}':\n\t\treturn rune, ctrlClose\n\tcase rune == '^':\n\t\treturn rune, ctrlEscape\n\tcase rune >= 'a' && rune <= 'z':\n\t\treturn rune, ctrlTag\n\tcase rune >= 'A' && rune <= 'Z':\n\t\treturn rune, ctrlTag\n\tcase rune >= '0' && rune <= '9':\n\t\treturn rune, ctrlTag\n\tcase rune == '-':\n\t\treturn rune, ctrlTag\n\tcase rune == ':':\n\t\treturn rune, ctrlTag\n\tcase unicode.IsSpace(rune):\n\t\treturn rune, ctrlSpace\n\t}\n\n\treturn rune, ctrlText\n}\n\n//--------------------\n// SML WRITER PROCESSOR\n//--------------------\n\n// Processor for writing SML.\ntype SmlWriterProcessor struct {\n\twriter      *bufio.Writer\n\tprettyPrint bool\n\tindentLevel int\n}\n\n// Create a new SML writer processor.\nfunc NewSmlWriterProcessor(writer io.Writer, prettyPrint bool) *SmlWriterProcessor {\n\tswp := &SmlWriterProcessor{\n\t\twriter:      bufio.NewWriter(writer),\n\t\tprettyPrint: prettyPrint,\n\t\tindentLevel: 0,\n\t}\n\n\treturn swp\n}\n\n// Open a tag.\nfunc (swp *SmlWriterProcessor) OpenTag(tag []string) {\n\tswp.writeIndent(true)\n\n\tswp.writer.WriteString(\"{\")\n\tswp.writer.WriteString(strings.Join(tag, \":\"))\n}\n\n// Close a tag.\nfunc (swp *SmlWriterProcessor) CloseTag(tag []string) {\n\tswp.writer.WriteString(\"}\")\n\n\tif swp.prettyPrint {\n\t\tswp.indentLevel--\n\t}\n\n\tswp.writer.Flush()\n}\n\n// Write a text.\nfunc (swp *SmlWriterProcessor) Text(text string) {\n\tta := strings.Replace(text, \"^\", \"^^\", -1)\n\ttb := strings.Replace(ta, \"{\", \"^{\", -1)\n\ttc := strings.Replace(tb, \"}\", \"^}\", -1)\n\n\tswp.writeIndent(false)\n\n\tswp.writer.WriteString(tc)\n}\n\n// Write an indent in case of pretty print.\nfunc (swp *SmlWriterProcessor) writeIndent(increase bool) {\n\tif swp.prettyPrint {\n\t\tif swp.indentLevel > 0 {\n\t\t\tswp.writer.WriteString(\"\\n\")\n\t\t}\n\n\t\tfor i := 0; i < swp.indentLevel; i++ {\n\t\t\tswp.writer.WriteString(\"\\t\")\n\t\t}\n\n\t\tif increase {\n\t\t\tswp.indentLevel++\n\t\t}\n\t} else {\n\t\tswp.writer.WriteString(\" \")\n\t}\n}\n\n/*\n\tEOF\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_11/tideland-cgl.googlecode.com/hg/cglsmr.go",
    "content": "/*\n\tTideland Common Go Library - Sorting and Map/Reduce\n\n\tCopyright (C) 2009-2011 Frank Mueller / Oldenburg / Germany\n\n\tRedistribution and use in source and binary forms, with or\n\tmodification, are permitted provided that the following conditions are\n\tmet:\n\n\tRedistributions of source code must retain the above copyright notice, this\n\tlist of conditions and the following disclaimer.\n\n\tRedistributions in binary form must reproduce the above copyright notice,\n\tthis list of conditions and the following disclaimer in the documentation\n\tand/or other materials provided with the distribution.\n\n\tNeither the name of Tideland nor the names of its contributors may be\n\tused to endorse or promote products derived from this software without\n\tspecific prior written permission.\n\n\tTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n\tAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n\tIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n\tARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n\tLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n\tCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n\tSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n\tINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n\tCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n\tARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\n\tTHE POSSIBILITY OF SUCH DAMAGE.\n*/\n\npackage cgl\n\n//--------------------\n// IMPORTS\n//--------------------\n\nimport (\n\t\"hash/adler32\"\n\t\"sort\"\n)\n\n//--------------------\n// CONTROL VALUES\n//--------------------\n\n// Threshold for switching from parallel to sequential quick sort.\nvar QuickSortParallelThreshold int = 4095\n\n// Threshold for switching from sequential quick sort to insertion sort.\nvar QuickSortSequentialThreshold int = 63\n\n//--------------------\n// HELPING FUNCS\n//--------------------\n\n// Simple insertion sort for smaller data collections.\nfunc insertionSort(data sort.Interface, lo, hi int) {\n\tfor i := lo + 1; i < hi+1; i++ {\n\t\tfor j := i; j > lo && data.Less(j, j-1); j-- {\n\t\t\tdata.Swap(j, j-1)\n\t\t}\n\t}\n}\n\n// Get the median based on Tukey's ninther.\nfunc median(data sort.Interface, lo, hi int) int {\n\tm := (lo + hi) / 2\n\td := (hi - lo) / 8\n\n\t// Move median into the middle.\n\n\tmot := func(ml, mm, mh int) {\n\t\tif data.Less(mm, ml) {\n\t\t\tdata.Swap(mm, ml)\n\t\t}\n\t\tif data.Less(mh, mm) {\n\t\t\tdata.Swap(mh, mm)\n\t\t}\n\t\tif data.Less(mm, ml) {\n\t\t\tdata.Swap(mm, ml)\n\t\t}\n\t}\n\n\t// Get low, middle, and high median.\n\n\tif hi-lo > 40 {\n\t\tmot(lo+d, lo, lo+2*d)\n\t\tmot(m-d, m, m+d)\n\t\tmot(hi-d, hi, hi-2*d)\n\t}\n\n\t// Get combined median.\n\n\tmot(lo, m, hi)\n\n\treturn m\n}\n\n// Partition the data based on the median.\nfunc partition(data sort.Interface, lo, hi int) (int, int) {\n\tmed := median(data, lo, hi)\n\tidx := lo\n\n\tdata.Swap(med, hi)\n\n\tfor i := lo; i < hi; i++ {\n\t\tif data.Less(i, hi) {\n\t\t\tdata.Swap(i, idx)\n\n\t\t\tidx++\n\t\t}\n\t}\n\n\tdata.Swap(idx, hi)\n\n\treturn idx - 1, idx + 1\n}\n\n// Sequential quicksort using itself recursively.\nfunc sequentialQuickSort(data sort.Interface, lo, hi int) {\n\tif hi-lo > QuickSortSequentialThreshold {\n\t\t// Use sequential quicksort.\n\n\t\tplo, phi := partition(data, lo, hi)\n\n\t\tsequentialQuickSort(data, lo, plo)\n\t\tsequentialQuickSort(data, phi, hi)\n\t} else {\n\t\t// Use insertion sort.\n\n\t\tinsertionSort(data, lo, hi)\n\t}\n}\n\n// Parallel quicksort using itself recursively\n// and concurrent.\nfunc parallelQuickSort(data sort.Interface, lo, hi int, done chan bool) {\n\tif hi-lo > QuickSortParallelThreshold {\n\t\t// Parallel QuickSort.\n\n\t\tplo, phi := partition(data, lo, hi)\n\t\tpartDone := make(chan bool)\n\n\t\tgo parallelQuickSort(data, lo, plo, partDone)\n\t\tgo parallelQuickSort(data, phi, hi, partDone)\n\n\t\t// Wait for the end of both sorts.\n\n\t\t<-partDone\n\t\t<-partDone\n\t} else {\n\t\t// Sequential QuickSort.\n\n\t\tsequentialQuickSort(data, lo, hi)\n\t}\n\n\t// Signal that it's done.\n\n\tdone <- true\n}\n\n//--------------------\n// PARALLEL QUICKSORT\n//--------------------\n\nfunc Sort(data sort.Interface) {\n\tdone := make(chan bool)\n\n\tgo parallelQuickSort(data, 0, data.Len()-1, done)\n\n\t<-done\n}\n\n//--------------------\n// BASIC KEY/VALUE TYPES\n//--------------------\n\n// Data processing is based on key/value pairs.\ntype KeyValue struct {\n\tKey   string\n\tValue interface{}\n}\n\n// Channel for the transfer of key/value pairs.\ntype KeyValueChan chan *KeyValue\n\n// Slice of key/value channels.\ntype KeyValueChans []KeyValueChan\n\n// Map a key/value pair, emit to the channel.\ntype MapFunc func(*KeyValue, KeyValueChan)\n\n// Reduce the key/values of the first channel, emit to the second channel.\ntype ReduceFunc func(KeyValueChan, KeyValueChan)\n\n// Channel for closing signals.\ntype SigChan chan bool\n\n//--------------------\n// HELPING FUNCS\n//--------------------\n\n// Close given channel after a number of signals.\nfunc closeSignalChannel(kvc KeyValueChan, size int) SigChan {\n\tsigChan := make(SigChan)\n\n\tgo func() {\n\t\tctr := 0\n\n\t\tfor {\n\t\t\t<-sigChan\n\n\t\t\tctr++\n\n\t\t\tif ctr == size {\n\t\t\t\tclose(kvc)\n\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n\n\treturn sigChan\n}\n\n// Perform the reducing.\nfunc performReducing(mapEmitChan KeyValueChan, reduceFunc ReduceFunc, reduceSize int, reduceEmitChan KeyValueChan) {\n\t// Start a closer for the reduce emit chan.\n\n\tsigChan := closeSignalChannel(reduceEmitChan, reduceSize)\n\n\t// Start reduce funcs.\n\n\treduceChans := make(KeyValueChans, reduceSize)\n\n\tfor i := 0; i < reduceSize; i++ {\n\t\treduceChans[i] = make(KeyValueChan)\n\n\t\tgo func(inChan KeyValueChan) {\n\t\t\treduceFunc(inChan, reduceEmitChan)\n\n\t\t\tsigChan <- true\n\t\t}(reduceChans[i])\n\t}\n\n\t// Read map emitted data.\n\n\tfor kv := range mapEmitChan {\n\t\thash := adler32.Checksum([]byte(kv.Key))\n\t\tidx := hash % uint32(reduceSize)\n\n\t\treduceChans[idx] <- kv\n\t}\n\n\t// Close reduce channels.\n\n\tfor _, reduceChan := range reduceChans {\n\t\tclose(reduceChan)\n\t}\n}\n\n// Perform the mapping.\nfunc performMapping(mapInChan KeyValueChan, mapFunc MapFunc, mapSize int, mapEmitChan KeyValueChan) {\n\t// Start a closer for the map emit chan.\n\n\tsigChan := closeSignalChannel(mapEmitChan, mapSize)\n\n\t// Start mapping goroutines.\n\n\tmapChans := make(KeyValueChans, mapSize)\n\n\tfor i := 0; i < mapSize; i++ {\n\t\tmapChans[i] = make(KeyValueChan)\n\n\t\tgo func(inChan KeyValueChan) {\n\t\t\tfor kv := range inChan {\n\t\t\t\tmapFunc(kv, mapEmitChan)\n\t\t\t}\n\n\t\t\tsigChan <- true\n\t\t}(mapChans[i])\n\t}\n\n\t// Dispatch input data to map channels.\n\n\tidx := 0\n\n\tfor kv := range mapInChan {\n\t\tmapChans[idx%mapSize] <- kv\n\n\t\tidx++\n\t}\n\n\t// Close mapping channels channel.\n\n\tfor i := 0; i < mapSize; i++ {\n\t\tclose(mapChans[i])\n\t}\n}\n\n//--------------------\n// MAP/REDUCE\n//--------------------\n\n// Simple map/reduce function.\nfunc MapReduce(inChan KeyValueChan, mapFunc MapFunc, mapSize int, reduceFunc ReduceFunc, reduceSize int) KeyValueChan {\n\tmapEmitChan := make(KeyValueChan)\n\treduceEmitChan := make(KeyValueChan)\n\n\t// Perform operations.\n\n\tgo performReducing(mapEmitChan, reduceFunc, reduceSize, reduceEmitChan)\n\tgo performMapping(inChan, mapFunc, mapSize, mapEmitChan)\n\n\treturn reduceEmitChan\n}\n\n//--------------------\n// RESULT SORTING\n//--------------------\n\n// Less function for sorting.\ntype KeyValueLessFunc func(*KeyValue, *KeyValue) bool\n\n// Sortable set of key/value pairs.\ntype SortableKeyValueSet struct {\n\tdata     []*KeyValue\n\tlessFunc KeyValueLessFunc\n}\n\n// Constructor for the sortable set.\nfunc NewSortableKeyValueSet(kvChan KeyValueChan, kvLessFunc KeyValueLessFunc) *SortableKeyValueSet {\n\ts := &SortableKeyValueSet{\n\t\tdata:     make([]*KeyValue, 0, 1024),\n\t\tlessFunc: kvLessFunc,\n\t}\n\n\tfor kv := range kvChan {\n\t\tl := len(s.data)\n\n\t\tif l == cap(s.data) {\n\t\t\ttmp := make([]*KeyValue, l, l+1024)\n\n\t\t\tcopy(tmp, s.data)\n\n\t\t\ts.data = tmp\n\t\t}\n\n\t\ts.data = s.data[0 : l+1]\n\t\ts.data[l] = kv\n\t}\n\n\treturn s\n}\n\n// Sort interface: Return the len of the data.\nfunc (s *SortableKeyValueSet) Len() int {\n\treturn len(s.data)\n}\n\n// Sort interface: Return which element is less.\nfunc (s *SortableKeyValueSet) Less(a, b int) bool {\n\treturn s.lessFunc(s.data[a], s.data[b])\n}\n\n// Sort interface: Swap two elements.\nfunc (s *SortableKeyValueSet) Swap(a, b int) {\n\ts.data[a], s.data[b] = s.data[b], s.data[a]\n}\n\n// Return the data using a channel.\nfunc (s *SortableKeyValueSet) DataChan() KeyValueChan {\n\tkvChan := make(KeyValueChan)\n\n\tgo func() {\n\t\tfor _, kv := range s.data {\n\t\t\tkvChan <- kv\n\t\t}\n\n\t\tclose(kvChan)\n\t}()\n\n\treturn kvChan\n}\n\n// SortedMapReduce performes a map/reduce and sorts the result.\nfunc SortedMapReduce(inChan KeyValueChan, mapFunc MapFunc, mapSize int, reduceFunc ReduceFunc, reduceSize int, lessFunc KeyValueLessFunc) KeyValueChan {\n\tkvChan := MapReduce(inChan, mapFunc, mapSize, reduceFunc, reduceSize)\n\ts := NewSortableKeyValueSet(kvChan, lessFunc)\n\n\tSort(s)\n\n\treturn s.DataChan()\n}\n\n// KeyLessFunc compares the keys of two key/value\n// pairs. It returns true if the key of a is less\n// the key of b.\nfunc KeyLessFunc(a *KeyValue, b *KeyValue) bool {\n\treturn a.Key < b.Key\n}\n\n/*\n\tEOF\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_11/tideland-cgl.googlecode.com/hg/cglsup.go",
    "content": "/*\n\tTideland Common Go Library - Supervision\n\n\tCopyright (C) 2011 Frank Mueller / Oldenburg / Germany\n\n\tRedistribution and use in source and binary forms, with or\n\tmodification, are permitted provided that the following conditions are\n\tmet:\n\n\tRedistributions of source code must retain the above copyright notice, this\n\tlist of conditions and the following disclaimer.\n\n\tRedistributions in binary form must reproduce the above copyright notice,\n\tthis list of conditions and the following disclaimer in the documentation\n\tand/or other materials provided with the distribution.\n\n\tNeither the name of Tideland nor the names of its contributors may be\n\tused to endorse or promote products derived from this software without\n\tspecific prior written permission.\n\n\tTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n\tAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n\tIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n\tARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n\tLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n\tCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n\tSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n\tINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n\tCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n\tARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\n\tTHE POSSIBILITY OF SUCH DAMAGE.\n*/\n\npackage cgl\n\n//--------------------\n// IMPORTS\n//--------------------\n\nimport (\n\t\"log\"\n\t\"os\"\n\t\"time\"\n)\n\n//--------------------\n// GLOBAL VARIABLES\n//--------------------\n\nvar supervisor *Supervisor\n\n//--------------------\n// INIT\n//--------------------\n\nfunc init() {\n\tsupervisor = NewSupervisor(nil)\n}\n\n//--------------------\n// FUNCTIONS\n//--------------------\n\n// Return the global supervisor.\nfunc GlobalSupervisor() *Supervisor {\n\treturn supervisor\n}\n\n//--------------------\n// RECOVERABLE\n//--------------------\n\n// The interface for recoverable types.\ntype Recoverable interface {\n\tSupervisor() *Supervisor\n\tRecover(Recoverable, interface{})\n}\n\n//--------------------\n// SUPERVISOR\n//--------------------\n\n// Message: Add a recoverable for mass recovering.\ntype addRecoverableMsg struct {\n\tid string\n\tr  Recoverable\n}\n\n// Message: Cry for help after an error.\ntype cryForHelpMsg struct {\n\tr   Recoverable\n\terr interface{}\n}\n\n// The supervisor itself.\ntype Supervisor struct {\n\tsupervisor   *Supervisor\n\trecoverables map[string]Recoverable\n\taddChan      chan *addRecoverableMsg\n\thelpChan     chan *cryForHelpMsg\n}\n\n// Create a new supervisor.\nfunc NewSupervisor(parent *Supervisor) *Supervisor {\n\ts := &Supervisor{\n\t\tsupervisor:   parent,\n\t\trecoverables: make(map[string]Recoverable),\n\t\taddChan:      make(chan *addRecoverableMsg),\n\t\thelpChan:     make(chan *cryForHelpMsg),\n\t}\n\n\tgo s.backend()\n\n\treturn s\n}\n\n// Add a recoverable for joint restart in case of an error.\nfunc (s *Supervisor) AddRecoverable(id string, r Recoverable) {\n\ts.addChan <- &addRecoverableMsg{id, r}\n}\n\n// Let a recoverable cry for help at its supervisor.\nfunc (s *Supervisor) Help(r Recoverable, err interface{}) {\n\ts.helpChan <- &cryForHelpMsg{r, err}\n}\n\n// Implement Supervisor() of the recoverable interface for the supervisor itself.\nfunc (s *Supervisor) Supervisor() *Supervisor {\n\treturn s.supervisor\n}\n\n// Implement Recover() of the recoverable interface for the supervisor itself.\nfunc (s *Supervisor) Recover(r Recoverable, err interface{}) {\n\tif s == r {\n\t\tgo s.backend()\n\t}\n}\n\n// Backend goroutine of the supervisor.\nfunc (s *Supervisor) backend() {\n\tdefer func() {\n\t\t// Test for error and cry for help\n\t\t// if needed.\n\t\tHelpIfNeeded(s, recover())\n\t}()\n\n\t// Wait for cries for help.\n\n\tfor {\n\t\tselect {\n\t\tcase add := <-s.addChan:\n\t\t\ts.recoverables[add.id] = add.r\n\t\tcase cfh := <-s.helpChan:\n\t\t\tif len(s.recoverables) > 0 {\n\t\t\t\t// Recover all recoverables.\n\n\t\t\t\tdone := false\n\n\t\t\t\tfor _, recoverable := range s.recoverables {\n\t\t\t\t\trecoverable.Recover(recoverable, cfh.err)\n\n\t\t\t\t\tif recoverable == cfh.r {\n\t\t\t\t\t\tdone = true\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Erroreous recoverable is not registered.\n\n\t\t\t\tif !done {\n\t\t\t\t\tcfh.r.Recover(cfh.r, cfh.err)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Recover the erroreous recoverable.\n\n\t\t\t\tcfh.r.Recover(cfh.r, cfh.err)\n\t\t\t}\n\t\t}\n\t}\n}\n\n//--------------------\n// HEARTBEATABLE\n//--------------------\n\n// The interface for heartbeatable types.\ntype Heartbeatable interface {\n\tRecoverable\n\tSetHearbeat(*Heartbeat)\n}\n\n//--------------------\n// HEARBEAT\n//--------------------\n\n// Heartbeat for one recoverable.\ntype Heartbeat struct {\n\trecoverable   Recoverable\n\tticker        *time.Ticker\n\topenTicks     int64\n\tHeartbeatChan chan *Heartbeat\n\tImAliveChan   chan bool\n}\n\n// Create a new heartbeat.\nfunc NewHeartbeat(r Recoverable, ns int64) *Heartbeat {\n\th := &Heartbeat{\n\t\trecoverable:   r,\n\t\tticker:        time.NewTicker(ns),\n\t\topenTicks:     0,\n\t\tHeartbeatChan: make(chan *Heartbeat),\n\t\tImAliveChan:   make(chan bool),\n\t}\n\n\tgo h.backend()\n\n\treturn h\n}\n\n// Backend goroutine of the heartbeat.\nfunc (h *Heartbeat) backend() {\n\tfor {\n\t\tselect {\n\t\tcase <-h.ticker.C:\n\t\t\t// Check open ticks.\n\t\t\tif h.openTicks > 0 {\n\t\t\t\th.recoverBelated()\n\t\t\t} else {\n\t\t\t\th.sendHeartbeat()\n\t\t\t}\n\t\tcase <-h.ImAliveChan:\n\t\t\t// Reduce number of open ticks.\n\t\t\tif h.openTicks > 0 {\n\t\t\t\th.openTicks--\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Recover a belated recaverable.\nfunc (h *Heartbeat) recoverBelated() {\n\terr := os.NewError(\"Belated recoverable!\")\n\n\tif h.recoverable.Supervisor() != nil {\n\t\t// Cry for help using the supervisor.\n\t\th.recoverable.Supervisor().Help(h.recoverable, err)\n\t} else {\n\t\t// Recover directly.\n\t\th.recoverable.Recover(h.recoverable, err)\n\t}\n\n\th.openTicks = 0\n}\n\n// Send a heartbeat.\nfunc (h *Heartbeat) sendHeartbeat() {\n\tselect {\n\tcase h.HeartbeatChan <- h:\n\t\tbreak\n\tdefault:\n\t\tlog.Printf(\"Heartbeat can't be sent!\")\n\t}\n\n\th.openTicks++\n}\n\n//--------------------\n// CONVENIENCE FUNCTIONS\n//--------------------\n\n// Tell the supervisor to help if\n// the passed error is not nil.\nfunc HelpIfNeeded(r Recoverable, err interface{}) {\n\t// Test for error.\n\tif err != nil {\n\t\t// Test for configured supervisor.\n\t\tif r.Supervisor() != nil {\n\t\t\t// Cry for help.\n\t\t\tr.Supervisor().Help(r, err)\n\t\t}\n\t}\n}\n\n// Send a heartbeat.\nfunc ImAlive(h *Heartbeat) {\n\th.ImAliveChan <- true\n}\n\n/*\n\tEOF\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_11/tideland-cgl.googlecode.com/hg/cgltim.go",
    "content": "/*\n\tTideland Common Go Library - Time and Crontab\n\n\tCopyright (C) 2009-2011 Frank Mueller / Oldenburg / Germany\n\n\tRedistribution and use in source and binary forms, with or\n\tmodification, are permitted provided that the following conditions are\n\tmet:\n\n\tRedistributions of source code must retain the above copyright notice, this\n\tlist of conditions and the following disclaimer.\n\n\tRedistributions in binary form must reproduce the above copyright notice,\n\tthis list of conditions and the following disclaimer in the documentation\n\tand/or other materials provided with the distribution.\n\n\tNeither the name of Tideland nor the names of its contributors may be\n\tused to endorse or promote products derived from this software without\n\tspecific prior written permission.\n\n\tTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n\tAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n\tIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n\tARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n\tLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n\tCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n\tSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n\tINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n\tCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n\tARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\n\tTHE POSSIBILITY OF SUCH DAMAGE.\n*/\n\npackage cgl\n\n//--------------------\n// IMPORTS\n//--------------------\n\nimport (\n\t\"log\"\n\t\"time\"\n)\n\n//--------------------\n// DATE AND TIME\n//--------------------\n\n// Calc nanoseconds from microseconds.\nfunc NsMicroseconds(count int64) int64 { return count * 1e3 }\n\n// Calc nanoseconds from milliseconds.\nfunc NsMilliseconds(count int64) int64 { return NsMicroseconds(count * 1e3) }\n\n// Calc nanoseconds from seconds.\nfunc NsSeconds(count int64) int64 { return NsMilliseconds(count * 1e3) }\n\n// Calc nanoseconds from minutes.\nfunc NsMinutes(count int64) int64 { return NsSeconds(count * 60) }\n\n// Calc nanoseconds from hours.\nfunc NsHours(count int64) int64 { return NsMinutes(count * 60) }\n\n// Calc nanoseconds from days.\nfunc NsDays(count int64) int64 { return NsHours(count * 24) }\n\n// Calc nanoseconds from weeks.\nfunc NsWeeks(count int64) int64 { return NsDays(count * 7) }\n\n// Test if the year of a time is in a given list.\nfunc YearInList(time *time.Time, years []int64) bool {\n\tfor _, year := range years {\n\t\tif time.Year == year {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// Test if a year of a time is in a given range.\nfunc YearInRange(time *time.Time, minYear, maxYear int64) bool {\n\treturn (minYear <= time.Year) && (time.Year <= maxYear)\n}\n\n// Test if the month of a time is in a given list.\nfunc MonthInList(time *time.Time, months []int) bool {\n\treturn fieldInList(time.Month, months)\n}\n\n// Test if a month of a time is in a given range.\nfunc MonthInRange(time *time.Time, minMonth, maxMonth int) bool {\n\treturn fieldInRange(time.Month, minMonth, maxMonth)\n}\n\n// Test if the day of a time is in a given list.\nfunc DayInList(time *time.Time, days []int) bool {\n\treturn fieldInList(time.Day, days)\n}\n\n// Test if a day of a time is in a given range.\nfunc DayInRange(time *time.Time, minDay, maxDay int) bool {\n\treturn fieldInRange(time.Day, minDay, maxDay)\n}\n\n// Test if the hour of a time is in a given list.\nfunc HourInList(time *time.Time, hours []int) bool {\n\treturn fieldInList(time.Hour, hours)\n}\n\n// Test if a hour of a time is in a given range.\nfunc HourInRange(time *time.Time, minHour, maxHour int) bool {\n\treturn fieldInRange(time.Hour, minHour, maxHour)\n}\n\n// Test if the minute of a time is in a given list.\nfunc MinuteInList(time *time.Time, minutes []int) bool {\n\treturn fieldInList(time.Minute, minutes)\n}\n\n// Test if a minute of a time is in a given range.\nfunc MinuteInRange(time *time.Time, minMinute, maxMinute int) bool {\n\treturn fieldInRange(time.Minute, minMinute, maxMinute)\n}\n\n// Test if the second of a time is in a given list.\nfunc SecondInList(time *time.Time, seconds []int) bool {\n\treturn fieldInList(time.Second, seconds)\n}\n\n// Test if a second of a time is in a given range.\nfunc SecondInRange(time *time.Time, minSecond, maxSecond int) bool {\n\treturn fieldInRange(time.Second, minSecond, maxSecond)\n}\n\n// Test if the weekday of a time is in a given list.\nfunc WeekdayInList(time *time.Time, weekdays []int) bool {\n\treturn fieldInList(time.Weekday, weekdays)\n}\n\n// Test if a weekday of a time is in a given range.\nfunc WeekdayInRange(time *time.Time, minWeekday, maxWeekday int) bool {\n\treturn fieldInRange(time.Weekday, minWeekday, maxWeekday)\n}\n\n//--------------------\n// JOB\n//--------------------\n\n// Check function type.\ntype CheckFunc func(*time.Time) (bool, bool)\n\n// Tast function type.\ntype TaskFunc func(string)\n\n// Job type.\ntype Job struct {\n\tid    string\n\tcheck CheckFunc\n\ttask  TaskFunc\n}\n\n// Create a new job.\nfunc NewJob(id string, check CheckFunc, task TaskFunc) *Job {\n\treturn &Job{id, check, task}\n}\n\n// Check if the job has to be performed at a given time.\nfunc (job *Job) checkAndPerform(time *time.Time) bool {\n\tperform, delete := job.check(time)\n\n\tif perform {\n\t\tgo job.task(job.id)\n\t}\n\n\treturn perform && delete\n}\n\n//--------------------\n// CRONTAB\n//--------------------\n\nconst (\n\topJobAdd = iota\n\topJobDel\n\topCrontabStop\n)\n\n// Crontab control type.\ntype crontabControl struct {\n\topCode int\n\targs   interface{}\n}\n\n// Crontab.\ntype Crontab struct {\n\tjobs    map[string]*Job\n\tcontrol chan *crontabControl\n\tticker  *time.Ticker\n}\n\n// Start a crontab server.\nfunc NewCrontab() *Crontab {\n\tctb := &Crontab{\n\t\tjobs:    make(map[string]*Job),\n\t\tcontrol: make(chan *crontabControl),\n\t\tticker:  time.NewTicker(1e9),\n\t}\n\n\tgo ctb.backend()\n\n\treturn ctb\n}\n\n// Stop the server.\nfunc (ctb *Crontab) Stop() {\n\tctb.control <- &crontabControl{opCrontabStop, nil}\n}\n\n// Add a job to the server.\nfunc (ctb *Crontab) AddJob(job *Job) {\n\tctb.control <- &crontabControl{opJobAdd, job}\n}\n\n// Delete a job from the server.\nfunc (ctb *Crontab) DeleteJob(id string) {\n\tctb.control <- &crontabControl{opJobDel, id}\n}\n\n// Return the supervisor.\nfunc (src *Crontab) Supervisor() *Supervisor {\n\treturn GlobalSupervisor()\n}\n\n// Recover after an error.\nfunc (ctb *Crontab) Recover(recoverable Recoverable, err interface{}) {\n\tlog.Printf(\"Recovering crontab backend after error '%v'!\", err)\n\n\tgo ctb.backend()\n}\n\n// Crontab backend.\nfunc (ctb *Crontab) backend() {\n\tdefer func() {\n\t\tHelpIfNeeded(ctb, recover())\n\t}()\n\n\tfor {\n\t\tselect {\n\t\tcase sc := <-ctb.control:\n\t\t\t// Control the server.\n\n\t\t\tswitch sc.opCode {\n\t\t\tcase opJobAdd:\n\t\t\t\tjob := sc.args.(*Job)\n\t\t\t\tctb.jobs[job.id] = job\n\t\t\tcase opJobDel:\n\t\t\t\tid := sc.args.(string)\n\t\t\t\tjob, _ := ctb.jobs[id]\n\t\t\t\tctb.jobs[id] = job, false\n\t\t\tcase opCrontabStop:\n\t\t\t\tctb.ticker.Stop()\n\t\t\t}\n\t\tcase <-ctb.ticker.C:\n\t\t\t// One tick every second.\n\n\t\t\tctb.tick()\n\t\t}\n\t}\n}\n\n// Handle one server tick.\nfunc (ctb *Crontab) tick() {\n\tnow := time.UTC()\n\tdeletes := make(map[string]*Job)\n\n\t// Check and perform jobs.\n\n\tfor id, job := range ctb.jobs {\n\t\tdelete := job.checkAndPerform(now)\n\n\t\tif delete {\n\t\t\tdeletes[id] = job\n\t\t}\n\t}\n\n\t// Delete those marked for deletion.\n\n\tfor id, job := range deletes {\n\t\tctb.jobs[id] = job, false\n\t}\n}\n\n//--------------------\n// HELPERS\n//--------------------\n\n// Test if an int is in a list of ints.\nfunc fieldInList(field int, list []int) bool {\n\tfor _, item := range list {\n\t\tif field == item {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// Test if an int is in a given range.\nfunc fieldInRange(field int, min, max int) bool {\n\treturn (min <= field) && (field <= max)\n}\n\n/*\n\tEOF\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_11/type_interfaces.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n)\n\ntype Square struct {\n\tside float32\n}\n\ntype Circle struct {\n\tradius float32\n}\n\ntype Shaper interface {\n\tArea() float32\n}\n\nfunc main() {\n\tvar areaIntf Shaper\n\tsq1 := new(Square)\n\tsq1.side = 5\n\n\tareaIntf = sq1\n\t// Is Square the type of areaIntf ?\n\tif t, ok := areaIntf.(*Square); ok {\n\t\tfmt.Printf(\"The type of areaIntf is: %T\\n\", t)\n\t}\n\tif u, ok := areaIntf.(*Circle); ok {\n\t\tfmt.Printf(\"The type of areaIntf is: %T\\n\", u)\n\t} else {\n\t\tfmt.Println(\"areaIntf does not contain a variable of type Circle\")\n\t}\n\t// testing with switch:\n\tswitch t := areaIntf.(type) {\n\tcase *Square:\n\t\tfmt.Printf(\"Type Square %T with value %v\\n\", t, t)\n\tcase *Circle:\n\t\tfmt.Printf(\"Type Circle %T with value %v\\n\", t, t)\n\t/*\n\t\t\t\tcase bool:\n\t\t   \t\t\tfmt.Printf(\"Type boolean %t\\n\", t)\n\t\t\t\tcase int:\n\t\t   \t\t\tfmt.Printf(\"Type int %d\\n\", t)\n\t\t\t\tcase *bool:\n\t\t   \t\t\tfmt.Printf(\"Type pointer to boolean %t\\n\", *t)\n\t\t\t\tcase *int:\n\t\t   \t\t\tfmt.Printf(\"Type pointer to int %d\\n\", *t)\n\t*/\n\tdefault:\n\t\tfmt.Printf(\"Unexpected type %T\", t)\n\t}\n}\n\nfunc (sq *Square) Area() float32 {\n\treturn sq.side * sq.side\n}\n\nfunc (ci *Circle) Area() float32 {\n\treturn ci.radius * ci.radius * math.Pi\n}\n"
  },
  {
    "path": "eBook/examples/chapter_12/cat.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n)\n\nfunc cat(r *bufio.Reader) {\n\tfor {\n\t\tbuf, err := r.ReadBytes('\\n')\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t}\n\t\tfmt.Fprintf(os.Stdout, \"%s\", buf)\n\t}\n\treturn\n}\n\nfunc main() {\n\tflag.Parse()\n\tif flag.NArg() == 0 {\n\t\tcat(bufio.NewReader(os.Stdin))\n\t}\n\tfor i := 0; i < flag.NArg(); i++ {\n\t\tf, err := os.Open(flag.Arg(i))\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"%s:error reading from %s: %s\\n\", os.Args[0], flag.Arg(i), err.Error())\n\t\t\tcontinue\n\t\t}\n\t\tcat(bufio.NewReader(f))\n\t\tf.Close()\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_12/cat2.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n)\n\nfunc cat(f *os.File) {\n\tconst NBUF = 512\n\tvar buf [NBUF]byte\n\tfor {\n\t\tswitch nr, err := f.Read(buf[:]); true {\n\t\tcase nr < 0:\n\t\t\tfmt.Fprintf(os.Stderr, \"cat: error reading: %s\\n\", err.Error())\n\t\t\tos.Exit(1)\n\t\tcase nr == 0: // EOF\n\t\t\treturn\n\t\tcase nr > 0:\n\t\t\tif nw, ew := os.Stdout.Write(buf[0:nr]); nw != nr {\n\t\t\t\tfmt.Fprintf(os.Stderr, \"cat: error writing: %s\\n\", ew.Error())\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc main() {\n\tflag.Parse() // Scans the arg list and sets up flags\n\tif flag.NArg() == 0 {\n\t\tcat(os.Stdin)\n\t}\n\tfor i := 0; i < flag.NArg(); i++ {\n\t\tf, err := os.Open(flag.Arg(i))\n\t\tif f == nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"cat: can't open %s: error %s\\n\", flag.Arg(i), err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\tcat(f)\n\t\tf.Close()\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_12/echo.go",
    "content": "package main\n\nimport (\n\t\"flag\" // command line option parser\n\t\"os\"\n)\n\nvar NewLine = flag.Bool(\"n\", false, \"print newline\") // echo -n flag, of type *bool\n\nconst (\n\tSpace   = \" \"\n\tNewline = \"\\n\"\n)\n\nfunc main() {\n\tflag.PrintDefaults()\n\tflag.Parse() // Scans the arg list and sets up flags\n\tvar s string = \"\"\n\tfor i := 0; i < flag.NArg(); i++ {\n\t\tif i > 0 {\n\t\t\ts += \" \"\n\t\t\tif *NewLine { // -n is parsed, flag becomes true\n\t\t\t\ts += Newline\n\t\t\t}\n\t\t}\n\t\ts += flag.Arg(i)\n\t}\n\tos.Stdout.WriteString(s)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_12/filecopy.go",
    "content": "// filecopy.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n)\n\nfunc main() {\n\tCopyFile(\"target.txt\", \"source.txt\")\n\tfmt.Println(\"Copy done!\")\n}\n\nfunc CopyFile(dstName, srcName string) (written int64, err error) {\n\tsrc, err := os.Open(srcName)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer src.Close()\n\n\tdst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0644)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer dst.Close()\n\n\treturn io.Copy(dst, src)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_12/fileinput.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n)\n\nfunc main() {\n\t// var inputFile *os.File\n\t// var inputError, readerError os.Error\n\t// var inputReader *bufio.Reader\n\t// var inputString string\n\n\tinputFile, inputError := os.Open(\"input.dat\")\n\tif inputError != nil {\n\t\tfmt.Printf(\"An error occurred on opening the inputfile\\n\" +\n\t\t\t\"Does the file exist?\\n\" +\n\t\t\t\"Have you got access to it?\\n\")\n\t\treturn // exit the function on error\n\t}\n\tdefer inputFile.Close()\n\n\tinputReader := bufio.NewReader(inputFile)\n\n\tfor {\n\t\tinputString, readerError := inputReader.ReadString('\\n')\n\t\tfmt.Printf(\"The input was: %s\", inputString)\n\t\tif readerError == io.EOF {\n\t\t\treturn // error or EOF\n\t\t}\t\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_12/fileoutput.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"os\"\n)\n\nfunc main() {\n\t// var outputWriter *bufio.Writer\n\t// var outputFile *os.File\n\t// var outputError os.Error\n\t// var outputString string\n\toutputFile, outputError := os.OpenFile(\"output.dat\", os.O_WRONLY|os.O_CREATE, 0666)\n\tif outputError != nil {\n\t\tfmt.Printf(\"An error occurred with file opening or creation\\n\")\n\t\treturn\n\t}\n\tdefer outputFile.Close()\n\n\toutputWriter := bufio.NewWriter(outputFile)\n\toutputString := \"hello world!\\n\"\n\n\tfor i := 0; i < 10; i++ {\n\t\toutputWriter.WriteString(outputString)\n\t}\n\toutputWriter.Flush()\n}\n"
  },
  {
    "path": "eBook/examples/chapter_12/filewrite.go",
    "content": "package main\n\nimport \"os\"\n\nfunc main() {\n\tos.Stdout.WriteString(\"hello, world\\n\")\n\tf, _ := os.OpenFile(\"test\", os.O_CREATE|os.O_WRONLY, 0)\n\tdefer f.Close()\n\tf.WriteString(\"hello, world in a file\\n\")\n}\n"
  },
  {
    "path": "eBook/examples/chapter_12/gob1.go",
    "content": "// gob1.go\npackage main\n\nimport (\n\t\"bytes\"\n\t\"encoding/gob\"\n\t\"fmt\"\n\t\"log\"\n)\n\ntype P struct {\n\tX, Y, Z int\n\tName    string\n}\n\ntype Q struct {\n\tX, Y *int32\n\tName string\n}\n\nfunc main() {\n\t// Initialize the encoder and decoder.  Normally enc and dec would be\n\t// bound to network connections and the encoder and decoder would\n\t// run in different processes.\n\tvar network bytes.Buffer        // Stand-in for a network connection\n\tenc := gob.NewEncoder(&network) // Will write to network.\n\tdec := gob.NewDecoder(&network) // Will read from network.\n\t// Encode (send) the value.\n\terr := enc.Encode(P{3, 4, 5, \"Pythagoras\"})\n\tif err != nil {\n\t\tlog.Fatal(\"encode error:\", err)\n\t}\n\t// Decode (receive) the value.\n\tvar q Q\n\terr = dec.Decode(&q)\n\tif err != nil {\n\t\tlog.Fatal(\"decode error:\", err)\n\t}\n\tfmt.Printf(\"%q: {%d,%d}\\n\", q.Name, q.X, q.Y)\n}\n\n// Output:   \"Pythagoras\": {3,4}\n"
  },
  {
    "path": "eBook/examples/chapter_12/gob2.go",
    "content": "// gob2.go\npackage main\n\nimport (\n\t\"encoding/gob\"\n\t\"log\"\n\t\"os\"\n)\n\ntype Address struct {\n\tType    string\n\tCity    string\n\tCountry string\n}\n\ntype VCard struct {\n\tFirstName string\n\tLastName  string\n\tAddresses []*Address\n\tRemark    string\n}\n\nvar content string\n\nfunc main() {\n\tpa := &Address{\"private\", \"Aartselaar\", \"Belgium\"}\n\twa := &Address{\"work\", \"Boom\", \"Belgium\"}\n\tvc := VCard{\"Jan\", \"Kersschot\", []*Address{pa, wa}, \"none\"}\n\t// fmt.Printf(\"%v: \\n\", vc) // {Jan Kersschot [0x126d2b80 0x126d2be0] none}:\n\t// using an encoder:\n\tfile, _ := os.OpenFile(\"vcard.gob\", os.O_CREATE|os.O_WRONLY, 0666)\n\tdefer file.Close()\n\tenc := gob.NewEncoder(file)\n\terr := enc.Encode(vc)\n\tif err != nil {\n\t\tlog.Println(\"Error in encoding gob\")\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_12/gzipped.go",
    "content": "// gzipped.go\npackage main\n\nimport (\n\t\"bufio\"\n\t\"compress/gzip\"\n\t\"fmt\"\n\t\"os\"\n)\n\nfunc main() {\n\tfName := \"MyFile.gz\"\n\tvar r *bufio.Reader\n\tfi, err := os.Open(fName)\n\tif err != nil {\n\t\tfmt.Fprintf(os.Stderr, \"%v, Can't open %s: error: %s\\n\", os.Args[0], fName,\n\t\t\terr)\n\t\tos.Exit(1)\n\t}\n\tdefer fi.Close()\n\tfz, err := gzip.NewReader(fi)\n\tif err != nil {\n\t\tr = bufio.NewReader(fi)\n\t} else {\n\t\tr = bufio.NewReader(fz)\n\t}\n\n\tfor {\n\t\tline, err := r.ReadString('\\n')\n\t\tif err != nil {\n\t\t\tfmt.Println(\"Done reading file\")\n\t\t\tos.Exit(0)\n\t\t}\n\t\tfmt.Println(line)\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_12/hash_sha1.go",
    "content": "// hash_sha1.go\npackage main\n\nimport (\n\t\"crypto/sha1\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n)\n\nfunc main() {\n\thasher := sha1.New()\n\tio.WriteString(hasher, \"test\")\n\tb := []byte{}\n\tfmt.Printf(\"Result: %x\\n\", hasher.Sum(b))\n\tfmt.Printf(\"Result: %d\\n\", hasher.Sum(b))\n\t//\n\thasher.Reset()\n\tdata := []byte(\"We shall overcome!\")\n\tn, err := hasher.Write(data)\n\tif n != len(data) || err != nil {\n\t\tlog.Printf(\"Hash write error: %v / %v\", n, err)\n\t}\n\tchecksum := hasher.Sum(b)\n\tfmt.Printf(\"Result: %x\\n\", checksum)\n}\n\n/* Output:\nResult: a94a8fe5ccb19ba61c4c0873d391e987982fbbd3\nResult: [169 74 143 229 204 177 155 166 28 76 8 115 211 145 233 135 152 47 187 211]\nResult: e2222bfc59850bbb00a722e764a555603bb59b2a\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_12/io_interfaces.go",
    "content": "// interfaces being used in the GO-package fmt\npackage main\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"os\"\n)\n\nfunc main() {\n\t// unbuffered\n\tfmt.Fprintf(os.Stdout, \"%s\\n\", \"hello world! - unbuffered\")\n\t// buffered: os.Stdout implements io.Writer\n\tbuf := bufio.NewWriter(os.Stdout)\n\t// and now so does buf.\n\tfmt.Fprintf(buf, \"%s\\n\", \"hello world! - buffered\")\n\tbuf.Flush()\n}\n"
  },
  {
    "path": "eBook/examples/chapter_12/json.go",
    "content": "// json.go\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n)\n\ntype Address struct {\n\tType    string\n\tCity    string\n\tCountry string\n}\n\ntype VCard struct {\n\tFirstName string\n\tLastName  string\n\tAddresses []*Address\n\tRemark    string\n}\n\nfunc main() {\n\tpa := &Address{\"private\", \"Aartselaar\", \"Belgium\"}\n\twa := &Address{\"work\", \"Boom\", \"Belgium\"}\n\tvc := VCard{\"Jan\", \"Kersschot\", []*Address{pa, wa}, \"none\"}\n\t// fmt.Printf(\"%v: \\n\", vc) // {Jan Kersschot [0x126d2b80 0x126d2be0] none}:\n\t// JSON format:\n\tjs, _ := json.Marshal(vc)\n\tfmt.Printf(\"JSON format: %s\", js)\n\t// using an encoder:\n\tfile, _ := os.OpenFile(\"vcard.json\", os.O_CREATE|os.O_WRONLY, 0)\n\tdefer file.Close()\n\tenc := json.NewEncoder(file)\n\terr := enc.Encode(vc)\n\tif err != nil {\n\t\tlog.Println(\"Error in encoding json\")\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_12/json_xml_case.go",
    "content": "// json_xml_case.go\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"log\"\n\t\"strings\"\n)\n\ntype thing struct {\n\tField1 int\n\tField2 string\n}\n\nfunc main() {\n\tx := `<x><field1>423</field1><field2>hello from xml</field2></x>`\n\tj := `{\"field1\": 423, \"field2\": \"hello from json\"}`\n\n\ttx := thing{}\n\tif err := xml.Unmarshal(strings.NewReader(x), &tx); err != nil {\n\t\tlog.Fatalf(\"Error unmarshaling XML: %v\", err)\n\t}\n\n\ttj := thing{}\n\tif err := json.Unmarshal([]byte(j), &tj); err != nil {\n\t\tlog.Fatalf(\"Error unmarshaling JSON: %v\", err)\n\t}\n\n\tfmt.Printf(\"From JSON: %#v\\n\", tj)\n\tfmt.Printf(\"From XML: %#v\\n\", tx)\n\n}\n\n/* Output with\ntype thing struct {\n\tField1 int\n\tField2 string\n}:\n\nFrom XML: main.thing{Field1:0, Field2:\"\"}   // All matching is case sensitive!\nFrom JSON: main.thing{Field1:423, Field2:\"hello from json\"}\n\nOutput with\ntype thing struct {\n\tfield1 int\n\tfield2 string\n}:\n\n2012/02/22 10:51:11 Error unmarshaling JSON: json: cannot unmarshal object\nfield1\" into unexported field field1 of type main.thing\n\nJSON uses reflection to unmarshal!\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_12/os_args.go",
    "content": "// os_args.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n)\n\nfunc main() {\n\twho := \"Alice \"\n\tif len(os.Args) > 1 {\n\t\twho += strings.Join(os.Args[1:], \" \")\n\t}\n\tfmt.Println(\"Good Morning\", who)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_12/products.txt",
    "content": "ABC|The ABC of Ruby|40\r\nFUNC|Functional languages|56\r\nGO|The Go Way|45\r\n"
  },
  {
    "path": "eBook/examples/chapter_12/products2.txt",
    "content": "ABC 40 150\r\nFUNC 56 280\r\nGO 45 356\r\n"
  },
  {
    "path": "eBook/examples/chapter_12/products_copy.txt",
    "content": "ABC|The ABC of Ruby|40\r\nFUNC|Functional languages|56\r\nGO|The Go Way|45\r\n"
  },
  {
    "path": "eBook/examples/chapter_12/read_file2.go",
    "content": "// read_csvfile.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t//\t\"io/ioutil\"\n\t//\t\"strings\"\n)\n\nfunc main() {\n\tfile, err := os.Open(\"products2.txt\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer file.Close()\n\n\tvar col1, col2, col3 []string\n\tfor {\n\t\tvar v1, v2, v3 string\n\t\t_, err := fmt.Fscanln(file, &v1, &v2, &v3)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tcol1 = append(col1, v1)\n\t\tcol2 = append(col2, v2)\n\t\tcol3 = append(col3, v3)\n\t}\n\n\tfmt.Println(col1)\n\tfmt.Println(col2)\n\tfmt.Println(col3)\n}\n\n/* Output:\n[ABC FUNC GO]\n[40 56 45]\n[150 280 356]\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_12/read_files.go",
    "content": "// read_files.go\npackage main\n\nimport (\n\t\"bufio\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n)\n\nfunc main() {\n\tfmt.Printf(\"Reading files...\\n\")\n\tflag.Parse()\n\n\tfor i := 0; i < flag.NArg(); i++ {\n\t\tfmt.Printf(\"[File: %v]\\n\", flag.Arg(i))\n\t\tfin, err := os.Open(flag.Arg(i))\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"The file %v does not exist!\\n\", flag.Arg(i))\n\t\t\tbreak\n\t\t}\n\t\tr := bufio.NewReader(fin)\n\t\tfor line, _, err := r.ReadLine(); err != io.EOF; line, _, err = r.ReadLine() {\n\t\t\tfmt.Printf(\"Lines: %v (error %v)\\n\", string(line), err)\n\t\t}\n\t\tfin.Close()\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_12/read_write_file1.go",
    "content": "// read_write_file.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"io/ioutil\"\n)\n\nfunc main() {\n\tinputFile := \"products.txt\"\n\toutputFile := \"products_copy.txt\"\n\tbuf, err := ioutil.ReadFile(inputFile)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%s\\n\", string(buf))\n\terr = ioutil.WriteFile(outputFile, buf, 0644) // oct, not hex\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_12/readinput1.go",
    "content": "// read input from the console:\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nvar (\n\tfirstName, lastName, s string\n\ti                      int\n\tf                      float32\n\tinput                  = \"56.12 / 5212 / Go\"\n\tformat                 = \"%f / %d / %s\"\n)\n\nfunc main() {\n\tfmt.Println(\"Please enter your full name: \")\n\tfmt.Scanln(&firstName, &lastName)\n\t// fmt.Scanf(\"%s %s\", &firstName, &lastName)\n\tfmt.Printf(\"Hi %s %s!\\n\", firstName, lastName) // Hi Chris Naegels\n\n\tfmt.Sscanf(input, format, &f, &i, &s)\n\tfmt.Println(\"From the string we read: \", f, i, s) // From the string we read:  56.12 5212 Go\n}\n"
  },
  {
    "path": "eBook/examples/chapter_12/readinput2.go",
    "content": "// read input from the console:\npackage main\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"os\"\n)\n\nvar inputReader *bufio.Reader\nvar input string\nvar err error\n\nfunc main() {\n\tinputReader = bufio.NewReader(os.Stdin) // reader for input\n\tfmt.Println(\"Please enter some input: \")\n\tinput, err = inputReader.ReadString('\\n')\n\n\tif err == nil {\n\t\tfmt.Printf(\"The input was: %s\\n\", input)\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_12/source.txt",
    "content": "ABC|The ABC of Ruby|40\r\nFUNC|Functional languages|56\r\nGO|The Go Way|45\r\n"
  },
  {
    "path": "eBook/examples/chapter_12/switch_input.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"os\"\n)\n\nfunc main() {\n\tinputReader := bufio.NewReader(os.Stdin)\n\tfmt.Println(\"Please enter your name:\")\n\tinput, err := inputReader.ReadString('\\n')\n\n\tif err != nil {\n\t\tfmt.Println(\"There were errors reading, exiting program.\")\n\t\treturn\n\t}\n\n\tfmt.Printf(\"Your name is %s\", input)\n\t// For Unix: test with delimiter \"\\n\", for Windows: test with \"\\r\\n\"\n\tswitch input {\n\tcase \"Philip\\r\\n\":\n\t\tfmt.Println(\"Welcome Philip!\")\n\tcase \"Chris\\r\\n\":\n\t\tfmt.Println(\"Welcome Chris!\")\n\tcase \"Ivo\\r\\n\":\n\t\tfmt.Println(\"Welcome Ivo!\")\n\tdefault:\n\t\tfmt.Printf(\"You are not welcome here! Goodbye!\")\n\t}\n\n\t// version 2:\n\tswitch input {\n\tcase \"Philip\\r\\n\":\n\t\tfallthrough\n\tcase \"Ivo\\r\\n\":\n\t\tfallthrough\n\tcase \"Chris\\r\\n\":\n\t\tfmt.Printf(\"Welcome %s\\n\", input)\n\tdefault:\n\t\tfmt.Printf(\"You are not welcome here! Goodbye!\\n\")\n\t}\n\n\t// version 3:\n\tswitch input {\n\tcase \"Philip\\r\\n\", \"Ivo\\r\\n\":\n\t\tfmt.Printf(\"Welcome %s\\n\", input)\n\tdefault:\n\t\tfmt.Printf(\"You are not welcome here! Goodbye!\\n\")\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_12/target.txt",
    "content": "ABC|The ABC of Ruby|40\r\nFUNC|Functional languages|56\r\nGO|The Go Way|45\r\n"
  },
  {
    "path": "eBook/examples/chapter_12/test",
    "content": "hello, world in a file\n"
  },
  {
    "path": "eBook/examples/chapter_12/vcard.json",
    "content": "{\"FirstName\":\"Jan\",\"LastName\":\"Kersschot\",\"Addresses\":[{\"Type\":\"private\",\"City\":\"Aartselaar\",\"Country\":\"Belgium\"},{\"Type\":\"work\",\"City\":\"Boom\",\"Country\":\"Belgium\"}],\"Remark\":\"none\"}\n"
  },
  {
    "path": "eBook/examples/chapter_12/xml.go",
    "content": "// xml.go\npackage main\n\nimport (\n\t\"encoding/xml\"\n\t\"fmt\"\n\t\"strings\"\n)\n\nvar t, token xml.Token\nvar err error\n\nfunc main() {\n\tinput := \"<Person><FirstName>Laura</FirstName><LastName>Lynn</LastName></Person>\"\n\tinputReader := strings.NewReader(input)\n\tp := xml.NewDecoder(inputReader)\n\n\tfor t, err = p.Token(); err == nil; t, err = p.Token() {\n\t\tswitch token := t.(type) {\n\t\tcase xml.StartElement:\n\t\t\tname := token.Name.Local\n\t\t\tfmt.Printf(\"Token name: %s\\n\", name)\n\t\t\tfor _, attr := range token.Attr {\n\t\t\t\tattrName := attr.Name.Local\n\t\t\t\tattrValue := attr.Value\n\t\t\t\tfmt.Printf(\"An attribute is: %s %s\\n\", attrName, attrValue)\n\t\t\t\t// ...\n\t\t\t}\n\t\tcase xml.EndElement:\n\t\t\tfmt.Println(\"End of token\")\n\t\tcase xml.CharData:\n\t\t\tcontent := string([]byte(token))\n\t\t\tfmt.Printf(\"This is the content: %v\\n\", content)\n\t\t\t// ...\n\t\tdefault:\n\t\t\t// ...\n\t\t}\n\t}\n}\n\n/* Output:\nToken name: Person\nToken name: FirstName\nThis is the content: Laura\nEnd of token\nToken name: LastName\nThis is the content: Lynn\nEnd of token\nEnd of token\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_13/errors.go",
    "content": "// errors.go\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nvar errNotFound error = errors.New(\"Not found error\")\n\nfunc main() {\n\tfmt.Printf(\"error: %v\", errNotFound)\n}\n\n// error: Not found error\n"
  },
  {
    "path": "eBook/examples/chapter_13/even/even/even.go",
    "content": "// even.go\npackage even\n\nfunc Even(i int) bool { // Exported function\n\treturn i%2 == 0\n}\n\nfunc Odd(i int) bool { // Exported function\n\treturn i%2 != 0\n}\n"
  },
  {
    "path": "eBook/examples/chapter_13/even/even/oddeven_test.go",
    "content": "// oddeven_test.go\npackage even\n\nimport \"testing\"\n\nfunc TestEven(t *testing.T) {\n\tif !Even(10) {\n\t\tt.Log(\" 10 must be even!\")\n\t\tt.Fail()\n\t}\n\tif Even(7) {\n\t\tt.Log(\" 7 is not even!\")\n\t\tt.Fail()\n\t}\n\n}\n\nfunc TestOdd(t *testing.T) {\n\tif !Odd(11) {\n\t\tt.Log(\" 11 must be odd!\")\n\t\tt.Fail()\n\t}\n\tif Odd(10) {\n\t\tt.Log(\" 10 is not odd!\")\n\t\tt.Fail()\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_13/even/even_main/even_main.go",
    "content": "// test_oddeven.go\npackage main\n\nimport (\n\t\"even/even\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tfor i := 0; i <= 100; i++ {\n\t\tfmt.Printf(\"Is the integer %d even? %v\\n\", i, even.Even(i))\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_13/exec.go",
    "content": "// exec.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n)\n\nfunc main() {\n\t// 1) os.StartProcess //\n\t/*********************/\n\t/* Linux: */\n\tenv := os.Environ()\n\tprocAttr := &os.ProcAttr{\n\t\tEnv: env,\n\t\tFiles: []*os.File{\n\t\t\tos.Stdin,\n\t\t\tos.Stdout,\n\t\t\tos.Stderr,\n\t\t},\n\t}\n\t// 1st example: list files\n\tpid, err := os.StartProcess(\"/bin/ls\", []string{\"ls\", \"-l\"}, procAttr)\n\tif err != nil {\n\t\tfmt.Printf(\"Error %v starting process!\", err) //\n\t\tos.Exit(1)\n\t}\n\tfmt.Printf(\"The process id is %v\", pid)\n\t// 2nd example: show all processes\n\tpid, err = os.StartProcess(\"/bin/ps\", []string{\"-e\", \"-opid,ppid,comm\"}, procAttr)\n\tif err != nil {\n\t\tfmt.Printf(\"Error %v starting process!\", err) //\n\t\tos.Exit(1)\n\t}\n\tfmt.Printf(\"The process id is %v\", pid)\n\t/* Output 1st:\n\t   The process id is &{2054 0}total 2056\n\t   -rwxr-xr-x 1 ivo ivo 1157555 2011-07-04 16:48 Mieken_exec\n\t   -rw-r--r-- 1 ivo ivo    2124 2011-07-04 16:48 Mieken_exec.go\n\t   -rw-r--r-- 1 ivo ivo   18528 2011-07-04 16:48 Mieken_exec_go_.6\n\t   -rwxr-xr-x 1 ivo ivo  913920 2011-06-03 16:13 panic.exe\n\t   -rw-r--r-- 1 ivo ivo     180 2011-04-11 20:39 panic.go\n\t*/\n\n\t// 2) exec.Run //\n\t/***************/\n\t// Linux:  OK, but not for ls ?\n\t// cmd := exec.Command(\"ls\", \"-l\")  // no error, but doesn't show anything ?\n\t// cmd := exec.Command(\"ls\")  \t\t// no error, but doesn't show anything ?\n\tcmd := exec.Command(\"gedit\") // this opens a gedit-window\n\terr = cmd.Run()\n\tif err != nil {\n\t\tfmt.Printf(\"Error %v executing command!\", err)\n\t\tos.Exit(1)\n\t}\n\tfmt.Printf(\"The command is %v\", cmd)\n\t// The command is &{/bin/ls [ls -l] []  <nil> <nil> <nil> 0xf840000210 <nil> true [0xf84000ea50 0xf84000e9f0 0xf84000e9c0] [0xf84000ea50 0xf84000e9f0 0xf84000e9c0] [] [] 0xf8400128c0}\n}\n\n// in Windows: uitvoering: Error fork/exec /bin/ls: The system cannot find the path specified. starting process!\n"
  },
  {
    "path": "eBook/examples/chapter_13/panic.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"Starting the program\")\n\tpanic(\"A severe error occurred: stopping the program!\")\n\tfmt.Println(\"Ending the program\")\n}\n"
  },
  {
    "path": "eBook/examples/chapter_13/panic_package.go",
    "content": "// panic_package.go\npackage main\n\nimport (\n\t\"./parse/parse\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tvar examples = []string{\n\t\t\"1 2 3 4 5\",\n\t\t\"100 50 25 12.5 6.25\",\n\t\t\"2 + 2 = 4\",\n\t\t\"1st class\",\n\t\t\"\",\n\t}\n\n\tfor _, ex := range examples {\n\t\tfmt.Printf(\"Parsing %q:\\n  \", ex)\n\t\tnums, err := parse.Parse(ex)\n\t\tif err != nil {\n\t\t\tfmt.Println(err) // here String() method from ParseError is used\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Println(nums)\n\t}\n}\n\n/* Output:\nParsing \"1 2 3 4 5\":\n  [1 2 3 4 5]\nParsing \"100 50 25 12.5 6.25\":\n  pkg parse: error parsing \"12.5\" as int\nParsing \"2 + 2 = 4\":\n  pkg parse: error parsing \"+\" as int\nParsing \"1st class\":\n  pkg parse: error parsing \"1st\" as int\nParsing \"\":\n  pkg: no words to parse\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_13/panic_recover.go",
    "content": "// panic_recover.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc badCall() {\n\tpanic(\"bad end\")\n}\n\nfunc test() {\n\tdefer func() {\n\t\tif e := recover(); e != nil {\n\t\t\tfmt.Printf(\"Panicing %s\\r\\n\", e)\n\t\t}\n\t}()\n\tbadCall()\n\tfmt.Printf(\"After bad call\\r\\n\") // <-- wordt niet bereikt\n}\n\nfunc main() {\n\tfmt.Printf(\"Calling test\\r\\n\")\n\ttest()\n\tfmt.Printf(\"Test completed\\r\\n\")\n}\n\n/* Output:\nCalling test\nPanicing bad end\nTest completed\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_13/parse/parse.go",
    "content": "// parse.go\npackage parse\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// A ParseError indicates an error in converting a word into an integer.\ntype ParseError struct {\n\tIndex int    // The index into the space-separated list of words.\n\tWord  string // The word that generated the parse error.\n\tErr   error  // The raw error that precipitated this error, if any.\n}\n\n// String returns a human-readable error message.\nfunc (e *ParseError) String() string {\n\treturn fmt.Sprintf(\"pkg parse: error parsing %q as int\", e.Word)\n}\n\n// Parse parses the space-separated words in in put as integers.\nfunc Parse(input string) (numbers []int, err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tvar ok bool\n\t\t\terr, ok = r.(error)\n\t\t\tif !ok {\n\t\t\t\terr = fmt.Errorf(\"pkg: %v\", r)\n\t\t\t}\n\t\t}\n\t}()\n\n\tfields := strings.Fields(input)\n\tnumbers = fields2numbers(fields)\n\treturn\n}\n\nfunc fields2numbers(fields []string) (numbers []int) {\n\tif len(fields) == 0 {\n\t\tpanic(\"no words to parse\")\n\t}\n\tfor idx, field := range fields {\n\t\tnum, err := strconv.Atoi(field)\n\t\tif err != nil {\n\t\t\tpanic(&ParseError{idx, field, err})\n\t\t}\n\t\tnumbers = append(numbers, num)\n\t}\n\treturn\n}\n"
  },
  {
    "path": "eBook/examples/chapter_13/xtime",
    "content": "#!/bin/sh\r\n/usr/bin/time -f '%Uu %Ss %er %MkB %C' \"$@\""
  },
  {
    "path": "eBook/examples/chapter_14/benchmark_channels.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nfunc main() {\n\tfmt.Println(\" sync\", testing.Benchmark(BenchmarkChannelSync).String())\n\tfmt.Println(\"buffered\", testing.Benchmark(BenchmarkChannelBuffered).String())\n}\n\nfunc BenchmarkChannelSync(b *testing.B) {\n\tch := make(chan int)\n\tgo func() {\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\tch <- i\n\t\t}\n\t\tclose(ch)\n\t}()\n\tfor range ch {\n\t}\n}\n\nfunc BenchmarkChannelBuffered(b *testing.B) {\n\tch := make(chan int, 128)\n\tgo func() {\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\tch <- i\n\t\t}\n\t\tclose(ch)\n\t}()\n\tfor range ch {\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_14/chaining.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n)\n\nvar ngoroutine = flag.Int(\"n\", 100000, \"how many goroutines\")\n\nfunc f(left, right chan int) { left <- 1 + <-right }\n\nfunc main() {\n\tflag.Parse()\n\tleftmost := make(chan int)\n\tvar left, right chan int = nil, leftmost\n\tfor i := 0; i < *ngoroutine; i++ {\n\t\tleft, right = right, make(chan int)\n\t\tgo f(left, right)\n\t}\n\tright <- 0      // bang!\n\tx := <-leftmost // wait for completion\n\tfmt.Println(x)  // 100000, ongeveer 1,5 s\n}\n"
  },
  {
    "path": "eBook/examples/chapter_14/channel_block.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nvar cnt = 0\n\nfunc main() {\n\tch1 := make(chan int)\n\tgo pump(ch1)       // pump hangs\n\tfmt.Println(<-ch1) // prints only 0\n\n\ttime.Sleep(time.Second)\n\tfmt.Println(cnt) // prints 1\n\n}\n\nfunc pump(ch chan int) {\n\tfor i := 0; ; i++ {\n\t\tch <- i // the channel will be block due to lack of consumer\n\t\tcnt++   // this code will only execute once\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_14/channel_block2.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tch1 := make(chan int)\n\tgo pump(ch1)\n\tgo suck(ch1) // tons of numbers appear\n\ttime.Sleep(1e9)\n}\n\nfunc pump(ch chan int) {\n\tfor i := 0; ; i++ {\n\t\tch <- i\n\t}\n}\n\nfunc suck(ch chan int) {\n\tfor {\n\t\tfmt.Println(<-ch)\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_14/channel_idiom.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tstream := pump()\n\tgo suck(stream)\n\ttime.Sleep(1e9)\n}\n\nfunc pump() chan int {\n\tch := make(chan int)\n\tgo func() {\n\t\tfor i := 0; ; i++ {\n\t\t\tch <- i\n\t\t}\n\t}()\n\treturn ch\n}\n\nfunc suck(ch chan int) {\n\tfor {\n\t\tfmt.Println(<-ch)\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_14/channel_idiom2.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tsuck(pump())\n\ttime.Sleep(1e9)\n}\n\nfunc pump() chan int {\n\tch := make(chan int)\n\tgo func() {\n\t\tfor i := 0; ; i++ {\n\t\t\tch <- i\n\t\t}\n\t}()\n\treturn ch\n}\n\nfunc suck(ch chan int) {\n\tgo func() {\n\t\tfor v := range ch {\n\t\t\tfmt.Println(v)\n\t\t}\n\t}()\n}\n"
  },
  {
    "path": "eBook/examples/chapter_14/conc_access.go",
    "content": "// conc_access.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n)\n\ntype Person struct {\n\tName   string\n\tsalary float64\n\tchF    chan func()\n}\n\nfunc NewPerson(name string, salary float64) *Person {\n\tp := &Person{name, salary, make(chan func())}\n\tgo p.backend()\n\treturn p\n}\n\nfunc (p *Person) backend() {\n\tfor f := range p.chF {\n\t\tf()\n\t}\n}\n\n// Set salary.\nfunc (p *Person) SetSalary(sal float64) {\n\tp.chF <- func() { p.salary = sal }\n}\n\n// Retrieve salary.\nfunc (p *Person) Salary() float64 {\n\tfChan := make(chan float64)\n\tp.chF <- func() { fChan <- p.salary }\n\treturn <-fChan\n}\n\nfunc (p *Person) String() string {\n\treturn \"Person - name is: \" + p.Name + \" - salary is: \" + strconv.FormatFloat(p.Salary(), 'f', 2, 64)\n}\n\nfunc main() {\n\tbs := NewPerson(\"Smith Bill\", 2500.5)\n\tfmt.Println(bs)\n\tbs.SetSalary(4000.25)\n\tfmt.Println(\"Salary changed:\")\n\tfmt.Println(bs)\n}\n\n/* Output:\nPerson - name is: Smith Bill - salary is: 2500.50\nSalary changed:\nPerson - name is: Smith Bill - salary is: 4000.25\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_14/general_lazy_evalution1.go",
    "content": "// general_lazy_evalution1.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\ntype Any interface{}\ntype EvalFunc func(Any) (Any, Any)\n\nfunc main() {\n\tevenFunc := func(state Any) (Any, Any) {\n\t\tos := state.(int)\n\t\tns := os + 2\n\t\treturn os, ns\n\t}\n\teven := BuildLazyIntEvaluator(evenFunc, 0)\n\n\tfor i := 0; i < 10; i++ {\n\t\tfmt.Printf(\"%vth even: %v\\n\", i, even())\n\t}\n}\n\nfunc BuildLazyEvaluator(evalFunc EvalFunc, initState Any) func() Any {\n\tretValChan := make(chan Any)\n\tloopFunc := func() {\n\t\tvar actState Any = initState\n\t\tvar retVal Any\n\t\tfor {\n\t\t\tretVal, actState = evalFunc(actState)\n\t\t\tretValChan <- retVal\n\t\t}\n\t}\n\tretFunc := func() Any {\n\t\treturn <-retValChan\n\t}\n\tgo loopFunc()\n\treturn retFunc\n}\n\nfunc BuildLazyIntEvaluator(evalFunc EvalFunc, initState Any) func() int {\n\tef := BuildLazyEvaluator(evalFunc, initState)\n\treturn func() int {\n\t\treturn ef().(int)\n\t}\n}\n\n/* Output:\n0th even: 0\n1th even: 2\n2th even: 4\n3th even: 6\n4th even: 8\n5th even: 10\n6th even: 12\n7th even: 14\n8th even: 16\n9th even: 18\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_14/goroutine1.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tfmt.Println(\"In main()\")\n\t// longWait()\n\tgo longWait()\n\t// shortWait()\n\tgo shortWait()\n\tfmt.Println(\"About to sleep in main()\")\n\ttime.Sleep(10 * 1e9) // sleep works with a Duration in nanoseconds (ns) !\n\tfmt.Println(\"At the end of main()\")\n}\n\nfunc longWait() {\n\tfmt.Println(\"Beginning longWait()\")\n\ttime.Sleep(5 * 1e9) // sleep for 5 seconds\n\tfmt.Println(\"End of longWait()\")\n}\n\nfunc shortWait() {\n\tfmt.Println(\"Beginning shortWait()\")\n\ttime.Sleep(2 * 1e9) // sleep for 2 seconds\n\tfmt.Println(\"End of shortWait()\")\n}\n"
  },
  {
    "path": "eBook/examples/chapter_14/goroutine2.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tch := make(chan string)\n\n\tgo sendData(ch)\n\tgo getData(ch)\n\n\ttime.Sleep(1e9)\n}\n\nfunc sendData(ch chan string) {\n\tch <- \"Washington\"\n\tch <- \"Tripoli\"\n\tch <- \"London\"\n\tch <- \"Beijing\"\n\tch <- \"Tokio\"\n}\n\nfunc getData(ch chan string) {\n\tvar input string\n\t// time.Sleep(1e9)\n\tfor {\n\t\tinput = <-ch\n\t\tfmt.Printf(\"%s \", input)\n\t}\n}\n\n// Washington Tripoli London Beijing Tokio\n"
  },
  {
    "path": "eBook/examples/chapter_14/goroutine3.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tch := make(chan string)\n\tgo sendData(ch)\n\tgetData(ch)\n}\n\nfunc sendData(ch chan string) {\n\tch <- \"Washington\"\n\tch <- \"Tripoli\"\n\tch <- \"London\"\n\tch <- \"Beijing\"\n\tch <- \"Tokio\"\n\tclose(ch)\n}\n\nfunc getData(ch chan string) {\n\tfor {\n\t\tinput, open := <-ch\n\t\tif !open {\n\t\t\tbreak\n\t\t}\n\t\tfmt.Printf(\"%s \", input)\n\t}\n}\n\n// Washington Tripoli London Beijing Tokio\n"
  },
  {
    "path": "eBook/examples/chapter_14/goroutine_select.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tch1 := make(chan int)\n\tch2 := make(chan int)\n\n\tgo pump1(ch1)\n\tgo pump2(ch2)\n\tgo suck(ch1, ch2)\n\n\ttime.Sleep(1e9)\n}\n\nfunc pump1(ch chan int) {\n\tfor i := 0; ; i++ {\n\t\tch <- i * 2\n\t}\n}\n\nfunc pump2(ch chan int) {\n\tfor i := 0; ; i++ {\n\t\tch <- i + 5\n\t}\n}\n\nfunc suck(ch1, ch2 chan int) {\n\tfor {\n\t\tselect {\n\t\tcase v := <-ch1:\n\t\t\tfmt.Printf(\"Received on channel 1: %d\\n\", v)\n\t\tcase v := <-ch2:\n\t\t\tfmt.Printf(\"Received on channel 2: %d\\n\", v)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_14/goroutine_select2.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"time\"\n)\n\nfunc main() {\n\t// setting GOMAXPROCS to 2 gives +- 22% performance increase,\n\t// but increasing the number doesn't increase the performance\n\t// without GOMAXPROCS: +- 86000\n\t// setting GOMAXPROCS to 2: +- 105000\n\t// setting GOMAXPROCS to 3: +- 94000\n\truntime.GOMAXPROCS(2)\n\tch1 := make(chan int)\n\tch2 := make(chan int)\n\n\tgo pump1(ch1)\n\tgo pump2(ch2)\n\tgo suck(ch1, ch2)\n\n\ttime.Sleep(1e9)\n}\n\nfunc pump1(ch chan int) {\n\tfor i := 0; ; i++ {\n\t\tch <- i * 2\n\t}\n}\n\nfunc pump2(ch chan int) {\n\tfor i := 0; ; i++ {\n\t\tch <- i + 5\n\t}\n}\n\nfunc suck(ch1, ch2 chan int) {\n\tfor i := 0; ; i++ {\n\t\tselect {\n\t\tcase v := <-ch1:\n\t\t\tfmt.Printf(\"%d - Received on channel 1: %d\\n\", i, v)\n\t\tcase v := <-ch2:\n\t\t\tfmt.Printf(\"%d - Received on channel 2: %d\\n\", i, v)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_14/lazy_evaluation.go",
    "content": "// lazy_evaluation.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nvar resume chan int\n\nfunc integers() chan int {\n\tyield := make(chan int)\n\tcount := 0\n\tgo func() {\n\t\tfor {\n\t\t\tyield <- count\n\t\t\tcount++\n\t\t}\n\t}()\n\treturn yield\n}\n\nfunc generateInteger() int {\n\treturn <-resume\n}\n\nfunc main() {\n\tresume = integers()\n\tfmt.Println(generateInteger()) //=> 0\n\tfmt.Println(generateInteger()) //=> 1\n\tfmt.Println(generateInteger()) //=> 2\n}\n"
  },
  {
    "path": "eBook/examples/chapter_14/max_tasks.go",
    "content": "package main\n\nconst MAXREQS = 50\n\nvar sem = make(chan int, MAXREQS)\n\ntype Request struct {\n\ta, b   int\n\treplyc chan int\n}\n\nfunc process(r *Request) {\n\t// do something\n}\n\nfunc handle(r *Request) {\n\tsem <- 1 // doesn't matter what we put in it\n\tprocess(r)\n\t<-sem // one empty place in the buffer: the next request can start\n}\n\nfunc server(service chan *Request) {\n\tfor {\n\t\trequest := <-service\n\t\tgo handle(request)\n\t}\n}\n\nfunc main() {\n\tservice := make(chan *Request)\n\tgo server(service)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_14/multiplex_server.go",
    "content": "// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\npackage main\n\nimport \"fmt\"\n\ntype Request struct {\n\ta, b   int\n\treplyc chan int // reply channel inside the Request\n}\n\ntype binOp func(a, b int) int\n\nfunc run(op binOp, req *Request) {\n\treq.replyc <- op(req.a, req.b)\n}\n\nfunc server(op binOp, service chan *Request) {\n\tfor {\n\t\treq := <-service // requests arrive here\n\t\t// start goroutine for request:\n\t\tgo run(op, req) // don't wait for op\n\t}\n}\n\nfunc startServer(op binOp) chan *Request {\n\treqChan := make(chan *Request)\n\tgo server(op, reqChan)\n\treturn reqChan\n}\n\nfunc main() {\n\tadder := startServer(func(a, b int) int { return a + b })\n\tconst N = 100\n\tvar reqs [N]Request\n\tfor i := 0; i < N; i++ {\n\t\treq := &reqs[i]\n\t\treq.a = i\n\t\treq.b = i + N\n\t\treq.replyc = make(chan int)\n\t\tadder <- req\n\t}\n\t// checks:\n\tfor i := N - 1; i >= 0; i-- { // doesn't matter what order\n\t\tif <-reqs[i].replyc != N+2*i {\n\t\t\tfmt.Println(\"fail at\", i)\n\t\t} else {\n\t\t\tfmt.Println(\"Request \", i, \" is ok!\")\n\t\t}\n\t}\n\tfmt.Println(\"done\")\n}\n"
  },
  {
    "path": "eBook/examples/chapter_14/multiplex_server2.go",
    "content": "// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\npackage main\n\nimport \"fmt\"\n\ntype Request struct {\n\ta, b   int\n\treplyc chan int // reply channel inside the Request\n}\n\ntype binOp func(a, b int) int\n\nfunc run(op binOp, req *Request) {\n\treq.replyc <- op(req.a, req.b)\n}\n\nfunc server(op binOp, service chan *Request, quit chan bool) {\n\tfor {\n\t\tselect {\n\t\tcase req := <-service:\n\t\t\tgo run(op, req)\n\t\tcase <-quit:\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc startServer(op binOp) (service chan *Request, quit chan bool) {\n\tservice = make(chan *Request)\n\tquit = make(chan bool)\n\tgo server(op, service, quit)\n\treturn service, quit\n}\n\nfunc main() {\n\tadder, quit := startServer(func(a, b int) int { return a + b })\n\tconst N = 100\n\tvar reqs [N]Request\n\tfor i := 0; i < N; i++ {\n\t\treq := &reqs[i]\n\t\treq.a = i\n\t\treq.b = i + N\n\t\treq.replyc = make(chan int)\n\t\tadder <- req\n\t}\n\t// checks:\n\tfor i := N - 1; i >= 0; i-- { // doesn't matter what order\n\t\tif <-reqs[i].replyc != N+2*i {\n\t\t\tfmt.Println(\"fail at\", i)\n\t\t} else {\n\t\t\tfmt.Println(\"Request \", i, \" is ok!\")\n\t\t}\n\t}\n\tquit <- true\n\tfmt.Println(\"done\")\n}\n"
  },
  {
    "path": "eBook/examples/chapter_14/sieve1.go",
    "content": "// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.package main\npackage main\n\nimport \"fmt\"\n\n// Send the sequence 2, 3, 4, ... to channel 'ch'.\nfunc generate(ch chan int) {\n\tfor i := 2; ; i++ {\n\t\tch <- i // Send 'i' to channel 'ch'.\n\t}\n}\n\n// Copy the values from channel 'in' to channel 'out',\n// removing those divisible by 'prime'.\nfunc filter(in, out chan int, prime int) {\n\tfor {\n\t\ti := <-in // Receive value of new variable 'i' from 'in'.\n\t\tif i%prime != 0 {\n\t\t\tout <- i // Send 'i' to channel 'out'.\n\t\t}\n\t}\n}\n\n// The prime sieve: Daisy-chain filter processes together.\nfunc main() {\n\tch := make(chan int) // Create a new channel.\n\tgo generate(ch)      // Start generate() as a goroutine.\n\tfor {\n\t\tprime := <-ch\n\t\tfmt.Print(prime, \" \")\n\t\tch1 := make(chan int)\n\t\tgo filter(ch, ch1, prime)\n\t\tch = ch1\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_14/sieve2.go",
    "content": "// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport (\n\t\"fmt\"\n)\n\n// Send the sequence 2, 3, 4, ... to returned channel\nfunc generate() chan int {\n\tch := make(chan int)\n\tgo func() {\n\t\tfor i := 2; ; i++ {\n\t\t\tch <- i\n\t\t}\n\t}()\n\treturn ch\n}\n\n// Filter out input values divisible by 'prime', send rest to returned channel\nfunc filter(in chan int, prime int) chan int {\n\tout := make(chan int)\n\tgo func() {\n\t\tfor {\n\t\t\tif i := <-in; i%prime != 0 {\n\t\t\t\tout <- i\n\t\t\t}\n\t\t}\n\t}()\n\treturn out\n}\n\nfunc sieve() chan int {\n\tout := make(chan int)\n\tgo func() {\n\t\tch := generate()\n\t\tfor {\n\t\t\tprime := <-ch\n\t\t\tch = filter(ch, prime)\n\t\t\tout <- prime\n\t\t}\n\t}()\n\treturn out\n}\n\nfunc main() {\n\tprimes := sieve()\n\tfor {\n\t\tfmt.Println(<-primes)\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_14/timer_goroutine.go",
    "content": "// default.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\ttick := time.Tick(1e8)\n\tboom := time.After(5e8)\n\tfor {\n\t\tselect {\n\t\tcase <-tick:\n\t\t\tfmt.Println(\"tick.\")\n\t\tcase <-boom:\n\t\t\tfmt.Println(\"BOOM!\")\n\t\t\treturn\n\t\tdefault:\n\t\t\tfmt.Println(\"    .\")\n\t\t\ttime.Sleep(5e7)\n\t\t}\n\t}\n}\n\n/* Output:\n    .\n    .\ntick.\n    .\n    .\ntick.\n    .\n    .\ntick.\n    .\n    .\ntick.\n    .\n    .\ntick.\nBOOM!\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_15/client.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"strings\"\n)\n\nfunc main() {\n\t//打开连接:\n\tconn, err := net.Dial(\"tcp\", \"localhost:50000\")\n\tif err != nil {\n\t\t//由于目标计算机积极拒绝而无法创建连接\n\t\tfmt.Println(\"Error dialing\", err.Error())\n\t\treturn // 终止程序\n\t}\n\n\tinputReader := bufio.NewReader(os.Stdin)\n\tfmt.Println(\"First, what is your name?\")\n\tclientName, _ := inputReader.ReadString('\\n')\n\t// fmt.Printf(\"CLIENTNAME %s\", clientName)\n\ttrimmedClient := strings.Trim(clientName, \"\\r\\n\") // Windows 平台下用 \"\\r\\n\"，Linux平台下使用 \"\\n\"\n\t// 给服务器发送信息直到程序退出：\n\tfor {\n\t\tfmt.Println(\"What to send to the server? Type Q to quit.\")\n\t\tinput, _ := inputReader.ReadString('\\n')\n\t\ttrimmedInput := strings.Trim(input, \"\\r\\n\")\n\t\t// fmt.Printf(\"input:--s%--\", input)\n\t\t// fmt.Printf(\"trimmedInput:--s%--\", trimmedInput)\n\t\tif trimmedInput == \"Q\" {\n\t\t\treturn\n\t\t}\n\t\t_, err = conn.Write([]byte(trimmedClient + \" says: \" + trimmedInput))\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_15/dial.go",
    "content": "// make a connection with www.example.org:\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n)\n\nfunc main() {\n\tconn, err := net.Dial(\"tcp\", \"192.0.32.10:80\") // tcp ipv4\n\tcheckConnection(conn, err)\n\tconn, err = net.Dial(\"udp\", \"192.0.32.10:80\") // udp\n\tcheckConnection(conn, err)\n\tconn, err = net.Dial(\"tcp\", \"[2620:0:2d0:200::10]:80\") // tcp ipv6\n\tcheckConnection(conn, err)\n}\nfunc checkConnection(conn net.Conn, err error) {\n\tif err != nil {\n\t\tfmt.Printf(\"error %v connecting!\", err)\n\t\tos.Exit(1)\n\t}\n\tfmt.Printf(\"Connection is made with %v\\n\", conn)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_15/elaborated_webserver.go",
    "content": "//Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\npackage main\n\nimport (\n\t\"bytes\"\n\t\"expvar\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"strconv\"\n)\n\n// hello world, the web server\nvar helloRequests = expvar.NewInt(\"hello-requests\")\n\n// flags:\nvar webroot = flag.String(\"root\", \"/home/user\", \"web root directory\")\n\n// simple flag server\nvar booleanflag = flag.Bool(\"boolean\", true, \"another flag for testing\")\n\n// Simple counter server. POSTing to it will set the value.\ntype Counter struct {\n\tn int\n}\n\n// a channel\ntype Chan chan int\n\nfunc main() {\n\tflag.Parse()\n\thttp.Handle(\"/\", http.HandlerFunc(Logger))\n\thttp.Handle(\"/go/hello\", http.HandlerFunc(HelloServer))\n\t// The counter is published as a variable directly.\n\tctr := new(Counter)\n\texpvar.Publish(\"counter\", ctr)\n\thttp.Handle(\"/counter\", ctr)\n\t// http.Handle(\"/go/\", http.FileServer(http.Dir(\"/tmp\"))) // uses the OS filesystem\n\thttp.Handle(\"/go/\", http.StripPrefix(\"/go/\", http.FileServer(http.Dir(*webroot))))\n\thttp.Handle(\"/flags\", http.HandlerFunc(FlagServer))\n\thttp.Handle(\"/args\", http.HandlerFunc(ArgServer))\n\thttp.Handle(\"/chan\", ChanCreate())\n\thttp.Handle(\"/date\", http.HandlerFunc(DateServer))\n\terr := http.ListenAndServe(\":12345\", nil)\n\tif err != nil {\n\t\tlog.Panicln(\"ListenAndServe:\", err)\n\t}\n}\n\nfunc Logger(w http.ResponseWriter, req *http.Request) {\n\tlog.Print(req.URL.String())\n\tw.WriteHeader(404)\n\tw.Write([]byte(\"oops\"))\n}\n\nfunc HelloServer(w http.ResponseWriter, req *http.Request) {\n\thelloRequests.Add(1)\n\tio.WriteString(w, \"hello, world!\\n\")\n}\n\n// This makes Counter satisfy the expvar.Var interface, so we can export\n// it directly.\nfunc (ctr *Counter) String() string { return fmt.Sprintf(\"%d\", ctr.n) }\n\nfunc (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) {\n\tswitch req.Method {\n\tcase \"GET\": // increment n\n\t\tctr.n++\n\tcase \"POST\": // set n to posted value\n\t\tbuf := new(bytes.Buffer)\n\t\tio.Copy(buf, req.Body)\n\t\tbody := buf.String()\n\t\tif n, err := strconv.Atoi(body); err != nil {\n\t\t\tfmt.Fprintf(w, \"bad POST: %v\\nbody: [%v]\\n\", err, body)\n\t\t} else {\n\t\t\tctr.n = n\n\t\t\tfmt.Fprint(w, \"counter reset\\n\")\n\t\t}\n\t}\n\tfmt.Fprintf(w, \"counter = %d\\n\", ctr.n)\n}\n\nfunc FlagServer(w http.ResponseWriter, req *http.Request) {\n\tw.Header().Set(\"Content-Type\", \"text/plain; charset=utf-8\")\n\tfmt.Fprint(w, \"Flags:\\n\")\n\tflag.VisitAll(func(f *flag.Flag) {\n\t\tif f.Value.String() != f.DefValue {\n\t\t\tfmt.Fprintf(w, \"%s = %s [default = %s]\\n\", f.Name, f.Value.String(), f.DefValue)\n\t\t} else {\n\t\t\tfmt.Fprintf(w, \"%s = %s\\n\", f.Name, f.Value.String())\n\t\t}\n\t})\n}\n\n// simple argument server\nfunc ArgServer(w http.ResponseWriter, req *http.Request) {\n\tfor _, s := range os.Args {\n\t\tfmt.Fprint(w, s, \" \")\n\t}\n}\n\nfunc ChanCreate() Chan {\n\tc := make(Chan)\n\tgo func(c Chan) {\n\t\tfor x := 0; ; x++ {\n\t\t\tc <- x\n\t\t}\n\t}(c)\n\treturn c\n}\n\nfunc (ch Chan) ServeHTTP(w http.ResponseWriter, req *http.Request) {\n\tio.WriteString(w, fmt.Sprintf(\"channel send #%d\\n\", <-ch))\n}\n\n// exec a program, redirecting output\nfunc DateServer(rw http.ResponseWriter, req *http.Request) {\n\trw.Header().Set(\"Content-Type\", \"text/plain; charset=utf-8\")\n\tr, w, err := os.Pipe()\n\tif err != nil {\n\t\tfmt.Fprintf(rw, \"pipe: %s\\n\", err)\n\t\treturn\n\t}\n\n\tp, err := os.StartProcess(\"/bin/date\", []string{\"date\"}, &os.ProcAttr{Files: []*os.File{nil, w, w}})\n\tdefer r.Close()\n\tw.Close()\n\tif err != nil {\n\t\tfmt.Fprintf(rw, \"fork/exec: %s\\n\", err)\n\t\treturn\n\t}\n\tdefer p.Release()\n\tio.Copy(rw, r)\n\twait, err := p.Wait()\n\tif err != nil {\n\t\tfmt.Fprintf(rw, \"wait: %s\\n\", err)\n\t\treturn\n\t}\n\tif !wait.Exited() {\n\t\tfmt.Fprintf(rw, \"date: %v\\n\", wait)\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_15/hello_world_webserver.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc HelloServer(w http.ResponseWriter, req *http.Request) {\n\tfmt.Println(\"Inside HelloServer handler\")\n\tfmt.Fprintf(w, \"Hello,\"+req.URL.Path[1:])\n}\n\nfunc main() {\n\thttp.HandleFunc(\"/\", HelloServer)\n\terr := http.ListenAndServe(\"localhost:8080\", nil)\n\tif err != nil {\n\t\tlog.Fatal(\"ListenAndServe: \", err.Error())\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_15/http_fetch.go",
    "content": "// httpfetch.go\r\npackage main\r\n\r\nimport (\r\n\t\"fmt\"\r\n\t\"net/http\"\r\n\t\"io/ioutil\"\r\n\t\"log\"\r\n)\r\n\r\nfunc main() {\r\n\tres, err := http.Get(\"http://www.google.com\")\r\n\tCheckError(err)\r\n\tdata, err := ioutil.ReadAll(res.Body)\r\n\tCheckError(err)\r\n\tfmt.Printf(\"Got: %q\", string(data))\r\n}\r\n\r\nfunc CheckError(err error) {\r\n\tif err != nil {\r\n\t\tlog.Fatalf(\"Get: %v\", err)\r\n\t}\r\n}\r\n"
  },
  {
    "path": "eBook/examples/chapter_15/http_fetch2.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"strings\"\n)\n\nfunc main() {\n\tinputReader := bufio.NewReader(os.Stdin)\n\tfmt.Println(\"Please enter the url...\")\n\turl, err := inputReader.ReadString('\\n')\n\turl = strings.TrimSuffix(url, \"\\r\\n\")\n\turl = strings.TrimSpace(url)\n\tcheckError(err)\n\tres, err := http.Get(url)\n\tcheckError(err)\n\tdata, err := ioutil.ReadAll(res.Body)\n\tcheckError(err)\n\tfmt.Printf(\"Got: %q\", string(data))\n}\n\nfunc checkError(err error) {\n\tif err != nil {\n\t\tlog.Fatalf(\"Get : %v\", err)\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_15/pipeline1.go",
    "content": "// pipeline1.go\npackage main\n\nimport (\n\t\"os\"\n\t\"text/template\"\n)\n\nfunc main() {\n\tt := template.New(\"template test\")\n\tt = template.Must(t.Parse(\"This is just static text. \\n{{\\\"This is pipeline data - because it is evaluated within the double braces.\\\"}} {{`So is this, but within reverse quotes.`}}\\n\"))\n\tt.Execute(os.Stdout, nil)\n}\n\n/*\nThis is just static text.\nThis is pipeline data - because it is evaluated within the double braces. So is this, but within reverse quotes.\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_15/poll_url.go",
    "content": "// poll_url.go\r\npackage main\r\n\r\nimport (\r\n\t\"fmt\"\r\n\t\"net/http\"\r\n)\r\n\r\nvar urls = []string{\r\n\t\"http://www.google.com/\",\r\n\t\"http://golang.org/\",\r\n\t\"http://blog.golang.org/\",\r\n}\r\n\r\nfunc main() {\r\n\t// Execute an HTTP HEAD request for all url's \r\n\t// and returns the HTTP status string or an error string.\r\n\tfor _, url := range urls {\r\n\t\tresp, err := http.Head(url)\r\n\t\tif err != nil {\r\n\t\t\tfmt.Println(\"Error\", url, err)\r\n\t\t}\r\n\t\tfmt.Print(url, \": \", resp.Status)\r\n\t}\r\n}\r\n/* Output:\r\nhttp://www.google.com/ :  302 Found\r\nhttp://golang.org/ :  200 OK\r\nhttp://blog.golang.org/ :  200 OK\r\n*/\r\n"
  },
  {
    "path": "eBook/examples/chapter_15/predefined_functions.go",
    "content": "// predefined_functions.go\npackage main\n\nimport (\n\t\"os\"\n\t\"text/template\"\n)\n\nfunc main() {\n\tt := template.New(\"test\")\n\tt = template.Must(t.Parse(\"{{with $x := `hello`}}{{printf `%s %s` $x `Mary`}}{{end}}!\\n\"))\n\tt.Execute(os.Stdout, nil)\n}\n\n// hello Mary!\n"
  },
  {
    "path": "eBook/examples/chapter_15/robust_webserver.go",
    "content": "// robust_webserver.go\npackage main\n\nimport (\n\t\"net/http\"\n\t\"io\"\n\t\"log\"\n)\n\nconst form = `<html><body><form action=\"#\" method=\"post\" name=\"bar\">\n\t\t<input type=\"text\" name=\"in\"/>\n\t\t<input type=\"submit\" value=\"Submit\"/>\n\t</form></html></body>`\n\ntype HandleFnc func(http.ResponseWriter, *http.Request)\n\n/* handle a simple get request */\nfunc SimpleServer(w http.ResponseWriter, request *http.Request) {\n\tio.WriteString(w, \"<h1>hello, world</h1>\")\n}\n\n/* handle a form, both the GET which displays the form\n   and the POST which processes it.*/\nfunc FormServer(w http.ResponseWriter, request *http.Request) {\n\tw.Header().Set(\"Content-Type\", \"text/html\")\n\tswitch request.Method {\n\tcase \"GET\":\n\t\t/* display the form to the user */\n\t\tio.WriteString(w, form)\n\tcase \"POST\":\n\t\t/* handle the form data, note that ParseForm must\n\t\t   be called before we can extract form data*/\n\t\t//request.ParseForm();\n\t\t//io.WriteString(w, request.Form[\"in\"][0])\n\t\tio.WriteString(w, request.FormValue(\"in\"))\n\t}\n}\n\nfunc main() {\n\thttp.HandleFunc(\"/test1\", logPanics(SimpleServer))\n\thttp.HandleFunc(\"/test2\", logPanics(FormServer))\n\tif err := http.ListenAndServe(\":8088\", nil); err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc logPanics(function HandleFnc) HandleFnc {\n\treturn func(writer http.ResponseWriter, request *http.Request) {\n\t\tdefer func() {\n\t\t\tif x := recover(); x != nil {\n\t\t\t\tlog.Printf(\"[%v] caught panic: %v\", request.RemoteAddr, x)\n\t\t\t}\n\t\t}()\n\t\tfunction(writer, request)\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_15/rpc/rpc_client.go",
    "content": "// rpc_client.go\r\n// if the server is not started:\r\n// can't get the server to start, so client stops immediately with error:\r\n// 2011/08/01 16:08:05 Error dialing:dial tcp :1234: \r\n//\t\tThe requested address is not valid in its context.\r\n// with serverAddress = localhost:\r\n// 2011/08/01 16:09:23 Error dialing:dial tcp 127.0.0.1:1234: \r\n//\t\tNo connection could be made because the target machine actively refused it.\r\npackage main\r\n\r\nimport (\r\n\t\"fmt\"\r\n\t\"log\"\r\n\t\"net/rpc\"\r\n\t\"./rpc_objects\"\r\n)\r\n\r\nconst serverAddress = \"localhost\"\r\n\r\nfunc main() {\r\n\tclient, err := rpc.DialHTTP(\"tcp\", serverAddress + \":1234\")\r\n\tif err != nil {\r\n\t\tlog.Fatal(\"Error dialing:\", err)\r\n\t}\r\n\t// Synchronous call\r\n\targs := &rpc_objects.Args{7, 8}\r\n\tvar reply int\r\n\terr = client.Call(\"Args.Multiply\", args, &reply)\r\n\tif err != nil {\r\n\t\tlog.Fatal(\"Args error:\", err)\r\n\t}\r\n\tfmt.Printf(\"Args: %d * %d = %d\", args.N, args.M, reply)\r\n}\r\n/* Output:\r\nStarting Process E:/Go/GoBoek/code_examples/chapter_14/rpc_client.exe ...\r\n\r\nArgs: 7 * 8 = 56\r\nEnd Process exit status 0\r\n*/\r\n"
  },
  {
    "path": "eBook/examples/chapter_15/rpc/rpc_objects.go",
    "content": "// rpc_objects.go\r\npackage rpc_objects\r\n\r\nimport \"net\"\r\n\r\ntype Args struct {\r\n\tN, M int\r\n}\r\n\r\nfunc (t *Args) Multiply(args *Args, reply *int) net.Error {\r\n\t*reply = args.N * args.M\r\n\treturn nil\r\n}"
  },
  {
    "path": "eBook/examples/chapter_15/rpc/rpc_server.go",
    "content": "// rpc_server.go\r\n// after client-exits the server shows the message:\r\n//       1:1234: The specified network name is no longer available.\r\n//       2011/08/01 16:19:04 rpc: rpc: server cannot decode request: WSARecv tcp 127.0.0.\r\npackage main\r\n\r\nimport (\r\n\t\"net/http\"\r\n\t\"log\"\r\n\t\"net\"\r\n\t\"net/rpc\"\r\n\t\"time\"\r\n\t\"./rpc_objects\"\r\n)\r\n\r\nfunc main() {\r\n\tcalc := new(rpc_objects.Args)\r\n\trpc.Register(calc)\r\n\trpc.HandleHTTP()\r\n\tlistener, e := net.Listen(\"tcp\", \"localhost:1234\")\r\n\tif e != nil {\r\n\t\tlog.Fatal(\"Starting RPC-server -listen error:\", e)\r\n\t}\r\n\tgo http.Serve(listener, nil)\r\n\ttime.Sleep(1000e9)\r\n}\r\n/* Output:\r\nStarting Process E:/Go/GoBoek/code_examples/chapter_14/rpc_server.exe ...\r\n\r\n** after 5 s: **\r\nEnd Process exit status 0\r\n*/\r\n"
  },
  {
    "path": "eBook/examples/chapter_15/rpc_updated/rpc_client.go",
    "content": "// rpc_client.go\r\n// if the server is not started:\r\n// can't get the server to start, so client stops immediately with error:\r\n// 2011/08/01 16:08:05 Error dialing:dial tcp :1234: \r\n//\t\tThe requested address is not valid in its context.\r\n// with serverAddress = localhost:\r\n// 2011/08/01 16:09:23 Error dialing:dial tcp 127.0.0.1:1234: \r\n//\t\tNo connection could be made because the target machine actively refused it.\r\npackage main\r\n\r\nimport (\r\n\t\"fmt\"\r\n\t\"log\"\r\n\t\"net/rpc\"\r\n\t\"./rpc_objects\"\r\n)\r\n\r\nconst serverAddress = \"localhost\"\r\n\r\nfunc main() {\r\n\tclient, err := rpc.DialHTTP(\"tcp\", serverAddress + \":1234\")\r\n\tif err != nil {\r\n\t\tlog.Fatal(\"Error dialing:\", err)\r\n\t}\r\n\t// Synchronous call\r\n\targs := &rpc_objects.Args{7, 8}\r\n\tvar reply int\r\n\terr = client.Call(\"Args.Multiply\", args, &reply)\r\n\tif err != nil {\r\n\t\tlog.Fatal(\"Args error:\", err)\r\n\t}\r\n\tfmt.Printf(\"Args: %d * %d = %d\", args.N, args.M, reply)\r\n}\r\n/* Output:\r\nStarting Process E:/Go/GoBoek/code_examples/chapter_14/rpc_client.exe ...\r\n\r\nArgs: 7 * 8 = 56\r\nEnd Process exit status 0\r\n*/\r\n"
  },
  {
    "path": "eBook/examples/chapter_15/rpc_updated/rpc_objects/rpc_objects.go",
    "content": "// rpc_objects.go\r\npackage rpc_objects\r\n\r\ntype Args struct {\r\n\tN, M int\r\n}\r\n\r\nfunc (t *Args) Multiply(args *Args, reply *int) error {\r\n\t*reply = args.N * args.M\r\n\treturn nil\r\n}"
  },
  {
    "path": "eBook/examples/chapter_15/rpc_updated/rpc_server.go",
    "content": "// rpc_server.go\r\n// after client-exits the server shows the message:\r\n//       1:1234: The specified network name is no longer available.\r\n//       2011/08/01 16:19:04 rpc: rpc: server cannot decode request: WSARecv tcp 127.0.0.\r\npackage main\r\n\r\nimport (\r\n\t\"net/http\"\r\n\t\"log\"\r\n\t\"net\"\r\n\t\"net/rpc\"\r\n\t\"time\"\r\n\t\"./rpc_objects\"\r\n)\r\n\r\nfunc main() {\r\n\tcalc := new(rpc_objects.Args)\r\n\trpc.Register(calc)\r\n\trpc.HandleHTTP()\r\n\tlistener, e := net.Listen(\"tcp\", \"localhost:1234\")\r\n\tif e != nil {\r\n\t\tlog.Fatal(\"Starting RPC-server -listen error:\", e)\r\n\t}\r\n\tgo http.Serve(listener, nil)\r\n\ttime.Sleep(1000e9)\r\n}\r\n/* Output:\r\nStarting Process E:/Go/GoBoek/code_examples/chapter_14/rpc_server.exe ...\r\n\r\n** after 5 s: **\r\nEnd Process exit status 0\r\n*/\r\n"
  },
  {
    "path": "eBook/examples/chapter_15/server.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net\"\n)\n\nfunc main() {\n\tfmt.Println(\"Starting the server ...\")\n\t// 创建 listener\n\tlistener, err := net.Listen(\"tcp\", \"localhost:50000\")\n\tif err != nil {\n\t\tfmt.Println(\"Error listening\", err.Error())\n\t\treturn //终止程序\n\t}\n\t// 监听并接受来自客户端的连接\n\tfor {\n\t\tconn, err := listener.Accept()\n\t\tif err != nil {\n\t\t\tfmt.Println(\"Error accepting\", err.Error())\n\t\t\treturn // 终止程序\n\t\t}\n\t\tgo doServerStuff(conn)\n\t}\n}\n\nfunc doServerStuff(conn net.Conn) {\n\tfor {\n\t\tbuf := make([]byte, 512)\n\t\tlen, err := conn.Read(buf)\n\t\tif err != nil {\n\t\t\tfmt.Println(\"Error reading\", err.Error())\n\t\t\treturn //终止程序\n\t\t}\n\t\tfmt.Printf(\"Received data: %v\", string(buf[:len]))\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_15/simple_tcp_server.go",
    "content": "// Simple multi-thread/multi-core TCP server.\npackage main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n)\n\nconst maxRead = 25\n\nfunc main() {\n\tflag.Parse()\n\tif flag.NArg() != 2 {\n\t\tpanic(\"usage: host port\")\n\t}\n\thostAndPort := fmt.Sprintf(\"%s:%s\", flag.Arg(0), flag.Arg(1))\n\tlistener := initServer(hostAndPort)\n\tfor {\n\t\tconn, err := listener.Accept()\n\t\tcheckError(err, \"Accept: \")\n\t\tgo connectionHandler(conn)\n\t}\n}\n\nfunc initServer(hostAndPort string) *net.TCPListener {\n\tserverAddr, err := net.ResolveTCPAddr(\"tcp\", hostAndPort)\n\tcheckError(err, \"Resolving address:port failed: '\"+hostAndPort+\"'\")\n\tlistener, err := net.ListenTCP(\"tcp\", serverAddr)\n\tcheckError(err, \"ListenTCP: \")\n\tprintln(\"Listening to: \", listener.Addr().String())\n\treturn listener\n}\n\nfunc connectionHandler(conn net.Conn) {\n\tconnFrom := conn.RemoteAddr().String()\n\tprintln(\"Connection from: \", connFrom)\n\tsayHello(conn)\n\tfor {\n\t\tvar ibuf []byte = make([]byte, maxRead+1)\n\t\tlength, err := conn.Read(ibuf[0:maxRead])\n\t\tibuf[maxRead] = 0 // to prevent overflow\n\t\tswitch err {\n\t\tcase nil:\n\t\t\thandleMsg(length, err, ibuf)\n\t\tcase os.EAGAIN: // try again\n\t\t\tcontinue\n\t\tdefault:\n\t\t\tgoto DISCONNECT\n\t\t}\n\t}\nDISCONNECT:\n\terr := conn.Close()\n\tprintln(\"Closed connection: \", connFrom)\n\tcheckError(err, \"Close: \")\n}\n\nfunc sayHello(to net.Conn) {\n\tobuf := []byte{'L', 'e', 't', '\\'', 's', ' ', 'G', 'O', '!', '\\n'}\n\twrote, err := to.Write(obuf)\n\tcheckError(err, \"Write: wrote \"+string(wrote)+\" bytes.\")\n}\n\nfunc handleMsg(length int, err error, msg []byte) {\n\tif length > 0 {\n\t\tprint(\"<\", length, \":\")\n\t\tfor i := 0; ; i++ {\n\t\t\tif msg[i] == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tfmt.Printf(\"%c\", msg[i])\n\t\t}\n\t\tprint(\">\")\n\t}\n}\n\nfunc checkError(error error, info string) {\n\tif error != nil {\n\t\tpanic(\"ERROR: \" + info + \" \" + error.Error()) // terminate program\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_15/simple_tcp_server_v1.go",
    "content": "// Simple multi-thread/multi-core TCP server.\npackage main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"net\"\n\t\"syscall\"\n)\n\nconst maxRead = 25\n\nfunc main() {\n\tflag.Parse()\n\tif flag.NArg() != 2 {\n\t\tpanic(\"usage: host port\")\n\t}\n\thostAndPort := fmt.Sprintf(\"%s:%s\", flag.Arg(0), flag.Arg(1))\n\tlistener := initServer(hostAndPort)\n\tfor {\n\t\tconn, err := listener.Accept()\n\t\tcheckError(err, \"Accept: \")\n\t\tgo connectionHandler(conn)\n\t}\n}\nfunc initServer(hostAndPort string) net.Listener {\n\tserverAddr, err := net.ResolveTCPAddr(\"tcp\", hostAndPort)\n\tcheckError(err, \"Resolving address:port failed: '\"+hostAndPort+\"'\")\n\tlistener, err := net.ListenTCP(\"tcp\", serverAddr)\n\tcheckError(err, \"ListenTCP: \")\n\tprintln(\"Listening to: \", listener.Addr().String())\n\treturn listener\n}\nfunc connectionHandler(conn net.Conn) {\n\tconnFrom := conn.RemoteAddr().String()\n\tprintln(\"Connection from: \", connFrom)\n\tsayHello(conn)\n\tfor {\n\t\tvar ibuf []byte = make([]byte, maxRead+1)\n\t\tlength, err := conn.Read(ibuf[0:maxRead])\n\t\tibuf[maxRead] = 0 // to prevent overflow\n\t\tswitch err {\n\t\tcase nil:\n\t\t\thandleMsg(length, err, ibuf)\n\t\tcase syscall.EAGAIN: // try again\n\t\t\tcontinue\n\t\tdefault:\n\t\t\tgoto DISCONNECT\n\t\t}\n\t}\nDISCONNECT:\n\terr := conn.Close()\n\tprintln(\"Closed connection: \", connFrom)\n\tcheckError(err, \"Close: \")\n}\nfunc sayHello(to net.Conn) {\n\tobuf := []byte{'L', 'e', 't', '\\'', 's', ' ', 'G', 'O', '!', '\\n'}\n\twrote, err := to.Write(obuf)\n\tcheckError(err, \"Write: wrote \"+string(wrote)+\" bytes.\")\n}\nfunc handleMsg(length int, err error, msg []byte) {\n\tif length > 0 {\n\t\tprint(\"<\", length, \":\")\n\t\tfor i := 0; ; i++ {\n\t\t\tif msg[i] == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tfmt.Printf(\"%c\", msg[i])\n\t\t}\n\t\tprint(\">\")\n\t}\n}\nfunc checkError(error error, info string) {\n\tif error != nil {\n\t\tpanic(\"ERROR: \" + info + \" \" + error.Error()) // terminate program\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_15/simple_webserver.go",
    "content": "// simple_webserver.go\r\npackage main  \r\n  \r\nimport (  \r\n     \"net/http\"  \r\n     \"io\"  \r\n)  \r\n\r\nconst form = `<html><body><form action=\"#\" method=\"post\" name=\"bar\">\r\n\t\t      <input type=\"text\" name=\"in\"/>\r\n\t\t\t  <input type=\"submit\" value=\"Submit\"/>\r\n\t\t\t  </form></html></body>`\r\n\r\n/* handle a simple get request */  \r\nfunc SimpleServer(w http.ResponseWriter, request *http.Request) {  \r\n     io.WriteString(w, \"<h1>hello, world</h1>\")  \r\n}  \r\n  \r\n/* handle a form, both the GET which displays the form \r\n   and the POST which processes it.*/  \r\nfunc FormServer(w http.ResponseWriter, request *http.Request) {\r\n     w.Header().Set(\"Content-Type\", \"text/html\")  \r\n     switch request.Method {  \r\n            case \"GET\":  \r\n                 /* display the form to the user */  \r\n                 io.WriteString(w, form );  \r\n            case \"POST\":  \r\n                 /* handle the form data, note that ParseForm must \r\n                    be called before we can extract form data*/  \r\n                 //request.ParseForm();  \r\n                 //io.WriteString(w, request.Form[\"in\"][0])\r\n\t\t\t\tio.WriteString(w, request.FormValue(\"in\")) \r\n     }  \r\n}  \r\n  \r\nfunc main() {  \r\n     http.HandleFunc(\"/test1\", SimpleServer)  \r\n     http.HandleFunc(\"/test2\", FormServer)  \r\n     if err := http.ListenAndServe(\":8088\", nil); err != nil {\r\n\t\tpanic(err)\r\n     }\r\n}  \r\n"
  },
  {
    "path": "eBook/examples/chapter_15/smtp.go",
    "content": "// smtp.go\r\npackage main\r\n\r\nimport (\r\n        \"bytes\"\r\n        \"log\"\r\n        \"net/smtp\"\r\n)\r\n\r\nfunc main() {\r\n        // Connect to the remote SMTP server.\r\n        client, err := smtp.Dial(\"mail.example.com:25\")\r\n        if err != nil {\r\n                log.Fatal(err)\r\n        }\r\n        // Set the sender and recipient.\r\n        client.Mail(\"sender@example.org\")\r\n        client.Rcpt(\"recipient@example.net\")\r\n        // Send the email body.\r\n        wc, err := client.Data()\r\n        if err != nil {\r\n                log.Fatal(err)\r\n        }\r\n        defer wc.Close()\r\n        buf := bytes.NewBufferString(\"This is the email body.\")\r\n        if _, err = buf.WriteTo(wc); err != nil {\r\n                log.Fatal(err)\r\n        }\r\n}\r\n\r\n"
  },
  {
    "path": "eBook/examples/chapter_15/smtp_auth.go",
    "content": "// smtp_auth.go\r\npackage main\r\n\r\nimport (\r\n        \"log\"\r\n        \"net/smtp\"\r\n)\r\n\r\nfunc main() {\r\n        // Set up authentication information.\r\n        auth := smtp.PlainAuth(\r\n                \"\",\r\n                \"user@example.com\",\r\n                \"password\",\r\n                \"mail.example.com\",\r\n        )\r\n        // Connect to the server, authenticate, set the sender and recipient,\r\n        // and send the email all in one step.\r\n        err := smtp.SendMail(\r\n                \"mail.example.com:25\",\r\n                auth,\r\n                \"sender@example.org\",\r\n                []string{\"recipient@example.net\"},\r\n                []byte(\"This is the email body.\"),\r\n        )\r\n        if err != nil {\r\n                log.Fatal(err)\r\n        }\r\n}\r\n"
  },
  {
    "path": "eBook/examples/chapter_15/socket.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n)\n\nfunc main() {\n\tvar (\n\t\thost          = \"www.apache.org\"\n\t\tport          = \"80\"\n\t\tremote        = host + \":\" + port\n\t\tmsg    string = \"GET / \\n\"\n\t\tdata          = make([]uint8, 4096)\n\t\tread          = true\n\t\tcount         = 0\n\t)\n\t// 创建一个socket\n\tcon, err := net.Dial(\"tcp\", remote)\n\t// 发送我们的消息，一个http GET请求\n\tio.WriteString(con, msg)\n\t// 读取服务器的响应\n\tfor read {\n\t\tcount, err = con.Read(data)\n\t\tread = (err == nil)\n\t\tfmt.Printf(string(data[0:count]))\n\t}\n\tcon.Close()\n}\n"
  },
  {
    "path": "eBook/examples/chapter_15/template_field.go",
    "content": "// template_field.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"text/template\"\n)\n\ntype Person struct {\n\tName string\n\tnonExportedAgeField string\n}\n\nfunc main() {\n\tt := template.New(\"hello\")\n\tt, _ = t.Parse(\"hello {{.Name}}!\")\n\tp := Person{Name: \"Mary\", nonExportedAgeField: \"31\"}\n\tif err := t.Execute(os.Stdout, p); err != nil {\n\t\tfmt.Println(\"There was an error:\", err.Error())\n\t}\n}\n\n// Output:   hello Mary!\n"
  },
  {
    "path": "eBook/examples/chapter_15/template_ifelse.go",
    "content": "// template_ifelse.go\npackage main\n\nimport (\n\t\"os\"\n\t\"text/template\"\n)\n\nfunc main() {\n\ttEmpty := template.New(\"template test\")\n\ttEmpty = template.Must(tEmpty.Parse(\"Empty pipeline if demo: {{if ``}} Will not print. {{end}}\\n\")) //empty pipeline following if\n\ttEmpty.Execute(os.Stdout, nil)\n\n\ttWithValue := template.New(\"template test\")\n\ttWithValue = template.Must(tWithValue.Parse(\"Non empty pipeline if demo: {{if `anything`}} Will print. {{end}}\\n\")) //non empty pipeline following if condition\n\ttWithValue.Execute(os.Stdout, nil)\n\n\ttIfElse := template.New(\"template test\")\n\ttIfElse = template.Must(tIfElse.Parse(\"if-else demo: {{if `anything`}} Print IF part. {{else}} Print ELSE part.{{end}}\\n\")) //non empty pipeline following if condition\n\ttIfElse.Execute(os.Stdout, nil)\n}\n\n/* Output:\nEmpty pipeline if demo:\nNon empty pipeline if demo:  Will print.\nif-else demo:  Print IF part.\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_15/template_validation.go",
    "content": "// template_validation.go\npackage main\n\nimport (\n\t\"text/template\"\n\t\"fmt\"\n)\n\nfunc main() {\n\ttOk := template.New(\"ok\")\n\t//a valid template, so no panic with Must:\n\ttemplate.Must(tOk.Parse(\"/* and a comment */ some static text: {{ .Name }}\"))\n\tfmt.Println(\"The first one parsed OK.\")\n\tfmt.Println(\"The next one ought to fail.\")\n\ttErr := template.New(\"error_template\")\n\ttemplate.Must(tErr.Parse(\" some static text {{ .Name }\"))\n}\n\n/* Output:\nThe first one parsed OK.\nThe next one ought to fail.\npanic: template: error_template:1: unexpected \"}\" in command\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_15/template_variables.go",
    "content": "// template_variables.go\npackage main\n\nimport (\n\t\"os\"\n\t\"text/template\"\n)\n\nfunc main() {\n\tt := template.New(\"test\")\n\tt = template.Must(t.Parse(\"{{with $3 := `hello`}}{{$3}}{{end}}!\\n\"))\n\tt.Execute(os.Stdout, nil)\n\n\tt = template.Must(t.Parse(\"{{with $x3 := `hola`}}{{$x3}}{{end}}!\\n\"))\n\tt.Execute(os.Stdout, nil)\n\n\tt = template.Must(t.Parse(\"{{with $x_1 := `hey`}}{{$x_1}} {{.}} {{$x_1}}{{end}}!\\n\"))\n\tt.Execute(os.Stdout, nil)\n}\n\n/* Output:\nhello!\nhola!\nhey hey hey!\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_15/template_with_end.go",
    "content": "// template_with_end.go\npackage main\n\nimport (\n\t\"os\"\n\t\"text/template\"\n)\n\nfunc main() {\n\tt := template.New(\"test\")\n\tt, _ = t.Parse(\"{{with `hello`}}{{.}}{{end}}!\\n\")\n\tt.Execute(os.Stdout, nil)\n\n\tt, _ = t.Parse(\"{{with `hello`}}{{.}} {{with `Mary`}}{{.}}{{end}}{{end}}!\\n\")\n\tt.Execute(os.Stdout, nil)\n}\n\n/* Output:\nhello!\nhello Mary!\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_15/twitter_status.go",
    "content": "// twitter_status.go\r\npackage main\r\n\r\nimport (\r\n\t\"net/http\"\r\n\t\"fmt\"\r\n\t\"encoding/xml\"\r\n\t\"io/ioutil\"\r\n)\r\n/* these structs will house the unmarshalled response. \r\n   they should be hierarchically shaped like the XML \r\n   but can omit irrelevant data. */\r\ntype Status struct {\r\n\tText string\r\n}\r\n\r\ntype User struct {\r\n\tXMLName xml.Name\r\n\tStatus  Status\r\n}\r\n// var user User\r\n\r\nfunc main() {\r\n\t// perform an HTTP request for the twitter status of user: Googland  \r\n\tresp, _ := http.Get(\"http://twitter.com/users/Googland.xml\")\r\n\t// initialize the structure of the XML response  \r\n\tuser := User{xml.Name{\"\", \"user\"}, Status{\"\"}}\r\n\t// unmarshal the XML into our structures \r\n\tdefer resp.Body.Close()\r\n\tif body, err := ioutil.ReadAll(resp.Body); err != nil {\r\n\t\t\tfmt.Printf(\"error: %s\", err.Error())\r\n\t} else {\r\n\t\t\tfmt.Printf(\"%s ---\", body)\r\n\t\t\txml.Unmarshal(body, &user)\r\n\t}\r\n\tfmt.Printf(\"name: %s \", user.XMLName)\r\n\tfmt.Printf(\"status: %s\", user.Status.Text)\r\n}\r\n/* Output:\r\nstatus: Robot cars invade California, on orders from Google: Google has been testing self-driving cars ... http://bit.ly/cbtpUN http://retwt.me/97p<exit code=\"0\" msg=\"process exited normally\"/>\r\nAfter Go1: no output: name: { user} status:\r\n*/"
  },
  {
    "path": "eBook/examples/chapter_15/websocket_client.go",
    "content": "// websocket_client.go\r\npackage main\r\n\r\nimport (\r\n\t\"fmt\"\r\n\t\"time\"\r\n\t\"code.google.com/p/go.net/websocket\"\r\n)\r\n\r\nfunc main() {\r\n\tws, err := websocket.Dial(\"ws://localhost:12345/websocket\", \"\",\r\n\t\t\"http://localhost/\")\r\n\tif err != nil {\r\n\t\tpanic(\"Dial: \" + err.Error())\r\n\t}\r\n\tgo readFromServer(ws)\r\n\ttime.Sleep(5e9)\r\n    ws.Close()\r\n}\r\n\r\nfunc readFromServer(ws *websocket.Conn) {\r\n\tbuf := make([]byte, 1000)\r\n\tfor {\r\n\t\tif _, err := ws.Read(buf); err != nil {\r\n\t\t\tfmt.Printf(\"%s\\n\", err.Error())\r\n\t\t\tbreak\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "eBook/examples/chapter_15/websocket_server.go",
    "content": "// websocket_server.go\r\npackage main\r\n\r\nimport (\r\n\t\"fmt\"\r\n\t\"net/http\"\r\n\t\"code.google.com/p/go.net/websocket\"\r\n)\r\n\r\nfunc server(ws *websocket.Conn) {\r\n\tfmt.Printf(\"new connection\\n\")\r\n\tbuf := make([]byte, 100)\r\n\tfor {\r\n\t\tif _, err := ws.Read(buf); err != nil {\r\n\t\t\tfmt.Printf(\"%s\", err.Error())\r\n\t\t\tbreak\r\n\t\t}\r\n\t}\r\n\tfmt.Printf(\" => closing connection\\n\")\r\n\tws.Close()\r\n}\r\n\r\nfunc main() {\r\n\thttp.Handle(\"/websocket\", websocket.Handler(server))\r\n\terr := http.ListenAndServe(\":12345\", nil)\r\n\tif err != nil {\r\n\t\tpanic(\"ListenAndServe: \" + err.Error())\r\n\t}\r\n}\r\n"
  },
  {
    "path": "eBook/examples/chapter_15/wiki/ANewPage.txt",
    "content": "Testing input of new page!\r\nGo Go Go !"
  },
  {
    "path": "eBook/examples/chapter_15/wiki/TestPage.txt",
    "content": "This is a sample Page."
  },
  {
    "path": "eBook/examples/chapter_15/wiki/page.txt",
    "content": "Hello Go - World !!!\r\nThis works great.\r\n"
  },
  {
    "path": "eBook/examples/chapter_15/wiki/page1.txt",
    "content": "This is a test!!"
  },
  {
    "path": "eBook/examples/chapter_15/wiki/page5.txt",
    "content": "Page5 is hereby started.\r\nThis is a first addition.\r\n2nd addition."
  },
  {
    "path": "eBook/examples/chapter_15/wiki/wiki.go",
    "content": "package main\n\nimport (\n\t\"net/http\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"regexp\"\n\t\"text/template\"\n)\n\nconst lenPath = len(\"/view/\")\n\nvar titleValidator = regexp.MustCompile(\"^[a-zA-Z0-9]+$\")\nvar templates = make(map[string]*template.Template)\nvar err error\n\ntype Page struct {\n\tTitle string\n\tBody  []byte\n}\n\nfunc init() {\n\tfor _, tmpl := range []string{\"edit\", \"view\"} {\n\t\ttemplates[tmpl] = template.Must(template.ParseFiles(tmpl + \".html\"))\n\t}\n}\n\nfunc main() {\n\thttp.HandleFunc(\"/view/\", makeHandler(viewHandler))\n\thttp.HandleFunc(\"/edit/\", makeHandler(editHandler))\n\thttp.HandleFunc(\"/save/\", makeHandler(saveHandler))\n\terr := http.ListenAndServe(\"localhost:8080\", nil)\n\tif err != nil {\n\t\tlog.Fatal(\"ListenAndServe: \", err.Error())\n\t}\n}\n\nfunc makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\ttitle := r.URL.Path[lenPath:]\n\t\tif !titleValidator.MatchString(title) {\n\t\t\thttp.NotFound(w, r)\n\t\t\treturn\n\t\t}\n\t\tfn(w, r, title)\n\t}\n}\n\nfunc viewHandler(w http.ResponseWriter, r *http.Request, title string) {\n\tp, err := load(title)\n\tif err != nil { // page not found\n\t\thttp.Redirect(w, r, \"/edit/\"+title, http.StatusFound)\n\t\treturn\n\t}\n\trenderTemplate(w, \"view\", p)\n}\n\nfunc editHandler(w http.ResponseWriter, r *http.Request, title string) {\n\tp, err := load(title)\n\tif err != nil {\n\t\tp = &Page{Title: title}\n\t}\n\trenderTemplate(w, \"edit\", p)\n}\n\nfunc saveHandler(w http.ResponseWriter, r *http.Request, title string) {\n\tbody := r.FormValue(\"body\")\n\tp := &Page{Title: title, Body: []byte(body)}\n\terr := p.save()\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\thttp.Redirect(w, r, \"/view/\"+title, http.StatusFound)\n}\n\nfunc renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {\n\terr := templates[tmpl].Execute(w, p)\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t}\n}\n\nfunc (p *Page) save() error {\n\tfilename := p.Title + \".txt\"\n\t// file created with read-write permissions for the current user only\n\treturn ioutil.WriteFile(filename, p.Body, 0600)\n}\n\nfunc load(title string) (*Page, error) {\n\tfilename := title + \".txt\"\n\tbody, err := ioutil.ReadFile(filename)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &Page{Title: title, Body: body}, nil\n}\n"
  },
  {
    "path": "eBook/examples/chapter_15/wiki/wiki_part1.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io/ioutil\"\n)\n\ntype Page struct {\n\tTitle string\n\tBody  []byte\n}\n\nfunc (p *Page) save() error {\n\tfilename := p.Title + \".txt\"\n\treturn ioutil.WriteFile(filename, p.Body, 0600)\n}\n\nfunc load(title string) (*Page, error) {\n\tfilename := title + \".txt\"\n\tbody, err := ioutil.ReadFile(filename)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &Page{Title: title, Body: body}, nil\n}\n\nfunc main() {\n\tp1 := &Page{Title: \"TestPage\", Body: []byte(\"This is a sample Page.\")}\n\tp1.save()\n\tp2, _ := load(\"TestPage\")\n\tfmt.Println(string(p2.Body))\n}\n"
  },
  {
    "path": "eBook/examples/chapter_15/wiki/wiki_part2.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net/http\"\n)\n\ntype Page struct {\n\tTitle string\n\tBody  []byte\n}\n\nfunc (p *Page) save() error {\n\tfilename := p.Title + \".txt\"\n\treturn ioutil.WriteFile(filename, p.Body, 0600)\n}\n\nfunc load(title string) (*Page, error) {\n\tfilename := title + \".txt\"\n\tbody, err := ioutil.ReadFile(filename)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &Page{Title: title, Body: body}, nil\n}\n\nconst lenPath = len(\"/view/\")\n\nfunc viewHandler(w http.ResponseWriter, r *http.Request) {\n\ttitle := r.URL.Path[lenPath:]\n\tp, _ := load(title)\n\tfmt.Fprintf(w, \"<h1>%s</h1><div>%s</div>\", p.Title, p.Body)\n}\n\nfunc main() {\n\thttp.HandleFunc(\"/view/\", viewHandler)\n\thttp.ListenAndServe(\":8080\", nil)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_16/closures_goroutines.go",
    "content": "// closures_goroutines.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nvar values = [5]int{10, 11, 12, 13, 14}\n\nfunc main() {\n\t// version A:\n\tfor ix := range values { // ix is index!\n\t\tfunc() {\n\t\t\tfmt.Print(ix, \" \")\n\t\t}() // call closure, prints each index\n\t}\n\tfmt.Println()\n\t// version B: same as A, but call closure as a goroutine\n\tfor ix := range values {\n\t\tgo func() {\n\t\t\tfmt.Print(ix, \" \")\n\t\t}()\n\t}\n\ttime.Sleep(1e9)\n\t// version C: the right way\n\tfor ix := range values {\n\t\tgo func(ix interface{}) {\n\t\t\tfmt.Print(ix, \" \")\n\t\t}(ix)\n\t}\n\ttime.Sleep(1e9)\n\t// version D: print out the values:\n\tfor ix := range values {\n\t\tval := values[ix]\n\t\tgo func() {\n\t\t\tfmt.Print(val, \" \")\n\t\t}()\n\t}\n\ttime.Sleep(1e9)\n}\n\n/* Output:\n0 1 2 3 4\n4 4 4 4 4\n1 0 3 4 2\n0 1 2 4 3\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_16/pointer_interface.go",
    "content": "// nexter.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\ntype nexter interface {\n\tnext() byte\n}\n\nfunc nextFew1(n nexter, num int) []byte {\n\tvar b []byte\n\tfor i := 0; i < num; i++ {\n\t\tb[i] = n.next()\n\t}\n\treturn b\n}\n\nfunc nextFew2(n *nexter, num int) []byte {\n\tvar b []byte\n\tfor i := 0; i < num; i++ {\n\t\tb[i] = n.next() // compile error:  n.next undefined (type *nexter has no field or method next)\n\t}\n\treturn b\n}\n\nfunc main() {\n\tfmt.Println(\"Hello World!\")\n}\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v1/Makefile",
    "content": "# Copyright 2009 The Go Authors. All rights reserved.\n# Use of this source code is governed by a BSD-style\n# license that can be found in the LICENSE file.\n\ninclude $(GOROOT)/src/Make.inc\n\nTARG=goto\nGOFILES=\\\n\tkey.go\\\n\tmain.go\\\n\tstore.go\\\n\ninclude $(GOROOT)/src/Make.cmd\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v1/key.go",
    "content": "package main\n\nvar keyChar = []byte(\"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\")\n\nfunc genKey(n int) string {\n\tif n == 0 {\n\t\treturn string(keyChar[0])\n\t}\n\tl := len(keyChar)\n\ts := make([]byte, 20) // FIXME: will overflow. eventually.\n\ti := len(s)\n\tfor n > 0 && i >= 0 {\n\t\ti--\n\t\tj := n % l\n\t\tn = (n - j) / l\n\t\ts[i] = keyChar[j]\n\t}\n\treturn string(s[i:])\n}\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v1/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n)\n\nconst AddForm = `\n<form method=\"POST\" action=\"/add\">\nURL: <input type=\"text\" name=\"url\">\n<input type=\"submit\" value=\"Add\">\n</form>\n`\n\nvar store = NewURLStore()\n\nfunc main() {\n\thttp.HandleFunc(\"/\", Redirect)\n\thttp.HandleFunc(\"/add\", Add)\n\thttp.ListenAndServe(\":8080\", nil)\n}\n\nfunc Redirect(w http.ResponseWriter, r *http.Request) {\n\tkey := r.URL.Path[1:]\n\turl := store.Get(key)\n\tif url == \"\" {\n\t\thttp.NotFound(w, r)\n\t\treturn\n\t}\n\thttp.Redirect(w, r, url, http.StatusFound)\n}\n\nfunc Add(w http.ResponseWriter, r *http.Request) {\n\turl := r.FormValue(\"url\")\n\tif url == \"\" {\n\t\tw.Header().Set(\"Content-Type\", \"text/html\")\n\t\tfmt.Fprint(w, AddForm)\n\t\treturn\n\t}\n\tkey := store.Put(url)\n\tfmt.Fprintf(w, \"http://localhost:8080/%s\", key)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v1/store.go",
    "content": "package main\n\nimport \"sync\"\n\ntype URLStore struct {\n\turls map[string]string\n\tmu   sync.RWMutex\n}\n\nfunc NewURLStore() *URLStore {\n\treturn &URLStore{urls: make(map[string]string)}\n}\n\nfunc (s *URLStore) Get(key string) string {\n\ts.mu.RLock()\n\tdefer s.mu.RUnlock()\n\treturn s.urls[key]\n}\n\nfunc (s *URLStore) Set(key, url string) bool {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\tif _, present := s.urls[key]; present {\n\t\treturn false\n\t}\n\ts.urls[key] = url\n\treturn true\n}\n\nfunc (s *URLStore) Count() int {\n\ts.mu.RLock()\n\tdefer s.mu.RUnlock()\n\treturn len(s.urls)\n}\n\nfunc (s *URLStore) Put(url string) string {\n\tfor {\n\t\tkey := genKey(s.Count()) // generate the short URL\n\t\tif ok := s.Set(key, url); ok {\n\t\t\treturn key\n\t\t}\n\t}\n\t// shouldn't get here\n\treturn \"\"\n}\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v2/Makefile",
    "content": "# Copyright 2009 The Go Authors. All rights reserved.\n# Use of this source code is governed by a BSD-style\n# license that can be found in the LICENSE file.\n\ninclude $(GOROOT)/src/Make.inc\n\nTARG=goto\nGOFILES=\\\n\tkey.go\\\n\tmain.go\\\n\tstore.go\\\n\ninclude $(GOROOT)/src/Make.cmd\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v2/key.go",
    "content": "package main\n\nvar keyChar = []byte(\"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\")\n\nfunc genKey(n int) string {\n\tif n == 0 {\n\t\treturn string(keyChar[0])\n\t}\n\tl := len(keyChar)\n\ts := make([]byte, 20) // FIXME: will overflow. eventually.\n\ti := len(s)\n\tfor n > 0 && i >= 0 {\n\t\ti--\n\t\tj := n % l\n\t\tn = (n - j) / l\n\t\ts[i] = keyChar[j]\n\t}\n\treturn string(s[i:])\n}\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v2/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n)\n\nvar store = NewURLStore(\"store.gob\")\n\nfunc main() {\n\thttp.HandleFunc(\"/\", Redirect)\n\thttp.HandleFunc(\"/add\", Add)\n\thttp.ListenAndServe(\":8080\", nil)\n}\n\nfunc Redirect(w http.ResponseWriter, r *http.Request) {\n\tkey := r.URL.Path[1:]\n\turl := store.Get(key)\n\tif url == \"\" {\n\t\thttp.NotFound(w, r)\n\t\treturn\n\t}\n\thttp.Redirect(w, r, url, http.StatusFound)\n}\n\nfunc Add(w http.ResponseWriter, r *http.Request) {\n\turl := r.FormValue(\"url\")\n\tif url == \"\" {\n\t\tw.Header().Set(\"Content-Type\", \"text/html\")\n\t\tfmt.Fprint(w, AddForm)\n\t\treturn\n\t}\n\tkey := store.Put(url)\n\tfmt.Fprintf(w, \"http://localhost:8080/%s\", key)\n}\n\nconst AddForm = `\n<form method=\"POST\" action=\"/add\">\nURL: <input type=\"text\" name=\"url\">\n<input type=\"submit\" value=\"Add\">\n</form>\n`\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v2/store.go",
    "content": "package main\n\nimport (\n\t\"encoding/gob\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"sync\"\n)\n\ntype URLStore struct {\n\turls map[string]string\n\tmu   sync.RWMutex\n\tfile *os.File\n}\n\ntype record struct {\n\tKey, URL string\n}\n\nfunc NewURLStore(filename string) *URLStore {\n\ts := &URLStore{urls: make(map[string]string)}\n\tf, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)\n\tif err != nil {\n\t\tlog.Fatal(\"Error opening URLStore:\", err)\n\t}\n\ts.file = f\n\tif err := s.load(); err != nil {\n\t\tlog.Println(\"Error loading URLStore:\", err)\n\t}\n\treturn s\n}\n\nfunc (s *URLStore) Get(key string) string {\n\ts.mu.RLock()\n\tdefer s.mu.RUnlock()\n\treturn s.urls[key]\n}\n\nfunc (s *URLStore) Set(key, url string) bool {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\tif _, present := s.urls[key]; present {\n\t\treturn false\n\t}\n\ts.urls[key] = url\n\treturn true\n}\n\nfunc (s *URLStore) Count() int {\n\ts.mu.RLock()\n\tdefer s.mu.RUnlock()\n\treturn len(s.urls)\n}\n\nfunc (s *URLStore) Put(url string) string {\n\tfor {\n\t\tkey := genKey(s.Count())\n\t\tif ok := s.Set(key, url); ok {\n\t\t\tif err := s.save(key, url); err != nil {\n\t\t\t\tlog.Println(\"Error saving to URLStore:\", err)\n\t\t\t}\n\t\t\treturn key\n\t\t}\n\t}\n\tpanic(\"shouldn't get here\")\n}\n\nfunc (s *URLStore) load() error {\n\tif _, err := s.file.Seek(0, 0); err != nil {\n\t\treturn err\n\t}\n\td := gob.NewDecoder(s.file)\n\tvar err error\n\tfor err == nil {\n\t\tvar r record\n\t\tif err = d.Decode(&r); err == nil {\n\t\t\ts.Set(r.Key, r.URL)\n\t\t}\n\t}\n\tif err == io.EOF {\n\t\treturn nil\n\t}\n\treturn err\n}\n\nfunc (s *URLStore) save(key, url string) error {\n\te := gob.NewEncoder(s.file)\n\treturn e.Encode(record{key, url})\n}\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v2/store.gob",
    "content": ""
  },
  {
    "path": "eBook/examples/chapter_19/goto_v3/Makefile",
    "content": "# Copyright 2009 The Go Authors. All rights reserved.\n# Use of this source code is governed by a BSD-style\n# license that can be found in the LICENSE file.\n\ninclude $(GOROOT)/src/Make.inc\n\nTARG=goto\nGOFILES=\\\n\tkey.go\\\n\tmain.go\\\n\tstore.go\\\n\ninclude $(GOROOT)/src/Make.cmd\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v3/key.go",
    "content": "package main\n\nvar keyChar = []byte(\"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\")\n\nfunc genKey(n int) string {\n\tif n == 0 {\n\t\treturn string(keyChar[0])\n\t}\n\tl := len(keyChar)\n\ts := make([]byte, 20) // FIXME: will overflow. eventually.\n\ti := len(s)\n\tfor n > 0 && i >= 0 {\n\t\ti--\n\t\tj := n % l\n\t\tn = (n - j) / l\n\t\ts[i] = keyChar[j]\n\t}\n\treturn string(s[i:])\n}\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v3/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"net/http\"\n)\n\nvar (\n\tlistenAddr = flag.String(\"http\", \":8080\", \"http listen address\")\n\tdataFile   = flag.String(\"file\", \"store.gob\", \"data store file name\")\n\thostname   = flag.String(\"host\", \"localhost:8080\", \"http host name\")\n)\n\nvar store *URLStore\n\nfunc main() {\n\tflag.Parse()\n\tstore = NewURLStore(*dataFile)\n\thttp.HandleFunc(\"/\", Redirect)\n\thttp.HandleFunc(\"/add\", Add)\n\thttp.ListenAndServe(*listenAddr, nil)\n}\n\nfunc Redirect(w http.ResponseWriter, r *http.Request) {\n\tkey := r.URL.Path[1:]\n\turl := store.Get(key)\n\tif url == \"\" {\n\t\thttp.NotFound(w, r)\n\t\treturn\n\t}\n\thttp.Redirect(w, r, url, http.StatusFound)\n}\n\nfunc Add(w http.ResponseWriter, r *http.Request) {\n\turl := r.FormValue(\"url\")\n\tif url == \"\" {\n\t\tfmt.Fprint(w, AddForm)\n\t\treturn\n\t}\n\tkey := store.Put(url)\n\tfmt.Fprintf(w, \"http://%s/%s\", *hostname, key)\n}\n\nconst AddForm = `\n<form method=\"POST\" action=\"/add\">\nURL: <input type=\"text\" name=\"url\">\n<input type=\"submit\" value=\"Add\">\n</form>\n`\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v3/store.go",
    "content": "package main\n\nimport (\n\t//\t\"bufio\"\n\t\"encoding/gob\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"sync\"\n)\n\nconst saveQueueLength = 1000\n\ntype URLStore struct {\n\turls map[string]string\n\tmu   sync.RWMutex\n\tsave chan record\n}\n\ntype record struct {\n\tKey, URL string\n}\n\nfunc NewURLStore(filename string) *URLStore {\n\ts := &URLStore{\n\t\turls: make(map[string]string),\n\t\tsave: make(chan record, saveQueueLength),\n\t}\n\tif err := s.load(filename); err != nil {\n\t\tlog.Println(\"Error loading URLStore:\", err)\n\t}\n\tgo s.saveLoop(filename)\n\treturn s\n}\n\nfunc (s *URLStore) Get(key string) string {\n\ts.mu.RLock()\n\tdefer s.mu.RUnlock()\n\treturn s.urls[key]\n}\n\nfunc (s *URLStore) Set(key, url string) bool {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\tif _, present := s.urls[key]; present {\n\t\treturn false\n\t}\n\ts.urls[key] = url\n\treturn true\n}\n\nfunc (s *URLStore) Count() int {\n\ts.mu.RLock()\n\tdefer s.mu.RUnlock()\n\treturn len(s.urls)\n}\n\nfunc (s *URLStore) Put(url string) string {\n\tfor {\n\t\tkey := genKey(s.Count())\n\t\tif ok := s.Set(key, url); ok {\n\t\t\ts.save <- record{key, url}\n\t\t\treturn key\n\t\t}\n\t}\n\tpanic(\"shouldn't get here\")\n}\n\nfunc (s *URLStore) load(filename string) error {\n\tf, err := os.Open(filename)\n\tif err != nil {\n\t\tlog.Println(\"Error opening URLStore:\", err)\n\t\treturn err\n\t}\n\tdefer f.Close()\n\t// buffered reading:\n\t// b := bufio.NewReader(f)\n\t// d := gob.NewDecoder(b)\n\td := gob.NewDecoder(f)\n\tfor err == nil {\n\t\tvar r record\n\t\tif err = d.Decode(&r); err == nil {\n\t\t\ts.Set(r.Key, r.URL)\n\t\t}\n\t}\n\tif err == io.EOF {\n\t\treturn nil\n\t}\n\t// error occurred:\n\tlog.Println(\"Error decoding URLStore:\", err) // map hasn't been read correctly\n\treturn err\n}\n\nfunc (s *URLStore) saveLoop(filename string) {\n\tf, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)\n\tif err != nil {\n\t\tlog.Fatal(\"Error opening URLStore: \", err)\n\t}\n\tdefer f.Close()\n\te := gob.NewEncoder(f)\n\t// buffered encoding:\n\t// b := bufio.NewWriter(f)\n\t// e := gob.NewEncoder(b)\n\t// defer b.Flush()\n\tfor {\n\t\tr := <-s.save // takes a record from the channel\n\t\tif err := e.Encode(r); err != nil {\n\t\t\tlog.Println(\"Error saving to URLStore: \", err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v4/Makefile",
    "content": "# Copyright 2009 The Go Authors. All rights reserved.\n# Use of this source code is governed by a BSD-style\n# license that can be found in the LICENSE file.\n\ninclude $(GOROOT)/src/Make.inc\n\nTARG=goto\nGOFILES=\\\n\tkey.go\\\n\tmain.go\\\n\tstore.go\\\n\ninclude $(GOROOT)/src/Make.cmd\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v4/key.go",
    "content": "package main\n\nvar keyChar = []byte(\"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\")\n\nfunc genKey(n int) string {\n\tif n == 0 {\n\t\treturn string(keyChar[0])\n\t}\n\tl := len(keyChar)\n\ts := make([]byte, 20) // FIXME: will overflow. eventually.\n\ti := len(s)\n\tfor n > 0 && i >= 0 {\n\t\ti--\n\t\tj := n % l\n\t\tn = (n - j) / l\n\t\ts[i] = keyChar[j]\n\t}\n\treturn string(s[i:])\n}\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v4/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"net/http\"\n)\n\nvar (\n\tlistenAddr = flag.String(\"http\", \":8080\", \"http listen address\")\n\tdataFile   = flag.String(\"file\", \"store.json\", \"data store file name\")\n\thostname   = flag.String(\"host\", \"localhost:8080\", \"http host name\")\n)\n\nvar store *URLStore\n\nfunc main() {\n\tflag.Parse()\n\tstore = NewURLStore(*dataFile)\n\thttp.HandleFunc(\"/\", Redirect)\n\thttp.HandleFunc(\"/add\", Add)\n\thttp.ListenAndServe(*listenAddr, nil)\n}\n\nfunc Redirect(w http.ResponseWriter, r *http.Request) {\n\tkey := r.URL.Path[1:]\n\turl := store.Get(key)\n\tif url == \"\" {\n\t\thttp.NotFound(w, r)\n\t\treturn\n\t}\n\thttp.Redirect(w, r, url, http.StatusFound)\n}\n\nfunc Add(w http.ResponseWriter, r *http.Request) {\n\turl := r.FormValue(\"url\")\n\tif url == \"\" {\n\t\tfmt.Fprint(w, AddForm)\n\t\treturn\n\t}\n\tkey := store.Put(url)\n\tfmt.Fprintf(w, \"http://%s/%s\", *hostname, key)\n}\n\nconst AddForm = `\n<form method=\"POST\" action=\"/add\">\nURL: <input type=\"text\" name=\"url\">\n<input type=\"submit\" value=\"Add\">\n</form>\n`\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v4/store.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"sync\"\n)\n\nconst saveQueueLength = 1000\n\ntype URLStore struct {\n\turls map[string]string\n\tmu   sync.RWMutex\n\tsave chan record\n}\n\ntype record struct {\n\tKey, URL string\n}\n\nfunc NewURLStore(filename string) *URLStore {\n\ts := &URLStore{\n\t\turls: make(map[string]string),\n\t\tsave: make(chan record, saveQueueLength),\n\t}\n\tif err := s.load(filename); err != nil {\n\t\tlog.Println(\"Error loading URLStore:\", err)\n\t}\n\tgo s.saveLoop(filename)\n\treturn s\n}\n\nfunc (s *URLStore) Get(key string) string {\n\ts.mu.RLock()\n\tdefer s.mu.RUnlock()\n\treturn s.urls[key]\n}\n\nfunc (s *URLStore) Set(key, url string) bool {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\tif _, present := s.urls[key]; present {\n\t\treturn false\n\t}\n\ts.urls[key] = url\n\treturn true\n}\n\nfunc (s *URLStore) Count() int {\n\ts.mu.RLock()\n\tdefer s.mu.RUnlock()\n\treturn len(s.urls)\n}\n\nfunc (s *URLStore) Put(url string) string {\n\tfor {\n\t\tkey := genKey(s.Count())\n\t\tif ok := s.Set(key, url); ok {\n\t\t\ts.save <- record{key, url}\n\t\t\treturn key\n\t\t}\n\t}\n\tpanic(\"shouldn't get here\")\n}\n\nfunc (s *URLStore) load(filename string) error {\n\tf, err := os.Open(filename)\n\tif err != nil {\n\t\tlog.Println(\"Error opening URLStore:\", err)\n\t\treturn err\n\t}\n\tdefer f.Close()\n\td := json.NewDecoder(f)\n\tfor err == nil {\n\t\tvar r record\n\t\tif err = d.Decode(&r); err == nil {\n\t\t\ts.Set(r.Key, r.URL)\n\t\t}\n\t}\n\tif err == io.EOF {\n\t\treturn nil\n\t}\n\t// error occurred:\n\tlog.Println(\"Error decoding URLStore:\", err) // map hasn't been read correctly\n\treturn err\n}\n\nfunc (s *URLStore) saveLoop(filename string) {\n\tf, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)\n\tif err != nil {\n\t\tlog.Fatal(\"Error opening URLStore: \", err)\n\t}\n\tdefer f.Close()\n\te := json.NewEncoder(f)\n\tfor {\n\t\tr := <-s.save // takes a record from the channel\n\t\tif err := e.Encode(r); err != nil {\n\t\t\tlog.Println(\"Error saving to URLStore: \", err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v4/store.json",
    "content": "{\"Key\":\"0\",\"URL\":\"http://www.standaard.be\"}\r\n{\"Key\":\"1\",\"URL\":\"http://www.google.be\"}\r\n{\"Key\":\"2\",\"URL\":\"http://www.gva.be\"}\r\n{\"Key\":\"3\",\"URL\":\"http://www.golang.org\"}\r\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v5/Makefile",
    "content": "# Copyright 2009 The Go Authors. All rights reserved.\n# Use of this source code is governed by a BSD-style\n# license that can be found in the LICENSE file.\n\ninclude $(GOROOT)/src/Make.inc\n\nTARG=goto\nGOFILES=\\\n\tkey.go\\\n\tmain.go\\\n\tstore.go\\\n\ninclude $(GOROOT)/src/Make.cmd\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v5/demo.sh",
    "content": "#!/bin/sh\n\ngomake\n./goto -http=:8081 -rpc=true &\nmaster_pid=$!\nsleep 1\n./goto -master=127.0.0.1:8081 &\nslave_pid=$!\necho \"Running master on :8081, slave on :8080.\"\necho \"Visit: http://localhost:8080/add\"\necho \"Press enter to shut down\"\nread\nkill $master_pid\nkill $slave_pid\n\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v5/key.go",
    "content": "package main\n\nvar keyChar = []byte(\"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\")\n\nfunc genKey(n int) string {\n\tif n == 0 {\n\t\treturn string(keyChar[0])\n\t}\n\tl := len(keyChar)\n\ts := make([]byte, 20) // FIXME: will overflow. eventually.\n\ti := len(s)\n\tfor n > 0 && i >= 0 {\n\t\ti--\n\t\tj := n % l\n\t\tn = (n - j) / l\n\t\ts[i] = keyChar[j]\n\t}\n\treturn string(s[i:])\n}\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v5/main.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/rpc\"\n)\n\nvar (\n\tlistenAddr = flag.String(\"http\", \":8080\", \"http listen address\")\n\tdataFile   = flag.String(\"file\", \"store.gob\", \"data store file name\")\n\thostname   = flag.String(\"host\", \"localhost:8080\", \"http host name\")\n\tmasterAddr = flag.String(\"master\", \"\", \"RPC master address\")\n\trpcEnabled = flag.Bool(\"rpc\", false, \"enable RPC server\")\n)\n\nvar store Store\n\nfunc main() {\n\tflag.Parse()\n\tif *masterAddr != \"\" {\n\t\tstore = NewProxyStore(*masterAddr)\n\t} else {\n\t\tstore = NewURLStore(*dataFile)\n\t}\n\tif *rpcEnabled { // the master is the rpc server:\n\t\trpc.RegisterName(\"Store\", store)\n\t\trpc.HandleHTTP()\n\t}\n\thttp.HandleFunc(\"/\", Redirect)\n\thttp.HandleFunc(\"/add\", Add)\n\thttp.ListenAndServe(*listenAddr, nil)\n\n}\n\nfunc Redirect(w http.ResponseWriter, r *http.Request) {\n\tkey := r.URL.Path[1:]\n\tif key == \"\" {\n\t\thttp.NotFound(w, r)\n\t\treturn\n\t}\n\tvar url string\n\tif err := store.Get(&key, &url); err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\thttp.Redirect(w, r, url, http.StatusFound)\n}\n\nfunc Add(w http.ResponseWriter, r *http.Request) {\n\turl := r.FormValue(\"url\")\n\tif url == \"\" {\n\t\tfmt.Fprint(w, AddForm)\n\t\treturn\n\t}\n\tvar key string\n\tif err := store.Put(&url, &key); err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\tfmt.Fprintf(w, \"http://%s/%s\", *hostname, key)\n}\n\nconst AddForm = `\n<form method=\"POST\" action=\"/add\">\nURL: <input type=\"text\" name=\"url\">\n<input type=\"submit\" value=\"Add\">\n</form>\n`\n"
  },
  {
    "path": "eBook/examples/chapter_19/goto_v5/store.go",
    "content": "package main\n\nimport (\n\t\"encoding/gob\"\n\t\"errors\"\n\t\"io\"\n\t\"log\"\n\t\"net/rpc\"\n\t\"os\"\n\t\"sync\"\n)\n\nconst saveQueueLength = 1000\n\ntype Store interface {\n\tPut(url, key *string) error\n\tGet(key, url *string) error\n}\n\ntype ProxyStore struct {\n\turls   *URLStore // local cache\n\tclient *rpc.Client\n}\n\ntype URLStore struct {\n\turls map[string]string\n\tmu   sync.RWMutex\n\tsave chan record\n}\n\ntype record struct {\n\tKey, URL string\n}\n\nfunc NewURLStore(filename string) *URLStore {\n\ts := &URLStore{urls: make(map[string]string)}\n\tif filename != \"\" {\n\t\ts.save = make(chan record, saveQueueLength)\n\t\tif err := s.load(filename); err != nil {\n\t\t\tlog.Println(\"Error loading URLStore: \", err)\n\t\t}\n\t\tgo s.saveLoop(filename)\n\t}\n\treturn s\n}\n\nfunc (s *URLStore) Get(key, url *string) error {\n\ts.mu.RLock()\n\tdefer s.mu.RUnlock()\n\tif u, ok := s.urls[*key]; ok {\n\t\t*url = u\n\t\treturn nil\n\t}\n\treturn errors.New(\"key not found\")\n}\n\nfunc (s *URLStore) Set(key, url *string) error {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\tif _, present := s.urls[*key]; present {\n\t\treturn errors.New(\"key already exists\")\n\t}\n\ts.urls[*key] = *url\n\treturn nil\n}\n\nfunc (s *URLStore) count() int {\n\ts.mu.RLock()\n\tdefer s.mu.RUnlock()\n\treturn len(s.urls)\n}\n\nfunc (s *URLStore) Put(url, key *string) error {\n\tfor {\n\t\t*key = genKey(s.count())\n\t\tif err := s.Set(key, url); err == nil {\n\t\t\tbreak\n\t\t}\n\t}\n\tif s.save != nil {\n\t\ts.save <- record{*key, *url}\n\t}\n\treturn nil\n}\n\nfunc (s *URLStore) load(filename string) error {\n\tf, err := os.Open(filename)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\td := gob.NewDecoder(f)\n\tfor err == nil {\n\t\tvar r record\n\t\tif err = d.Decode(&r); err == nil {\n\t\t\ts.Set(&r.Key, &r.URL)\n\t\t}\n\t}\n\tif err == io.EOF {\n\t\treturn nil\n\t}\n\treturn err\n}\n\nfunc (s *URLStore) saveLoop(filename string) {\n\tf, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)\n\tif err != nil {\n\t\tlog.Fatal(\"Error opening URLStore: \", err)\n\t}\n\te := gob.NewEncoder(f)\n\tfor {\n\t\tr := <-s.save\n\t\tif err := e.Encode(r); err != nil {\n\t\t\tlog.Println(\"Error saving to URLStore: \", err)\n\t\t}\n\t}\n}\n\nfunc NewProxyStore(addr string) *ProxyStore {\n\tclient, err := rpc.DialHTTP(\"tcp\", addr)\n\tif err != nil {\n\t\tlog.Println(\"Error constructing ProxyStore: \", err)\n\t\t// return // ?\n\t}\n\treturn &ProxyStore{urls: NewURLStore(\"\"), client: client}\n}\n\nfunc (s *ProxyStore) Get(key, url *string) error {\n\tif err := s.urls.Get(key, url); err == nil {\n\t\treturn nil\n\t}\n\t// rpc call to master:\n\tif err := s.client.Call(\"Store.Get\", key, url); err != nil {\n\t\treturn err\n\t}\n\ts.urls.Set(key, url) // update local cache\n\treturn nil\n}\n\nfunc (s *ProxyStore) Put(url, key *string) error {\n\t// rpc call to master:\n\tif err := s.client.Call(\"Store.Put\", url, key); err != nil {\n\t\treturn err\n\t}\n\ts.urls.Set(key, url) // update local cache\n\treturn nil\n}\n"
  },
  {
    "path": "eBook/examples/chapter_2/hello_world1.go",
    "content": "// hello_world1.go\npackage main\n\nfunc main() {\n\tprintln(\"Hello\", \"world\")\n}\n"
  },
  {
    "path": "eBook/examples/chapter_2/version.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n)\n\nfunc main() {\n\tfmt.Printf(\"%s\", runtime.Version())\n}\n\n// Output:\n// go1.0.3 or go 1.1\n"
  },
  {
    "path": "eBook/examples/chapter_20/helloapp/app.yaml",
    "content": "application: helloworld\nversion: 1\nruntime: go\napi_version: 3\n\nhandlers:\n- url: /.*\n  script: _go_app\n"
  },
  {
    "path": "eBook/examples/chapter_20/helloapp/hello/helloworld2_version1.go",
    "content": "package hello\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n)\n\nfunc init() {\n\thttp.HandleFunc(\"/\", handler)\n}\n\nfunc handler(w http.ResponseWriter, r *http.Request) {\n\tfmt.Fprint(w, \"Hello, world!\")\n}\n"
  },
  {
    "path": "eBook/examples/chapter_20/helloapp/hello/helloworld2_version2.go",
    "content": "package hello\n\nimport (\n\t\"appengine\"\n\t\"appengine/user\"\n\t\"fmt\"\n\t\"net/http\"\n)\n\nfunc init() {\n\thttp.HandleFunc(\"/\", handler)\n}\n\nfunc handler(w http.ResponseWriter, r *http.Request) {\n\tc := appengine.NewContext(r)\n\tu := user.Current(c)\n\tif u == nil {\n\t\turl, err := user.LoginURL(c, r.URL.String())\n\t\tif err != nil {\n\t\t\thttp.Error(w, err.String(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t\tw.Header().Set(\"Location\", url)\n\t\tw.WriteHeader(http.StatusFound)\n\t\treturn\n\t}\n\tfmt.Fprintf(w, \"Hello, %v!\", u)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_20/helloapp/hello/helloworld2_version3.go",
    "content": "package hello\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"template\"\n)\n\nconst guestbookForm = `\n<html>\n  <body>\n    <form action=\"/sign\" method=\"post\">\n      <div><textarea name=\"content\" rows=\"3\" cols=\"60\"></textarea></div>\n      <div><input type=\"submit\" value=\"Sign Guestbook\"></div>\n    </form>\n  </body>\n</html>\n`\nconst signTemplateHTML = `\n<html>\n  <body>\n    <p>You wrote:</p>\n    <pre>{{html .}}</pre>\n  </body>\n</html>\n`\n\nvar signTemplate = template.Must(template.New(\"sign\").Parse(signTemplateHTML))\n\nfunc init() {\n\thttp.HandleFunc(\"/\", root)\n\thttp.HandleFunc(\"/sign\", sign)\n}\n\nfunc root(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"Content-Type\", \"text/html\")\n\tfmt.Fprint(w, guestbookForm)\n}\n\nfunc sign(w http.ResponseWriter, r *http.Request) {\n\tw.Header().Set(\"Content-Type\", \"text/html\")\n\terr := signTemplate.Execute(w, r.FormValue(\"content\"))\n\tif err != nil {\n\t\thttp.Error(w, err.String(), http.StatusInternalServerError)\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_20/helloapp/hello/helloworld2_version4.go",
    "content": "package hello\n\nimport (\n\t\"appengine\"\n\t\"appengine/datastore\"\n\t\"appengine/user\"\n\t\"net/http\"\n\t\"template\"\n\t\"time\"\n)\n\nconst guestbookTemplateHTML = `\n<html>\n  <body>\n    {{range .}}\n      {{with .Author}}\n        <p><b>{{html .}}</b> wrote:</p>\n      {{else}}\n        <p>An anonymous person wrote:</p>\n      {{end}}\n      <pre>{{html .Content}}</pre>\n      <pre>{{html .Date}}</pre>\n    {{end}}\n    <form action=\"/sign\" method=\"post\">\n      <div><textarea name=\"content\" rows=\"3\" cols=\"60\"></textarea></div>\n      <div><input type=\"submit\" value=\"Sign Guestbook\"></div>\n    </form>\n  </body>\n</html>\n`\n\nvar guestbookTemplate = template.Must(template.New(\"book\").Parse(guestbookTemplateHTML))\n\ntype Greeting struct {\n\tAuthor  string\n\tContent string\n\tDate    datastore.Time\n}\n\nfunc init() {\n\thttp.HandleFunc(\"/\", root)\n\thttp.HandleFunc(\"/sign\", sign)\n}\n\nfunc root(w http.ResponseWriter, r *http.Request) {\n\tc := appengine.NewContext(r)\n\tq := datastore.NewQuery(\"Greeting\").Order(\"-Date\").Limit(10)\n\tgreetings := make([]Greeting, 0, 10)\n\tif _, err := q.GetAll(c, &greetings); err != nil {\n\t\thttp.Error(w, err.String(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\tif err := guestbookTemplate.Execute(w, greetings); err != nil {\n\t\thttp.Error(w, err.String(), http.StatusInternalServerError)\n\t}\n}\n\nfunc sign(w http.ResponseWriter, r *http.Request) {\n\tc := appengine.NewContext(r)\n\tg := Greeting{\n\t\tContent: r.FormValue(\"content\"),\n\t\tDate:    datastore.SecondsToTime(time.Seconds()),\n\t}\n\tif u := user.Current(c); u != nil {\n\t\tg.Author = u.String()\n\t}\n\t_, err := datastore.Put(c, datastore.NewIncompleteKey(c, \"Greeting\", nil), &g)\n\tif err != nil {\n\t\thttp.Error(w, err.String(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\thttp.Redirect(w, r, \"/\", http.StatusFound)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_20/helloworld.go",
    "content": "// helloworld.go\npackage helloworld\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n)\n\nfunc init() {\n\thttp.HandleFunc(\"/\", handle)\n}\n\nfunc handle(w http.ResponseWriter, r *http.Request) {\n\tfmt.Fprint(w, \"<html><body>Hello, World! 세상아 안녕!! </body></html>\")\n}\n"
  },
  {
    "path": "eBook/examples/chapter_3/CandGo/Makefile",
    "content": "include $(GOROOT)/src/Make.inc\r\n\r\nTARG=rand\r\nCGOFILES=\\\r\n   c1.go\\\r\n\r\ninclude $(GOROOT)/src/Make.pkg"
  },
  {
    "path": "eBook/examples/chapter_3/CandGo/c1.go",
    "content": "// c1.go\npackage rand\n\n// #include <stdlib.h>\nimport \"C\"\n\nfunc Random() int {\n\treturn int(C.random())\n}\n\nfunc Seed(i int) {\n\tC.srandom(C.uint(i))\n}\n"
  },
  {
    "path": "eBook/examples/chapter_3/CandGo/c2.go",
    "content": "// c2.go\npackage print\n\n// #include <stdio.h>\n// #include <stdlib.h>\nimport \"C\"\nimport \"unsafe\"\n\nfunc Print(s string) {\n\tcs := C.CString(s)\n\tdefer C.free(unsafe.Pointer(cs))\n\tC.fputs(cs, (*C.FILE)(C.stdout))\n}\n"
  },
  {
    "path": "eBook/examples/chapter_3/Makefile",
    "content": "        include $(GOROOT)/src/Make.inc\r\nTARG=test\r\nGOFILES=\\\r\n\ttest1.go\\\r\ntest2.go\\\r\n\r\ninclude $(GOROOT)/src/Make.cmd\r\n"
  },
  {
    "path": "eBook/examples/chapter_3/gocomp",
    "content": "#!/bin/bash\r\nFILES=~/goprograms/compiletest/*.go\r\nfor f in $FILES\r\ndo\r\n  echo \"Processing $f file...\"\r\n  # take action on each file. $f stores current file name\r\n  # cat $f\r\n  6g $f >> compout\r\ndone\r\n"
  },
  {
    "path": "eBook/examples/chapter_3/gocomp.bat",
    "content": "FOR %%X in (*.go) DO 8g %%X >> compout\r\n\r\n"
  },
  {
    "path": "eBook/examples/chapter_3/run.cmd",
    "content": "set GOROOT=E:\\Go\\GoforWindows\\gowin32_release.r59\\go\r\nset GOBIN=$GOROOT\\bin\r\nset PATH=%PATH%;$GOBIN\r\nset GOARCH=386\r\nset GOOS=windows\r\necho off\r\n8g %1.go\r\n8l -o %1.exe %1.8\r\n%1\r\n"
  },
  {
    "path": "eBook/examples/chapter_4/alias.go",
    "content": "package main\n\nimport fm \"fmt\" // alias\n\nfunc main() {\n\tfm.Println(\"hello, world\")\n}\n"
  },
  {
    "path": "eBook/examples/chapter_4/casting.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar n int16 = 34\n\tvar m int32\n\n\t// compiler error: cannot use n (type int16) as type int32 in assignment\n\t//m = n\n\tm = int32(n)\n\n\tfmt.Printf(\"32 bit int is:  %d\\n\", m)\n\tfmt.Printf(\"16 bit int is:  %d\\n\", n)\n}\n\n/* Output:\n32 bit int is:  34\n16 bit int is:  34\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_4/char.go",
    "content": "// char.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tvar ch int = '\\u0041'\n\tvar ch2 int = '\\u03B2'\n\tvar ch3 int = '\\U00101234'\n\tfmt.Printf(\"%d - %d - %d\\n\", ch, ch2, ch3)\n\tfmt.Printf(\"%c - %c - %c\\n\", ch, ch2, ch3)\n\tfmt.Printf(\"%X - %X - %X\\n\", ch, ch2, ch3)\n\tfmt.Printf(\"%U - %U - %U\", ch, ch2, ch3)\n}\n\n/* Ouput:\n65 - 946 - 1053236\nA - β - 􁈴\n41 - 3B2 - 101234\nU+0041 - U+03B2 - U+101234\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_4/count_substring.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc main() {\n\tvar str string = \"Hello, how is it going, Hugo?\"\n\tvar manyG = \"gggggggggg\"\n\n\tfmt.Printf(\"Number of H's in %s is: \", str)\n\tfmt.Printf(\"%d\\n\", strings.Count(str, \"H\"))\n\n\tfmt.Printf(\"Number of double g's in %s is: \", manyG)\n\tfmt.Printf(\"%d\\n\", strings.Count(manyG, \"gg\"))\n}\n"
  },
  {
    "path": "eBook/examples/chapter_4/function_calls_function.go",
    "content": "package main\n\nvar a string\n\nfunc main() {\n\ta = \"G\"\n\tprint(a)\n\tf1()\n}\nfunc f1() {\n\ta := \"O\"\n\tprint(a)\n\tf2()\n}\nfunc f2() {\n\tprint(a)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_4/global_scope.go",
    "content": "package main\n\nvar a = \"G\"\n\nfunc main() {\n\tn()\n\tm()\n\tn()\n}\nfunc n() {\n\tprint(a)\n}\nfunc m() {\n\ta = \"O\"\n\tprint(a)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_4/goos.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"runtime\"\n)\n\nfunc main() {\n\tvar goos string = runtime.GOOS\n\tfmt.Printf(\"The operating system is: %s\\n\", goos)\n\tpath := os.Getenv(\"PATH\")\n\tfmt.Printf(\"Path is %s\\n\", path)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_4/gotemplate.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n)\n\nconst c = \"C\"\n\nvar v int = 5\n\ntype T struct{}\n\nfunc init() {\n\t// initialization of package\n}\n\nfunc main() {\n\tvar a int\n\tFunc1()\n\t// ...\n\tfmt.Println(a)\n}\n\nfunc (t T) Method1() {\n\t//...\n}\n\nfunc Func1() { // exported function Func1\n\t//...\n}\n"
  },
  {
    "path": "eBook/examples/chapter_4/hello_world.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"hello, world\")\n}\n"
  },
  {
    "path": "eBook/examples/chapter_4/hello_world2.go",
    "content": "package main\n\nimport \"fmt\" // Package implementing formatted I/O.\n\nfunc main() {\n\tfmt.Printf(\"Hello, world; or Καλημέρα κόσμε; or こんにちは 世界\\n\")\n}\n"
  },
  {
    "path": "eBook/examples/chapter_4/index_in_string.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc main() {\n\tvar str string = \"Hi, I'm Marc, Hi.\"\n\n\tfmt.Printf(\"The position of \\\"Marc\\\" is: \")\n\tfmt.Printf(\"%d\\n\", strings.Index(str, \"Marc\"))\n\n\tfmt.Printf(\"The position of the first instance of \\\"Hi\\\" is: \")\n\tfmt.Printf(\"%d\\n\", strings.Index(str, \"Hi\"))\n\tfmt.Printf(\"The position of the last instance of \\\"Hi\\\" is: \")\n\tfmt.Printf(\"%d\\n\", strings.LastIndex(str, \"Hi\"))\n\n\tfmt.Printf(\"The position of \\\"Burger\\\" is: \")\n\tfmt.Printf(\"%d\\n\", strings.Index(str, \"Burger\"))\n}\n"
  },
  {
    "path": "eBook/examples/chapter_4/init.go",
    "content": "package trans\n\nimport \"math\"\n\nvar Pi float64\n\nfunc init() {\n\tPi = 4 * math.Atan(1) // init() function computes Pi\n}\n"
  },
  {
    "path": "eBook/examples/chapter_4/local_scope.go",
    "content": "package main\n\nvar a = \"G\"\n\nfunc main() {\n\tn()\n\tm()\n\tn()\n}\nfunc n() { print(a) }\nfunc m() {\n\ta := \"O\"\n\tprint(a)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_4/pointer.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar i1 = 5\n\tfmt.Printf(\"An integer: %d, its location in memory: %p\\n\", i1, &i1)\n\n\tvar intP *int\n\tintP = &i1\n\tfmt.Printf(\"The value at memory location %p is %d\\n\", intP, *intP)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_4/presuffix.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc main() {\n\tvar str string = \"This is an example of a string\"\n\n\tfmt.Printf(\"T/F? Does the string \\\"%s\\\" have prefix %s? \", str, \"Th\")\n\tfmt.Printf(\"%t\\n\", strings.HasPrefix(str, \"Th\"))\n}\n\n// Output: T/F? Does the string \"This is an example of a string\" have prefix Th? true\n"
  },
  {
    "path": "eBook/examples/chapter_4/random.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"time\"\n)\n\nfunc main() {\n\tfor i := 0; i < 10; i++ {\n\t\ta := rand.Int()\n\t\tfmt.Printf(\"%d / \", a)\n\t}\n\tfor i := 0; i < 5; i++ {\n\t\tr := rand.Intn(8)\n\t\tfmt.Printf(\"%d / \", r)\n\t}\n\tfmt.Println()\n\ttimens := int64(time.Now().Nanosecond())\n\trand.Seed(timens)\n\tfor i := 0; i < 10; i++ {\n\t\tfmt.Printf(\"%2.2f / \", 100*rand.Float32())\n\t}\n}\n\n/* Output:\n134020434 / 1597969999 / 1721070109 / 2068675587 / 1237770961 / 220031192 / 2031484958 / 583324308 / 958990240 / 413002649 / 6 / 7 / 2 / 1 / 0 /\n22.84 / 10.12 / 44.32 / 58.58 / 15.49 / 12.23 / 30.16 / 88.48 / 34.26 / 27.18 /\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_4/repeat_string.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc main() {\n\tvar origS string = \"Hi there! \"\n\tvar newS string\n\n\tnewS = strings.Repeat(origS, 3)\n\tfmt.Printf(\"The new repeated string is: %s\\n\", newS)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_4/string_conversion.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n)\n\nfunc main() {\n\tvar orig string = \"666\"\n\tvar an int\n\tvar newS string\n\n\tfmt.Printf(\"The size of ints is: %d\\n\", strconv.IntSize)\n\n\tan, _ = strconv.Atoi(orig)\n\tfmt.Printf(\"The integer is: %d\\n\", an)\n\tan = an + 5\n\tnewS = strconv.Itoa(an)\n\tfmt.Printf(\"The new string is: %s\\n\", newS)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_4/string_pointer.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\ts := \"good bye\"\n\tvar p *string = &s\n\t*p = \"ciao\"\n\n\tfmt.Printf(\"Here is the pointer p: %p\\n\", p)  // prints address\n\tfmt.Printf(\"Here is the string *p: %s\\n\", *p) // prints string\n\tfmt.Printf(\"Here is the string  s: %s\\n\", s)  // prints same string\n}\n"
  },
  {
    "path": "eBook/examples/chapter_4/strings_splitjoin.go",
    "content": "// strings.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc main() {\n\tstr := \"The quick brown fox jumps over the lazy dog\"\n\tsl := strings.Fields(str)\n\tfmt.Printf(\"Splitted in slice: %v\\n\", sl)\n\tfor _, val := range sl {\n\t\tfmt.Printf(\"%s - \", val)\n\t}\n\tfmt.Println()\n\tstr2 := \"GO1|The ABC of Go|25\"\n\tsl2 := strings.Split(str2, \"|\")\n\tfmt.Printf(\"Splitted in slice: %v\\n\", sl2)\n\tfor _, val := range sl2 {\n\t\tfmt.Printf(\"%s - \", val)\n\t}\n\tfmt.Println()\n\tstr3 := strings.Join(sl2, \";\")\n\tfmt.Printf(\"sl2 joined by ;: %s\\n\", str3)\n}\n\n/* Output:\nSplitted in slice: [The quick brown fox jumps over the lazy dog]\nThe - quick - brown - fox - jumps - over - the - lazy - dog -\nSplitted in slice: [GO1 The ABC of Go 25]\nGO1 - The ABC of Go - 25 -\nsl2 joined by ;: GO1;The ABC of Go;25\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_4/testcrash.go",
    "content": "// testcrash.go\n// compiles , but crashes\npackage main\n\nfunc main() {\n\tvar p *int = nil\n\t*p = 0\n\n}\n\n// in Windows: stops only with: <exit code=\"-1073741819\" msg=\"process crashed\"/>\n// runtime error: invalid memory address or nil pointer dereference\n"
  },
  {
    "path": "eBook/examples/chapter_4/time.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nvar week time.Duration\n\nfunc main() {\n\tt := time.Now()\n\tfmt.Println(t)                                              // Wed Dec 21 09:52:14 +0100 RST 2011\n\tfmt.Printf(\"%02d.%02d.%4d\\n\", t.Day(), t.Month(), t.Year()) // 21.12.2011\n\tt = time.Now().UTC()\n\tfmt.Println(t)          // Wed Dec 21 08:52:14 +0000 UTC 2011\n\tfmt.Println(time.Now()) // Wed Dec 21 09:52:14 +0100 RST 2011\n\t// calculating times:\n\tweek = 60 * 60 * 24 * 7 * 1e9 // must be in nanosec\n\tweek_from_now := t.Add(week)\n\tfmt.Println(week_from_now) // Wed Dec 28 08:52:14 +0000 UTC 2011\n\t// formatting times:\n\tfmt.Println(t.Format(time.RFC822))         // 21 Dec 11 0852 UTC\n\tfmt.Println(t.Format(time.ANSIC))          // Wed Dec 21 08:56:34 2011\n\tfmt.Println(t.Format(\"02 Jan 2006 15:04\")) // 21 Dec 2011 08:52\n\ts := t.Format(\"20060102\")\n\tfmt.Println(t, \"=>\", s) // Wed Dec 21 08:52:14 +0000 UTC 2011 => 20111221\n}\n"
  },
  {
    "path": "eBook/examples/chapter_4/toupper_lower.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc main() {\n\tvar orig string = \"Hey, how are you George?\"\n\tvar lower string\n\tvar upper string\n\n\tfmt.Printf(\"The original string is: %s\\n\", orig)\n\tlower = strings.ToLower(orig)\n\tfmt.Printf(\"The lowercase string is: %s\\n\", lower)\n\tupper = strings.ToUpper(orig)\n\tfmt.Printf(\"The uppercase string is: %s\\n\", upper)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_4/type.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype TZ int\n\nfunc main() {\n\tvar a, b TZ = 3, 4\n\tc := a + b\n\tfmt.Printf(\"c has the value: %d\", c)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_4/type_mixing.go",
    "content": "package main\n\nfunc main() {\n\tvar a int\n\tvar b int32\n\ta = 15\n\tb = a + a // compiler error\n\tb = b + 5 // ok: 5 is a constant\n}\n"
  },
  {
    "path": "eBook/examples/chapter_4/use_init.go",
    "content": "package main\n\nimport (\n\t\"./trans\"\n\t\"fmt\"\n)\n\nvar twoPi = 2 * trans.Pi // decl computes twoPi\n\nfunc main() {\n\tfmt.Printf(\"2*Pi = %g\\n\", twoPi) // 2*Pi = 6.283185307179586\n}\n"
  },
  {
    "path": "eBook/examples/chapter_4/user_init.go",
    "content": "package main\n\nimport (\n\t\"./trans\"\n\t\"fmt\"\n)\n\nvar twoPi = 2 * trans.Pi\n\nfunc main() {\n\tfmt.Printf(\"2*Pi = %g\\n\", twoPi) // 2*Pi = 6.283185307179586\n}\n"
  },
  {
    "path": "eBook/examples/chapter_5/booleans.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tbool1 := true\n\tif bool1 {\n\t\tfmt.Printf(\"The value is true\\n\")\n\t} else {\n\t\tfmt.Printf(\"The value is false\\n\")\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_5/for1.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfor i := 0; i < 5; i++ {\n\t\tfmt.Printf(\"This is the %d iteration\\n\", i)\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_5/for2.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar i int = 5\n\n\tfor i >= 0 {\n\t\ti = i - 1\n\t\tfmt.Printf(\"The variable i is now: %d\\n\", i)\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_5/for3.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar i int = 5\n\n\tfor {\n\t\ti = i - 1\n\t\tfmt.Printf(\"The variable i is now: %d\\n\", i)\n\t\tif i < 0 {\n\t\t\tbreak\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_5/for4.go",
    "content": "package main\n\nfunc main() {\n\tfor i := 0; i < 3; i++ {\n\t\tfor j := 0; j < 10; j++ {\n\t\t\tif j > 5 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tprint(j)\n\t\t}\n\t\tprint(\"  \")\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_5/for5.go",
    "content": "package main\n\nfunc main() {\n\tfor i := 0; i < 10; i++ {\n\t\tif i == 5 {\n\t\t\tcontinue\n\t\t}\n\t\tprint(i)\n\t\tprint(\" \")\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_5/for6.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\nLABEL1:\n\tfor i := 0; i <= 5; i++ {\n\t\tfor j := 0; j <= 5; j++ {\n\t\t\tif j == 4 {\n\t\t\t\tcontinue LABEL1\n\t\t\t}\n\t\t\tfmt.Printf(\"i is: %d, and j is: %d\\n\", i, j)\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "eBook/examples/chapter_5/for_string.go",
    "content": "// for_string.go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tstr := \"Go is a beautiful language!\"\n\tfmt.Printf(\"The length of str is: %d\\n\", len(str))\n\tfor ix := 0; ix < len(str); ix++ {\n\t\tfmt.Printf(\"Character on position %d is: %c \\n\", ix, str[ix])\n\t}\n\tstr2 := \"日本語\"\n\tfmt.Printf(\"The length of str2 is: %d\\n\", len(str2))\n\tfor ix := 0; ix < len(str2); ix++ {\n\t\tfmt.Printf(\"Character on position %d is: %c \\n\", ix, str2[ix])\n\t}\n}\n\n/* Output:\nThe length of str is: 27\nCharacter on position 0 is: G\nCharacter on position 1 is: o\nCharacter on position 2 is:\nCharacter on position 3 is: i\nCharacter on position 4 is: s\nCharacter on position 5 is:\nCharacter on position 6 is: a\nCharacter on position 7 is:\nCharacter on position 8 is: b\nCharacter on position 9 is: e\nCharacter on position 10 is: a\nCharacter on position 11 is: u\nCharacter on position 12 is: t\nCharacter on position 13 is: i\nCharacter on position 14 is: f\nCharacter on position 15 is: u\nCharacter on position 16 is: l\nCharacter on position 17 is:\nCharacter on position 18 is: l\nCharacter on position 19 is: a\nCharacter on position 20 is: n\nCharacter on position 21 is: g\nCharacter on position 22 is: u\nCharacter on position 23 is: a\nCharacter on position 24 is: g\nCharacter on position 25 is: e\nCharacter on position 26 is: !\nThe length of str2 is: 9\nCharacter on position 0 is: æ\nCharacter on position 1 is: \nCharacter on position 2 is: ¥\nCharacter on position 3 is: æ\nCharacter on position 4 is: \nCharacter on position 5 is: ¬\nCharacter on position 6 is: è\nCharacter on position 7 is: ª\nCharacter on position 8 is: \n*/\n"
  },
  {
    "path": "eBook/examples/chapter_5/goto.go",
    "content": "package main\n\nfunc main() {\n\ti := 0\nHERE:\n\tprint(i)\n\ti++\n\tif i == 5 {\n\t\treturn\n\t}\n\tgoto HERE\n}\n"
  },
  {
    "path": "eBook/examples/chapter_5/goto2.go",
    "content": "// compile error goto2.go:8: goto TARGET jumps over declaration of b at goto2.go:8\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\ta := 1\n\tgoto TARGET // compile error\n\tb := 9\nTARGET:\n\tb += a\n\tfmt.Printf(\"a is %v *** b is %v\", a, b)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_5/ifelse.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar first int = 10\n\tvar cond int\n\n\tif first <= 0 {\n\t\tfmt.Printf(\"first is less than or equal to 0\\n\")\n\t} else if first > 0 && first < 5 {\n\t\tfmt.Printf(\"first is between 0 and 5\\n\")\n\t} else {\n\t\tfmt.Printf(\"first is 5 or greater\\n\")\n\t}\n\n\tif cond = 5; cond > 10 {\n\t\tfmt.Printf(\"cond is greater than 10\\n\")\n\t} else {\n\t\tfmt.Printf(\"cond is not greater than 10\\n\")\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_5/range_string.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tstr := \"Go is a beautiful language!\"\n\tfmt.Printf(\"The length of str is: %d\\n\", len(str))\n\tfor pos, char := range str {\n\t\tfmt.Printf(\"Character on position %d is: %c \\n\", pos, char)\n\t}\n\tfmt.Println()\n\tstr2 := \"Chinese: 日本語\"\n\tfmt.Printf(\"The length of str2 is: %d\\n\", len(str2))\n\tfor pos, char := range str2 {\n\t\tfmt.Printf(\"character %c starts at byte position %d\\n\", char, pos)\n\t}\n\tfmt.Println()\n\tfmt.Println(\"index int(rune) rune    char bytes\")\n\tfor index, rune := range str2 {\n\t\tfmt.Printf(\"%-2d      %d      %U '%c' % X\\n\", index, rune, rune, rune, []byte(string(rune)))\n\t}\n}\n\n/* Output:\nThe length of str is: 27\nCharacter on position 0 is: G\nCharacter on position 1 is: o\nCharacter on position 2 is:\nCharacter on position 3 is: i\nCharacter on position 4 is: s\nCharacter on position 5 is:\nCharacter on position 6 is: a\nCharacter on position 7 is:\nCharacter on position 8 is: b\nCharacter on position 9 is: e\nCharacter on position 10 is: a\nCharacter on position 11 is: u\nCharacter on position 12 is: t\nCharacter on position 13 is: i\nCharacter on position 14 is: f\nCharacter on position 15 is: u\nCharacter on position 16 is: l\nCharacter on position 17 is:\nCharacter on position 18 is: l\nCharacter on position 19 is: a\nCharacter on position 20 is: n\nCharacter on position 21 is: g\nCharacter on position 22 is: u\nCharacter on position 23 is: a\nCharacter on position 24 is: g\nCharacter on position 25 is: e\nCharacter on position 26 is: !\n\nThe length of str2 is: 18\ncharacter C starts at byte position 0\ncharacter h starts at byte position 1\ncharacter i starts at byte position 2\ncharacter n starts at byte position 3\ncharacter e starts at byte position 4\ncharacter s starts at byte position 5\ncharacter e starts at byte position 6\ncharacter : starts at byte position 7\ncharacter   starts at byte position 8\ncharacter 日 starts at byte position 9\ncharacter 本 starts at byte position 12\ncharacter 語 starts at byte position 15\n\nindex int(rune) rune    char bytes\n0       67      U+0043 'C' 43\n1       104      U+0068 'h' 68\n2       105      U+0069 'i' 69\n3       110      U+006E 'n' 6E\n4       101      U+0065 'e' 65\n5       115      U+0073 's' 73\n6       101      U+0065 'e' 65\n7       58      U+003A ':' 3A\n8       32      U+0020 ' ' 20\n9       26085      U+65E5 '日' E6 97 A5\n12      26412      U+672C '本' E6 9C AC\n15      35486      U+8A9E '語' E8 AA 9E\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_5/string_conversion2.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n)\n\nfunc main() {\n\tvar orig string = \"ABC\"\n\t// var an int\n\tvar newS string\n\t// var err error\n\n\tfmt.Printf(\"The size of ints is: %d\\n\", strconv.IntSize)\n\t// anInt, err = strconv.Atoi(origStr)\n\tan, err := strconv.Atoi(orig)\n\tif err != nil {\n\t\tfmt.Printf(\"orig %s is not an integer - exiting with error\\n\", orig)\n\t\treturn\n\t}\n\tfmt.Printf(\"The integer is %d\\n\", an)\n\tan = an + 5\n\tnewS = strconv.Itoa(an)\n\tfmt.Printf(\"The new string is: %s\\n\", newS)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_5/switch1.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar num1 int = 100\n\n\tswitch num1 {\n\tcase 98, 99:\n\t\tfmt.Println(\"It's equal to 98\")\n\tcase 100:\n\t\tfmt.Println(\"It's equal to 100\")\n\tdefault:\n\t\tfmt.Println(\"It's not equal to 98 or 100\")\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_5/switch2.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar num1 int = 7\n\n\tswitch {\n\tcase num1 < 0:\n\t\tfmt.Println(\"Number is negative\")\n\tcase num1 > 0 && num1 < 10:\n\t\tfmt.Println(\"Number is between 0 and 10\")\n\tdefault:\n\t\tfmt.Println(\"Number is 10 or greater\")\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_6/blank_identifier.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar i1 int\n\tvar f1 float32\n\ti1, _, f1 = ThreeValues()\n\tfmt.Printf(\"The int: %d, the float; %f\\n\", i1, f1)\n}\n\nfunc ThreeValues() (int, int, float32) {\n\treturn 5, 6, 7.5\n}\n"
  },
  {
    "path": "eBook/examples/chapter_6/defer.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tFunction1()\n}\n\nfunc Function1() {\n\tfmt.Printf(\"In Function1 at the top\\n\")\n\tdefer Function2()\n\tfmt.Printf(\"In Function1 at the bottom!\\n\")\n}\n\nfunc Function2() {\n\tfmt.Printf(\"Function2: Deferred until the end of the calling function!\")\n}\n"
  },
  {
    "path": "eBook/examples/chapter_6/defer_dbconn.go",
    "content": "// defer_dbconn.go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tdoDBOperations()\n}\n\nfunc connectToDB() {\n\tfmt.Println(\"ok, connected to db\")\n}\n\nfunc disconnectFromDB() {\n\tfmt.Println(\"ok, disconnected from db\")\n}\n\nfunc doDBOperations() {\n\tconnectToDB()\n\tfmt.Println(\"Defering the database disconnect.\")\n\tdefer disconnectFromDB() //function called here with defer\n\tfmt.Println(\"Doing some DB operations ...\")\n\tfmt.Println(\"Oops! some crash or network error ...\")\n\tfmt.Println(\"Returning from function here!\")\n\treturn //terminate the program\n\t// deferred function executed here just before actually returning, even if there is a return or abnormal termination before\n}\n\n/* Output:\nok, connected to db\nDefering the database disconnect.\nDoing some DB operations ...\nOops! some crash or network error ...\nReturning from function here!\nok, disconnected from db\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_6/defer_logvalues.go",
    "content": "// defer_logvalues.go\npackage main\n\nimport (\n\t\"io\"\n\t\"log\"\n)\n\nfunc func1(s string) (n int, err error) {\n\tdefer func() {\n\t\tlog.Printf(\"func1(%q) = %d, %v\", s, n, err)\n\t}()\n\treturn 7, io.EOF\n}\n\nfunc main() {\n\tfunc1(\"Go\")\n}\n\n// Output: 2011/10/04 10:46:11 func1(\"Go\") = 7, EOF\n"
  },
  {
    "path": "eBook/examples/chapter_6/defer_tracing.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc trace(s string)   { fmt.Println(\"entering:\", s) }\nfunc untrace(s string) { fmt.Println(\"leaving:\", s) }\n\nfunc a() {\n\ttrace(\"a\")\n\tdefer untrace(\"a\")\n\tfmt.Println(\"in a\")\n}\n\nfunc b() {\n\ttrace(\"b\")\n\tdefer untrace(\"b\")\n\tfmt.Println(\"in b\")\n\ta()\n}\nfunc main() {\n\tb()\n}\n"
  },
  {
    "path": "eBook/examples/chapter_6/defer_tracing2.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc trace(s string) string {\n\tfmt.Println(\"entering:\", s)\n\treturn s\n}\nfunc un(s string) {\n\tfmt.Println(\"leaving:\", s)\n}\n\nfunc a() {\n\tdefer un(trace(\"a\"))\n\tfmt.Println(\"in a\")\n}\n\nfunc b() {\n\tdefer un(trace(\"b\"))\n\tfmt.Println(\"in b\")\n\ta()\n}\nfunc main() {\n\tb()\n}\n"
  },
  {
    "path": "eBook/examples/chapter_6/fibonacci.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc main() {\n\tresult := 0\n\tstart := time.Now()\n\tfor i := 0; i <= 25; i++ {\n\t\tresult = fibonacci(i)\n\t\tfmt.Printf(\"fibonacci(%d) is: %d\\n\", i, result)\n\t}\n\tend := time.Now()\n\tdelta := end.Sub(start)\n\tfmt.Printf(\"longCalculation took this amount of time: %s\\n\", delta)\n}\n\nfunc fibonacci(n int) (res int) {\n\tif n <= 1 {\n\t\tres = 1\n\t} else {\n\t\tres = fibonacci(n-1) + fibonacci(n-2)\n\t}\n\treturn\n}\n\n/* Output:\nfibonacci(0) is: 1\nfibonacci(1) is: 1\nfibonacci(2) is: 2\nfibonacci(3) is: 3\nfibonacci(4) is: 5\nfibonacci(5) is: 8\nfibonacci(6) is: 13\nfibonacci(7) is: 21\nfibonacci(8) is: 34\nfibonacci(9) is: 55\nfibonacci(10) is: 89\nfibonacci(11) is: 144\nfibonacci(12) is: 233\nfibonacci(13) is: 377\nfibonacci(14) is: 610\nfibonacci(15) is: 987\nfibonacci(16) is: 1597\nfibonacci(17) is: 2584\nfibonacci(18) is: 4181\nfibonacci(19) is: 6765\nfibonacci(20) is: 10946\nfibonacci(21) is: 17711\nfibonacci(22) is: 28657\nfibonacci(23) is: 46368\nfibonacci(24) is: 75025\nfibonacci(25) is: 121393\nlongCalculation took this amount of time: 3.0001ms\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_6/fibonacci_memoization.go",
    "content": "// fibonacci_memoization.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nconst LIM = 41\n\nvar fibs [LIM]uint64\n\nfunc main() {\n\tvar result uint64 = 0\n\tstart := time.Now()\n\tfor i := 0; i < LIM; i++ {\n\t\tresult = fibonacci(i)\n\t\tfmt.Printf(\"fibonacci(%d) is: %d\\n\", i, result)\n\t}\n\tend := time.Now()\n\tdelta := end.Sub(start)\n\tfmt.Printf(\"longCalculation took this amount of time: %s\\n\", delta)\n}\n\nfunc fibonacci(n int) (res uint64) {\n\t// memoization: check if fibonacci(n) is already known in array:\n\tif fibs[n] != 0 {\n\t\tres = fibs[n]\n\t\treturn\n\t}\n\tif n <= 1 {\n\t\tres = 1\n\t} else {\n\t\tres = fibonacci(n-1) + fibonacci(n-2)\n\t}\n\tfibs[n] = res\n\treturn\n}\n\n/*\nOutput: LIM=40:\nnormal (fibonacci.go): the calculation took this amount of time: 4.730270 s\n     with memoization: the calculation took this amount of time: 0.001000 s\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_6/filter_factory.go",
    "content": "// filter_factory.go\npackage main\n\nimport \"fmt\"\n\ntype flt func(int) bool\ntype slice_split func([]int) ([]int, []int)\n\nfunc isOdd(integer int) bool {\n\tif integer%2 == 0 {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc isBiggerThan4(integer int) bool {\n\tif integer > 4 {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc filter_factory(f flt) slice_split {\n\treturn func(s []int) (yes, no []int) {\n\t\tfor _, val := range s {\n\t\t\tif f(val) {\n\t\t\t\tyes = append(yes, val)\n\t\t\t} else {\n\t\t\t\tno = append(no, val)\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n}\n\nfunc main() {\n\ts := []int{1, 2, 3, 4, 5, 7}\n\tfmt.Println(\"s = \", s)\n\todd_even_function := filter_factory(isOdd)\n\todd, even := odd_even_function(s)\n\tfmt.Println(\"odd = \", odd)\n\tfmt.Println(\"even = \", even)\n\t//separate those that are bigger than 4 and those that are not.\n\tbigger, smaller := filter_factory(isBiggerThan4)(s)\n\tfmt.Println(\"Bigger than 4: \", bigger)\n\tfmt.Println(\"Smaller than or equal to 4: \", smaller)\n}\n\n/*\ns =  [1 2 3 4 5 7]\nodd =  [1 3 5 7]\neven =  [2 4]\nBigger than 4:  [5 7]\nSmaller than or equal to 4:  [1 2 3 4]\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_6/function_closure.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar f = Adder()\n\tfmt.Print(f(1), \" - \")\n\tfmt.Print(f(20), \" - \")\n\tfmt.Print(f(300))\n}\n\nfunc Adder() func(int) int {\n\tvar x int\n\treturn func(delta int) int {\n\t\tx += delta\n\t\treturn x\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_6/function_filter.go",
    "content": "// function_filter.go\npackage main\n\nimport \"fmt\"\n\ntype flt func(int) bool\n\n// isOdd takes an ints and returns a bool set to true if the\n// int parameter is odd, or false if not.\n// isOdd is of type func(int) bool which is what flt is declared to be.\n\nfunc isOdd(n int) bool {\n\tif n%2 == 0 {\n\t\treturn false\n\t}\n\treturn true\n}\n\n// Same comment for isEven\nfunc isEven(n int) bool {\n\tif n%2 == 0 {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc filter(sl []int, f flt) []int {\n\tvar res []int\n\tfor _, val := range sl {\n\t\tif f(val) {\n\t\t\tres = append(res, val)\n\t\t}\n\t}\n\treturn res\n}\n\nfunc main() {\n\tslice := []int{1, 2, 3, 4, 5, 7}\n\tfmt.Println(\"slice = \", slice)\n\todd := filter(slice, isOdd)\n\tfmt.Println(\"Odd elements of slice are: \", odd)\n\teven := filter(slice, isEven)\n\tfmt.Println(\"Even elements of slice are: \", even)\n}\n\n/*\nslice =  [1 2 3 4 5 7]\nOdd elements of slice are:  [1 3 5 7]\nEven elements of slice are:  [2 4]\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_6/function_literal.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tf()\n}\n\nfunc f() {\n\tfor i := 0; i < 4; i++ {\n\t\tg := func(i int) { fmt.Printf(\"%d \", i) }\n\t\tg(i)\n\t\tfmt.Printf(\" - g is of type %T and has value %v\\n\", g, g)\n\t}\n\n}\n"
  },
  {
    "path": "eBook/examples/chapter_6/function_parameter.go",
    "content": "// function_parameter.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tcallback(1, Add)\n}\n\nfunc Add(a, b int) {\n\tfmt.Printf(\"The sum of %d and %d is: %d\\n\", a, b, a+b)\n}\n\nfunc callback(y int, f func(int, int)) {\n\tf(y, 2) // this becomes Add(1, 2)\n}\n\n// Output:  The sum of 1 and 2 is: 3\n"
  },
  {
    "path": "eBook/examples/chapter_6/function_return.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\t// make an Add2 function, give it a name p2, and call it:\n\tp2 := Add2()\n\tfmt.Printf(\"Call Add2 for 2 gives: %v\\n\", p2(2))\n\t// make a special Adder function, a gets value 2:\n\tTwoAdder := Adder(2)\n\tfmt.Printf(\"The result is: %v\\n\", TwoAdder(2))\n}\n\nfunc Add2() func(b int) int {\n\treturn func(b int) int {\n\t\treturn b + 2\n\t}\n}\n\nfunc Adder(a int) func(b int) int {\n\treturn func(b int) int {\n\t\treturn a + b\n\t}\n}\n\n/* Output:\nCall Add2 for 2 gives: 4\nThe result is: 4\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_6/greeting.go",
    "content": "package main\n\nfunc main() {\n\tprintln(\"In main before calling greeting\")\n\tgreeting()\n\tprintln(\"In main after calling greeting\")\n}\n\nfunc greeting() {\n\tprintln(\"In greeting: Hi!!!!!\")\n}\n"
  },
  {
    "path": "eBook/examples/chapter_6/minmax.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar min, max int\n\tmin, max = MinMax(78, 65)\n\tfmt.Printf(\"Minimum is: %d, Maximum is: %d\\n\", min, max)\n}\n\nfunc MinMax(a int, b int) (min int, max int) {\n\tif a < b {\n\t\tmin = a\n\t\tmax = b\n\t} else { // a = b or a < b\n\t\tmin = b\n\t\tmax = a\n\t}\n\treturn\n}\n"
  },
  {
    "path": "eBook/examples/chapter_6/multiple_return.go",
    "content": "package main\n\nimport \"fmt\"\n\nvar num int = 10\nvar numx2, numx3 int\n\nfunc main() {\n\tnumx2, numx3 = getX2AndX3(num)\n\tPrintValues()\n\tnumx2, numx3 = getX2AndX3_2(num)\n\tPrintValues()\n}\n\nfunc PrintValues() {\n\tfmt.Printf(\"num = %d, 2x num = %d, 3x num = %d\\n\", num, numx2, numx3)\n}\n\nfunc getX2AndX3(input int) (int, int) {\n\treturn 2 * input, 3 * input\n}\n\nfunc getX2AndX3_2(input int) (x2 int, x3 int) {\n\tx2 = 2 * input\n\tx3 = 3 * input\n\t// return x2, x3\n\treturn\n}\n"
  },
  {
    "path": "eBook/examples/chapter_6/mut_recurs.go",
    "content": "// mut_recurs.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tfmt.Printf(\"%d is even: is %t\\n\", 16, even(16)) // 16 is even: is true\n\tfmt.Printf(\"%d is odd: is %t\\n\", 17, odd(17))   // 17 is odd: is true\n\tfmt.Printf(\"%d is odd: is %t\\n\", 18, odd(18))   // 18 is odd: is false\n}\n\nfunc even(nr int) bool {\n\tif nr == 0 {\n\t\treturn true\n\t}\n\treturn odd(RevSign(nr) - 1)\n}\n\nfunc odd(nr int) bool {\n\tif nr == 0 {\n\t\treturn false\n\t}\n\treturn even(RevSign(nr) - 1)\n}\n\nfunc RevSign(nr int) int {\n\tif nr < 0 {\n\t\treturn -nr\n\t}\n\treturn nr\n}\n"
  },
  {
    "path": "eBook/examples/chapter_6/return_defer.go",
    "content": "// test_defer.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc f() (ret int) {\n\tdefer func() {\n\t\tret++\n\t}()\n\treturn 1\n}\n\nfunc main() {\n\tfmt.Println(f())\n}\n\n// Output: 2\n"
  },
  {
    "path": "eBook/examples/chapter_6/side_effect.go",
    "content": "// side_effect.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc Multiply(a, b int, reply *int) {\n\t*reply = a * b\n}\n\nfunc main() {\n\tn := 0\n\treply := &n\n\tMultiply(10, 5, reply)\n\tfmt.Println(\"Multiply:\", *reply) // Multiply: 50\n\tfmt.Println(\"Multiply:\", n)      // Multiply: 50\n}\n"
  },
  {
    "path": "eBook/examples/chapter_6/simple_function.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar i1 int = MultiPly3Nums(2, 5, 6)\n\tfmt.Printf(\"Multiply 2 * 5 * 6 = %d\\n\", i1)\n\t// fmt.Printf(\"Multiply 2 * 5 * 6 = %d\\n\", MultiPly3Nums(2, 5, 6))\n}\n\nfunc MultiPly3Nums(a int, b int, c int) int {\n\t// var product int = a * b * c\n\t// return product\n\treturn a * b * c\n}\n"
  },
  {
    "path": "eBook/examples/chapter_6/varnumpar.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tx := Min(1, 3, 2, 0)\n\tfmt.Printf(\"The minimum is: %d\\n\", x)\n\tslice := []int{7, 9, 3, 5, 1}\n\tx = Min(slice...)\n\tfmt.Printf(\"The minimum in the slice is: %d\", x)\n}\n\nfunc Min(s ...int) int {\n\tif len(s) == 0 {\n\t\treturn 0\n\t}\n\tmin := s[0]\n\tfor _, v := range s {\n\t\tif v < min {\n\t\t\tmin = v\n\t\t}\n\t}\n\treturn min\n}\n\n/*\nThe minimum is: 0\nThe minimum in the slice is: 1\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_7/array_literals.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar arrAge = [5]int{18, 20, 15, 22, 16}\n\tvar arrLazy = [...]int{5, 6, 7, 8, 22}\n\t// var arrLazy = []int{5, 6, 7, 8, 22}\n\tvar arrKeyValue = [5]string{3: \"Chris\", 4: \"Ron\"}\n\t//var arrKeyValue = []string{3: \"Chris\", 4: \"Ron\"}\n\tfor i := 0; i < len(arrAge); i++ {\n\t\tfmt.Printf(\"Age at %d is %d\\n\", i, arrAge[i])\n\t}\n\tfmt.Println()\n\n\tfor i := 0; i < len(arrLazy); i++ {\n\t\tfmt.Printf(\"Number at %d is %d\\n\", i, arrLazy[i])\n\t}\n\tfmt.Printf(\"\\n\")\n\n\tfor i := 0; i < len(arrKeyValue); i++ {\n\t\tfmt.Printf(\"Person at %d is %s\\n\", i, arrKeyValue[i])\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_7/array_slices.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar arr1 [6]int\n\tvar slice1 []int = arr1[2:5] // index 5 niet meegerekend!\n\n\t// load the array with integers: 0,1,2,3,4,5\n\tfor i := 0; i < len(arr1); i++ {\n\t\tarr1[i] = i\n\t}\n\n\t// print the slice:\n\tfor i := 0; i < len(slice1); i++ {\n\t\tfmt.Printf(\"Slice at %d is %d\\n\", i, slice1[i])\n\t}\n\n\tfmt.Printf(\"The length of arr1 is %d\\n\", len(arr1))\n\tfmt.Printf(\"The length of slice1 is %d\\n\", len(slice1))\n\tfmt.Printf(\"The capacity of slice1 is %d\\n\", cap(slice1))\n\n\t// grow the slice:\n\tslice1 = slice1[0:4]\n\tfor i := 0; i < len(slice1); i++ {\n\t\tfmt.Printf(\"Slice at %d is %d\\n\", i, slice1[i])\n\t}\n\tfmt.Printf(\"The length of slice1 is %d\\n\", len(slice1))\n\tfmt.Printf(\"The capacity of slice1 is %d\\n\", cap(slice1))\n\n\t// grow the slice beyond capacity:\n\t// slice1 = slice1[0:7 ] // panic: runtime error: slice bounds out of range\n}\n"
  },
  {
    "path": "eBook/examples/chapter_7/array_sum.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tarray := [3]float64{7.0, 8.5, 9.1}\n\tx := Sum(&array) // Note the explicit address-of operator to pass a pointer to the array\n\tfmt.Printf(\"The sum of the array is: %f\", x)\n}\n\nfunc Sum(a *[3]float64) (sum float64) {\n\tfor _, v := range a { // can also with dereferencing *a to get back to the array\n\t\tsum += v\n\t}\n\treturn\n}\n\n// Output: The sum of the array is: 24.600000\n"
  },
  {
    "path": "eBook/examples/chapter_7/copy_append_slice.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tslFrom := []int{1, 2, 3}\n\tslTo := make([]int, 10)\n\n\tn := copy(slTo, slFrom)\n\tfmt.Println(slTo)                    // output: [1 2 3 0 0 0 0 0 0 0]\n\tfmt.Printf(\"Copied %d elements\\n\", n) // n == 3\n\n\tsl3 := []int{1, 2, 3}\n\tsl3 = append(sl3, 4, 5, 6)\n\tfmt.Println(sl3) // output: [1 2 3 4 5 6]\n}\n"
  },
  {
    "path": "eBook/examples/chapter_7/for_arrays.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar arr1 [5]int\n\t// var arr1 = new([5]int)\n\n\tfor i := 0; i < len(arr1); i++ {\n\t\tarr1[i] = i * 2\n\t}\n\n\tfor i := 0; i < len(arr1); i++ {\n\t\tfmt.Printf(\"Array at index %d is %d\\n\", i, arr1[i])\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_7/for_string.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\ts := \"\\u00ff\\u754c\"\n\tfor i, c := range s {\n\t\tfmt.Printf(\"%d:%c \", i, c)\n\t}\n}\n\n// prints: 0:ÿ 2:界\n"
  },
  {
    "path": "eBook/examples/chapter_7/make_slice.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\t// var slice1 []int = make([]int, 10)\n\tslice1 := make([]int, 10)\n\t// load the array/slice:\n\tfor i := 0; i < len(slice1); i++ {\n\t\tslice1[i] = 5 * i\n\t}\n\t// print the slice:\n\tfor i := 0; i < len(slice1); i++ {\n\t\tfmt.Printf(\"Slice at %d is %d\\n\", i, slice1[i])\n\t}\n\tfmt.Printf(\"\\nThe length of slice1 is %d\\n\", len(slice1))\n\tfmt.Printf(\"The capacity of slice1 is %d\\n\", cap(slice1))\n}\n"
  },
  {
    "path": "eBook/examples/chapter_7/multidim_array.go",
    "content": "// multidim_array.go\npackage main\n\nimport \"fmt\"\n\nconst (\n\tWIDTH  = 1920\n\tHEIGHT = 1080\n\t// WIDTH =\t5\n\t// HEIGHT = 4\n)\n\ntype pixel int\n\nvar screen [WIDTH][HEIGHT]pixel\n\nfunc main() {\n\tfor y := 0; y < HEIGHT; y++ {\n\t\tfor x := 0; x < WIDTH; x++ {\n\t\t\tscreen[x][y] = 0\n\t\t}\n\t}\n\tfmt.Println(screen)\n\n\tfor row := range screen {\n\t\tfor column := range screen[0] {\n\t\t\tscreen[row][column] = 1\n\t\t}\n\t}\n\n\tfmt.Println(screen)\n}\n\n/* Output for WIDTH =\t5 and \tHEIGHT = 4:\n[[0 0 0 0] [0 0 0 0] [0 0 0 0] [0 0 0 0] [0 0 0 0]]\n[[1 1 1 1] [1 1 1 1] [1 1 1 1] [1 1 1 1] [1 1 1 1]]\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_7/pointer_array.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc f(a [3]int)   { fmt.Println(a) }\nfunc fp(a *[3]int) { fmt.Println(a) }\n\nfunc main() {\n\tvar ar [3]int\n\tf(ar)   // passes a copy of ar\n\tfp(&ar) // passes a pointer to ar\n}\n"
  },
  {
    "path": "eBook/examples/chapter_7/pointer_array2.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc fp(a *[3]int) { fmt.Println(a) }\n\nfunc main() {\n\tfor i := 0; i < 3; i++ {\n\t\tfp(&[3]int{i, i * i, i * i * i})\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_7/reslicing.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\t//var slice1 []int = make([]int, 0, 10)\n\tslice1 := make([]int, 0, 10)\n\t// load the slice, cap(slice1) is 10:\n\tfor i := 0; i < cap(slice1); i++ {\n\t\tslice1 = slice1[0 : i+1] // reslice\n\t\tslice1[i] = i\n\t\tfmt.Printf(\"The length of slice is %d\\n\", len(slice1))\n\t}\n\t// print the slice:\n\tfor i := 0; i < len(slice1); i++ {\n\t\tfmt.Printf(\"Slice at %d is %d\\n\", i, slice1[i])\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_7/slices_forrange.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar slice1 []int = make([]int, 4)\n\n\tslice1[0] = 1\n\tslice1[1] = 2\n\tslice1[2] = 3\n\tslice1[3] = 4\n\n\tfor ix, value := range slice1 {\n\t\tfmt.Printf(\"Slice at %d is: %d\\n\", ix, value)\n\t}\n}\n\n/*\nSlice at 0 is: 1\nSlice at 1 is: 2\nSlice at 2 is: 3\nSlice at 3 is: 4\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_7/slices_forrange2.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tseasons := []string{\"Spring\", \"Summer\", \"Autumn\", \"Winter\"}\n\n\tfor ix, season := range seasons {\n\t\tfmt.Printf(\"Season %d is: %s\\n\", ix, season)\n\t}\n\n\tvar season string\n\tfor _, season = range seasons {\n\t\tfmt.Printf(\"%s\\n\", season)\n\t}\n\n\tfor ix := range seasons {\n\t\tfmt.Printf(\"%d \", ix)\n\t}\n}\n\n/* Output:\nSeason 0 is: Spring\nSeason 1 is: Summer\nSeason 2 is: Autumn\nSeason 3 is: Winter\nSpring\nSummer\nAutumn\nWinter\n0 1 2 3\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_8/Makefile.txt",
    "content": "include $(GOROOT)/src/Make.inc\r\nTARG=pack1\r\nGOFILES=\\\r\n\tpack1.go\\\r\n\tpack1b.go\\\r\n\r\ninclude $(GOROOT)/src/Make.pkg\r\n"
  },
  {
    "path": "eBook/examples/chapter_8/invert_map.go",
    "content": "// invert_map.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nvar (\n\tbarVal = map[string]int{\"alpha\": 34, \"bravo\": 56, \"charlie\": 23, \"delta\": 87,\n\t\t\"echo\": 56, \"foxtrot\": 12, \"golf\": 34, \"hotel\": 16, \"indio\": 87, \"juliet\": 65, \"kilo\": 43, \"lima\": 98}\n)\n\nfunc main() {\n\tinvMap := make(map[int]string, len(barVal))\n\tfor k, v := range barVal {\n\t\tinvMap[v] = k\n\t}\n\tfmt.Println(\"inverted:\")\n\tfor k, v := range invMap {\n\t\tfmt.Printf(\"Key: %v, Value: %v / \", k, v)\n\t}\n}\n\n/* Output:\ninverted:\nKey: 12, Value: foxtrot / Key: 16, Value: hotel / Key: 87, Value: delta / Key: 23, Value: charlie /\nKey: 65, Value: juliet / Key: 43, Value: kilo / Key: 56, Value: bravo / Key: 98, Value: lima /\nKey: 34, Value: golf /\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_8/make_maps.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar mapLit map[string]int\n\t//var mapCreated map[string]float32\n\tvar mapAssigned map[string]int\n\n\tmapLit = map[string]int{\"one\": 1, \"two\": 2}\n\tmapCreated := make(map[string]float32)\n\tmapAssigned = mapLit\n\n\tmapCreated[\"key1\"] = 4.5\n\tmapCreated[\"key2\"] = 3.14159\n\tmapAssigned[\"two\"] = 3\n\n\tfmt.Printf(\"Map literal at \\\"one\\\" is: %d\\n\", mapLit[\"one\"])\n\tfmt.Printf(\"Map created at \\\"key2\\\" is: %f\\n\", mapCreated[\"key2\"])\n\tfmt.Printf(\"Map assigned at \\\"two\\\" is: %d\\n\", mapLit[\"two\"])\n\tfmt.Printf(\"Map literal at \\\"ten\\\" is: %d\\n\", mapLit[\"ten\"])\n}\n\n/* Output:\nMap literal at \"one\" is: 1\nMap created at \"key2\" is: 3.141590\nMap assigned at \"two\" is: 3\nMap literal at \"ten\" is: 0\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_8/map_func.go",
    "content": "// map_func.go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tmf := map[int]func() int{\n\t\t1: func() int { return 10 },\n\t\t2: func() int { return 20 },\n\t\t5: func() int { return 50 },\n\t}\n\tfmt.Println(mf)\n}\n\n// Output:  map[1:0x10903be0 5:0x10903ba0 2:0x10903bc0]\n"
  },
  {
    "path": "eBook/examples/chapter_8/map_testelement.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar value int\n\tvar isPresent bool\n\n\tmap1 := make(map[string]int)\n\tmap1[\"New Delhi\"] = 55\n\tmap1[\"Bejing\"] = 20\n\tmap1[\"Washington\"] = 25\n\n\tvalue, isPresent = map1[\"Bejing\"]\n\tif isPresent {\n\t\tfmt.Printf(\"The value of \\\"Bejing\\\" in map1 is: %d\\n\", value)\n\t} else {\n\t\tfmt.Println(\"map1 does not contain Bejing\")\n\t}\n\n\tvalue, isPresent = map1[\"Paris\"]\n\tfmt.Printf(\"Is \\\"Paris\\\" in map1 ?: %t\\n\", isPresent)\n\tfmt.Printf(\"Value is: %d\\n\", value)\n\n\t// delete an item:\n\tdelete(map1, \"Washington\")\n\tvalue, isPresent = map1[\"Washington\"]\n\tif isPresent {\n\t\tfmt.Printf(\"The value of \\\"Washington\\\" in map1 is: %d\\n\", value)\n\t} else {\n\t\tfmt.Println(\"map1 does not contain Washington\")\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_8/maps_forrange.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tmap1 := make(map[int]float32)\n\tmap1[1] = 1.0\n\tmap1[2] = 2.0\n\tmap1[3] = 3.0\n\tmap1[4] = 4.0\n\n\tfor key, value := range map1 {\n\t\tfmt.Printf(\"key is: %d - value is: %f\\n\", key, value)\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_8/maps_forrange2.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\t// Version A:\n\titems := make([]map[int]int, 5)\n\tfor i := range items {\n\t\titems[i] = make(map[int]int, 1)\n\t\titems[i][1] = 2\n\t}\n\tfmt.Printf(\"Version A: Value of items: %v\\n\", items)\n\n\t// Version B: NOT GOOD!\n\titems2 := make([]map[int]int, 5)\n\tfor _, item := range items2 {\n\t\titem = make(map[int]int, 1) // item is only a copy of the slice element.\n\t\titem[1] = 2                 // This 'item' will be lost on the next iteration.\n\t}\n\tfmt.Printf(\"Version B: Value of items: %v\\n\", items2)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_8/slice_maps.go",
    "content": "// slice_maps.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\t// Version A:\n\titems := make([]map[int]int, 5)\n\tfor i := range items {\n\t\titems[i] = make(map[int]int, 1)\n\t\titems[i][1] = 2\n\t}\n\tfmt.Printf(\"Version A: Value of items: %v\\n\", items)\n\t// Version B:\n\titems2 := make([]map[int]int, 5)\n\tfor _, item := range items2 {\n\t\titem = make(map[int]int, 1) // item is only a copy of the slice element.\n\t\titem[1] = 2                 // This 'item' will be lost on the next iteration.\n\t}\n\tfmt.Printf(\"Version B: Value of items: %v\\n\", items2)\n}\n\n/* Output:\nVersion A: Value of items: [map[1:2] map[1:2] map[1:2] map[1:2] map[1:2]]\nVersion B: Value of items: [map[] map[] map[] map[] map[]]\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_8/sort_map.go",
    "content": "// sort_map.go\n// the telephone alphabet:\npackage main\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n)\n\nvar (\n\tbarVal = map[string]int{\"alpha\": 34, \"bravo\": 56, \"charlie\": 23, \"delta\": 87,\n\t\t\"echo\": 56, \"foxtrot\": 12, \"golf\": 34, \"hotel\": 16, \"indio\": 87, \"juliet\": 65, \"kilo\": 43, \"lima\": 98}\n)\n\nfunc main() {\n\tfmt.Println(\"unsorted:\")\n\tfor k, v := range barVal {\n\t\tfmt.Printf(\"Key: %v, Value: %v / \", k, v)\n\t}\n\tkeys := make([]string, len(barVal))\n\ti := 0\n\tfor k := range barVal {\n\t\tkeys[i] = k\n\t\ti++\n\t}\n\tsort.Strings(keys)\n\tfmt.Println()\n\tfmt.Println(\"sorted:\")\n\tfor _, k := range keys {\n\t\tfmt.Printf(\"Key: %v, Value: %v / \", k, barVal[k])\n\t}\n}\n\n/* Output:\nunsorted:\nKey: indio, Value: 87 / Key: echo, Value: 56 / Key: juliet, Value: 65 / Key: charlie, Value: 23 /\nKey: hotel, Value: 16 / Key: lima, Value: 98 / Key: bravo, Value: 56 / Key: alpha, Value: 34 /\nKey: kilo, Value: 43 / Key: delta, Value: 87 / Key: golf, Value: 34 / Key: foxtrot, Value: 12 /\nsorted:\nKey: alpha, Value: 34 / Key: bravo, Value: 56 / Key: charlie, Value: 23 / Key: delta, Value: 87 /\nKey: echo, Value: 56 / Key: foxtrot, Value: 12 / Key: golf, Value: 34 / Key: hotel, Value: 16 /\nKey: indio, Value: 87 / Key: juliet, Value: 65 / Key: kilo, Value: 43 / Key: lima, Value: 98 /\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_9/big.go",
    "content": "// big.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"math/big\"\n)\n\nfunc main() {\n\t// Here are some calculations with bigInts:\n\tim := big.NewInt(math.MaxInt64)\n\tin := im\n\tio := big.NewInt(1956)\n\tip := big.NewInt(1)\n\tip.Mul(im, in).Add(ip, im).Div(ip, io)\n\tfmt.Printf(\"Big Int: %v\\n\", ip)\n\t// Here are some calculations with bigInts:\n\trm := big.NewRat(math.MaxInt64, 1956)\n\trn := big.NewRat(-1956, math.MaxInt64)\n\tro := big.NewRat(19, 56)\n\trp := big.NewRat(1111, 2222)\n\trq := big.NewRat(1, 1)\n\trq.Mul(rm, rn).Add(rq, ro).Mul(rq, rp)\n\tfmt.Printf(\"Big Rat: %v\\n\", rq)\n}\n\n/* Output:\nBig Int: 43492122561469640008497075573153004\nBig Rat: -37/112\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_9/book/book_main/main.go",
    "content": "package main\n\nimport (\n\t\"book/pack1\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tvar test1 string\n\ttest1 = pack1.ReturnStr()\n\tfmt.Printf(\"ReturnStr from package1: %s\\n\", test1)\n\tfmt.Printf(\"Integer from package1: %d\\n\", pack1.Pack1Int)\n\t//fmt.Printf(\"Float from package1: %f\\n\", pack1.pack1Float)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_9/book/pack1/pack1.go",
    "content": "package pack1\n\nvar Pack1Int int = 42\nvar pack1Float = 3.14\n\nfunc ReturnStr() string {\n\treturn \"Hello main!\"\n}\n"
  },
  {
    "path": "eBook/examples/chapter_9/book/package_mytest.go",
    "content": "package main\n\nimport (\n\t\"./pack1/pack1\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tvar test1 string\n\ttest1 = pack1.ReturnStr()\n\tfmt.Printf(\"ReturnStr from package1: %s\\n\", test1)\n\tfmt.Printf(\"Integer from package1: %d\\n\", pack1.Pack1Int)\n\t// fmt.Printf(“Float from package1: %f\\n”, pack1.pack1Float)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_9/doc_example/Package sort - The Go Programming Language_files/all.css",
    "content": "/* General Styles */\nbody {\n  font-family: \"Bitstream Vera Sans\", Verdana, sans-serif;\n  font-size: 81.25%;\n  line-height: 1.23em;\n  padding: 0;\n  margin: 1.23em;\n  background: white;\n  color: black;\n}\na {\n  color: #04a;\n  text-decoration: none;\n}\na:visited {\n  color: #04a;\n}\na:hover {\n  color: #a40;\n  text-decoration: underline;\n}\na:active {\n  color: #c00;\n}\ncode, pre {\n  font-size: 1.2em; \n}\npre {\n  background: #F0F0F0;\n  padding: 0.5em 1em;\n}\n\n/* Top bar */\n#container {\n  width: 100%;\n  margin: auto;\n}\n#topnav {\n  height: 55px;\n  background: url(/doc/logo.png) no-repeat top left;\n}\na#logo-box {\n  display: block;\n  height: 55px;\n}\nh1#title {\n  display: none;\n}\n#nav-main {\n  float: right;\n  width: 500px;\n  margin-top: -5px;\n  text-align: center;\n}\n#nav-main ul {\n  padding-left: 0;\n  margin-left: 0;\n  margin-bottom: 0.5em;\n}\n#nav-main li a {\n  display: inline;\n  display: inline-block;\n  padding: .46em .62em .38em .62em;\n}\n#nav-main li a:link,\n#nav-main li a:visited {\n  color: #000;\n}\n#nav-main li {\n  display: inline;\n  display: inline-block;\n  background: #e6e6e6 url(/doc/button_background.png) repeat-x;\n  border: solid 1px #999;\n  margin-left: -1px;\n  text-shadow: #fff 0 1px 0;\n  box-shadow: 0 1px 1px #ccc;\n  -moz-box-shadow: 0 1px 1px #ccc;\n  -webkit-box-shadow: 0 1px 1px #ccc;\n}\n#nav-main li:first-child {\n  -moz-border-top-left-radius: 4px;\n  border-top-left-radius: 4px;\n  -moz-border-bottom-left-radius: 4px;\n  border-bottom-left-radius: 4px;\n}\n#nav-main li:last-child {\n  -moz-border-top-right-radius: 4px;\n  border-top-right-radius: 4px;\n  -moz-border-bottom-right-radius: 4px;\n  border-bottom-right-radius: 4px;\n}\n#nav-main .quickref {\n  color: #444;\n}\n#nav-main .quickref .sep {\n  color: #999;\n}\n#search {\n  width: 120px;\n  margin-left: 0.5em;\n}\n#search.inactive {\n  text-align: center;\n  color: #444;\n}\n\n/* Footer */\n#site-info {\n  position: relative;\n  text-align: center;\n}\n#site-info, #site-info a:link, #site-info a:visited {\n  color: #aaa;\n}\n\n/* Content */\n#content {\n  clear: both;\n  padding: 0;\n  position: relative;\n  margin-top: 1.5em;\n  margin-bottom: 1.5em;\n  border-top: solid 1px #aaa;\n  border-bottom: solid 1px #aaa;\n}\n.left-column {\n  width: 49%;\n  float: left;\n}\n.right-column {\n  width: 49%;\n  float: right;\n}\n.end-columns {\n  clear: both;\n}\n#content h1 {\n  margin-bottom: -0em;\n  padding: 0;\n}\n#content h2 {\n  border-top: 2px solid #ddd;\n  padding: 8px 0;\n  margin: 1.5em 0 0;\n}\n#content .subtitle {\n  margin-top: 1em;\n  display: block;\n}\n.navtop a {\n  font-weight: normal; font-size: 7pt; \n  float: right; color: #999;\n}\n\n/* Content and Code Highlighting */\npre.ebnf, pre.grammar {\n  background: #FFFFE0;\n}\nspan.ln {\n  font-size: 80%;\n  color: #777777;\n}\nspan.comment {\n  color: #002090;\n}\nspan.highlight {\n  background: #FF9900;\n  font-weight: bold;\n}\nspan.highlight-comment {\n  background: #FF9900;\n  font-weight: bold;\n  color: #002090;\n}\nspan.selection {\n  background: #FFFF00\n}\nspan.selection-comment {\n  color: #002090;\n  background: #FFFF00\n}\nspan.selection-highlight {\n  background: #FF9900;\n  font-weight: bold;\n}\nspan.selection-highlight-comment {\n  background: #FF9900;\n  font-weight: bold;\n  color: #002090;\n}\nspan.alert {\n  color: #D00000;\n}\n#nav table {\n  width: 100%;\n}\n.detail {\n  padding: 0.25em 1em;\n  background: #F4F4F4;\n}\nsup.new {\n  color: red;\n  font-size: 8px;\n  line-height: 0;\n}\n"
  },
  {
    "path": "eBook/examples/chapter_9/doc_example/Package sort - The Go Programming Language_files/godocs.js",
    "content": "// Except as noted, this content is licensed under Creative Commons\n// Attribution 3.0\n\n/* A little code to ease navigation of these documents.\n *\n * On window load we:\n *  + Generate a table of contents (godocs_generateTOC)\n *  + Add links up to the top of the doc from each section (godocs_addTopLinks)\n */\n\n/* We want to do some stuff on page load (after the HTML is rendered).\n   So listen for that:\n */\nfunction bindEvent(el, e, fn) {\n  if (el.addEventListener){\n    el.addEventListener(e, fn, false);\n  } else if (el.attachEvent){\n    el.attachEvent('on'+e, fn);\n  }\n}\nbindEvent(window, 'load', godocs_onload);\n\nfunction godocs_onload() {\n  godocs_bindSearchEvents();\n  godocs_generateTOC();\n  godocs_addTopLinks();\n}\n\nfunction godocs_bindSearchEvents() {\n  var search = document.getElementById('search');\n  if (!search) {\n    // no search box (index disabled)\n    return;\n  }\n  function clearInactive() {\n    if (search.className == \"inactive\") {\n      search.value = \"\";\n      search.className = \"\";\n    }\n  }\n  function restoreInactive() {\n    if (search.value != \"\") {\n      return;\n    }\n    if (search.type != \"search\") {\n      search.value = search.getAttribute(\"placeholder\");\n    }\n    search.className = \"inactive\";\n  }\n  restoreInactive();\n  bindEvent(search, 'focus', clearInactive);\n  bindEvent(search, 'blur', restoreInactive);\n}\n\n/* Generates a table of contents: looks for h2 and h3 elements and generates\n * links.  \"Decorates\" the element with id==\"nav\" with this table of contents.\n */\nfunction godocs_generateTOC() {\n  var navbar = document.getElementById('nav');\n  if (!navbar) { return; }\n\n  var toc_items = [];\n\n  var i;\n  for (i = 0; i < navbar.parentNode.childNodes.length; i++) {\n    var node = navbar.parentNode.childNodes[i];\n    if ((node.tagName == 'h2') || (node.tagName == 'H2')) {\n      if (!node.id) {\n        node.id = 'tmp_' + i;\n      }\n      var text = godocs_nodeToText(node);\n      if (!text) { continue; }\n\n      var textNode = document.createTextNode(text);\n\n      var link = document.createElement('a');\n      link.href = '#' + node.id;\n      link.appendChild(textNode);\n\n      // Then create the item itself\n      var item = document.createElement('dt');\n\n      item.appendChild(link);\n      toc_items.push(item);\n    }\n    if ((node.tagName == 'h3') || (node.tagName == 'H3')) {\n      if (!node.id) {\n        node.id = 'tmp_' + i;\n      }\n      var text = godocs_nodeToText(node);\n      if (!text) { continue; }\n\n      var textNode = document.createTextNode(text);\n\n      var link = document.createElement('a');\n      link.href = '#' + node.id;\n      link.appendChild(textNode);\n\n      // Then create the item itself\n      var item = document.createElement('dd');\n\n      item.appendChild(link);\n      toc_items.push(item);\n    }\n  }\n\n  if (toc_items.length <= 1) { return; }\n\n  var dl1 = document.createElement('dl');\n  var dl2 = document.createElement('dl');\n\n  var split_index = (toc_items.length / 2) + 1;\n  if (split_index < 8) {\n    split_index = toc_items.length;\n  }\n\n  for (i = 0; i < split_index; i++) {\n    dl1.appendChild(toc_items[i]);\n  }\n  for (/* keep using i */; i < toc_items.length; i++) {\n    dl2.appendChild(toc_items[i]);\n  }\n\n  var tocTable = document.createElement('table');\n  navbar.appendChild(tocTable);\n  tocTable.className = 'unruled';\n  var tocBody = document.createElement('tbody');\n  tocTable.appendChild(tocBody);\n\n  var tocRow = document.createElement('tr');\n  tocBody.appendChild(tocRow);\n\n  // 1st column\n  var tocCell = document.createElement('td');\n  tocCell.className = 'first';\n  tocRow.appendChild(tocCell);\n  tocCell.appendChild(dl1);\n\n  // 2nd column\n  tocCell = document.createElement('td');\n  tocRow.appendChild(tocCell);\n  tocCell.appendChild(dl2);\n}\n\n/* Returns the \"This sweet header\" from <h2>This <i>sweet</i> header</h2>.\n * Takes a node, returns a string.\n */\nfunction godocs_nodeToText(node) {\n  var TEXT_NODE = 3; // Defined in Mozilla but not MSIE :(\n\n  var text = '';\n  for (var j = 0; j != node.childNodes.length; j++) {\n    var child = node.childNodes[j];\n    if (child.nodeType == TEXT_NODE) {\n      if (child.nodeValue != '[Top]') { //ok, that's a hack, but it works.\n        text = text + child.nodeValue;\n      }\n    } else {\n      text = text + godocs_nodeToText(child);\n    }\n  }\n  return text;\n}\n\n/* For each H2 heading, add a link up to the #top of the document.\n * (As part of this: ensure existence of 'top' named anchor link\n * (theoretically at doc's top).)\n */\nfunction godocs_addTopLinks() {\n  /* Make sure there's a \"top\" to link to. */\n  var top = document.getElementById('top');\n  if (!top) {\n    document.body.id = 'top';\n  }\n\n  if (!document.getElementsByTagName) return; // no browser support\n\n  var headers = document.getElementsByTagName('h2');\n\n  for (var i = 0; i < headers.length; i++) {\n    var span = document.createElement('span');\n    span.className = 'navtop';\n    var link = document.createElement('a');\n    span.appendChild(link);\n    link.href = '#top';\n    var textNode = document.createTextNode('[Top]');\n    link.appendChild(textNode);\n    headers[i].appendChild(span);\n  }\n}\n"
  },
  {
    "path": "eBook/examples/chapter_9/doc_example/sort.go",
    "content": "// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Sorting using a general interface\npackage sort\n\n// Sorting interface\ntype Interface interface {\n\tLen() int\n\tLess(i, j int) bool\n\tSwap(i, j int)\n}\n\n// General sort function\nfunc Sort(data Interface) {\n\tfor i := 1; i < data.Len(); i++ {\n\t\tfor j := i; j > 0 && data.Less(j, j-1); j-- {\n\t\t\tdata.Swap(j, j-1)\n\t\t}\n\t}\n}\n\n// Test if data is sorted\nfunc IsSorted(data Interface) bool {\n\tn := data.Len()\n\tfor i := n - 1; i > 0; i-- {\n\t\tif data.Less(i, i-1) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// Convenience types for common cases: IntArray\ntype IntArray []int\n\nfunc (p IntArray) Len() int           { return len(p) }\nfunc (p IntArray) Less(i, j int) bool { return p[i] < p[j] }\nfunc (p IntArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }\n\ntype Float64Array []float64\n\nfunc (p Float64Array) Len() int           { return len(p) }\nfunc (p Float64Array) Less(i, j int) bool { return p[i] < p[j] }\nfunc (p Float64Array) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }\n\ntype StringArray []string\n\nfunc (p StringArray) Len() int           { return len(p) }\nfunc (p StringArray) Less(i, j int) bool { return p[i] < p[j] }\nfunc (p StringArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }\n\n// Convenience wrappers for common cases\nfunc SortInts(a []int)         { Sort(IntArray(a)) }\nfunc SortFloat64s(a []float64) { Sort(Float64Array(a)) }\nfunc SortStrings(a []string)   { Sort(StringArray(a)) }\n\nfunc IntsAreSorted(a []int) bool         { return IsSorted(IntArray(a)) }\nfunc Float64sAreSorted(a []float64) bool { return IsSorted(Float64Array(a)) }\nfunc StringsAreSorted(a []string) bool   { return IsSorted(StringArray(a)) }\n"
  },
  {
    "path": "eBook/examples/chapter_9/doc_example/sortmain.go",
    "content": "// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// This package gives an example of how to use a custom package with interfaces\npackage main\n\nimport (\n\t\"./sort\"\n\t\"fmt\"\n)\n\n// sorting of slice of integers\nfunc ints() {\n\tdata := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}\n\ta := sort.IntArray(data) //conversion to type IntArray\n\tsort.Sort(a)\n\tif !sort.IsSorted(a) {\n\t\tpanic(\"fail\")\n\t}\n\tfmt.Printf(\"The sorted array is: %v\\n\", a)\n}\n\n// sorting of slice of strings\nfunc strings() {\n\tdata := []string{\"monday\", \"friday\", \"tuesday\", \"wednesday\", \"sunday\", \"thursday\", \"\", \"saturday\"}\n\ta := sort.StringArray(data)\n\tsort.Sort(a)\n\tif !sort.IsSorted(a) {\n\t\tpanic(\"fail\")\n\t}\n\tfmt.Printf(\"The sorted array is: %v\\n\", a)\n}\n\n// a type which describes a day of the week\ntype day struct {\n\tnum       int\n\tshortName string\n\tlongName  string\n}\n\ntype dayArray struct {\n\tdata []*day\n}\n\nfunc (p *dayArray) Len() int           { return len(p.data) }\nfunc (p *dayArray) Less(i, j int) bool { return p.data[i].num < p.data[j].num }\nfunc (p *dayArray) Swap(i, j int)      { p.data[i], p.data[j] = p.data[j], p.data[i] }\n\n// sorting of custom type day\nfunc days() {\n\tSunday := day{0, \"SUN\", \"Sunday\"}\n\tMonday := day{1, \"MON\", \"Monday\"}\n\tTuesday := day{2, \"TUE\", \"Tuesday\"}\n\tWednesday := day{3, \"WED\", \"Wednesday\"}\n\tThursday := day{4, \"THU\", \"Thursday\"}\n\tFriday := day{5, \"FRI\", \"Friday\"}\n\tSaturday := day{6, \"SAT\", \"Saturday\"}\n\tdata := []*day{&Tuesday, &Thursday, &Wednesday, &Sunday, &Monday, &Friday, &Saturday}\n\ta := dayArray{data}\n\tsort.Sort(&a)\n\tif !sort.IsSorted(&a) {\n\t\tpanic(\"fail\")\n\t}\n\tfor _, d := range data {\n\t\tfmt.Printf(\"%s \", d.longName)\n\t}\n\tfmt.Printf(\"\\n\")\n}\n\nfunc main() {\n\tints()\n\tstrings()\n\tdays()\n}\n\n/* Output:\nThe sorted array is: [-5467984 -784 0 0 42 59 74 238 905 959 7586 7586 9845]\nThe sorted array is: [ friday monday saturday sunday thursday tuesday wednesday]\nSunday Monday Tuesday Wednesday Thursday Friday Saturday\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_9/pattern.go",
    "content": "// pattern.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"regexp\"\n\t\"strconv\"\n)\n\nfunc main() {\n\tsearchIn := \"John: 2578.34 William: 4567.23 Steve: 5632.18\" // string to search\n\tpat := \"[0-9]+.[0-9]+\"                                      // pattern search for in searchIn\n\n\tf := func(s string) string {\n\t\tv, _ := strconv.ParseFloat(s, 32)\n\t\treturn strconv.FormatFloat(v*2, 'f', 2, 32)\n\t}\n\n\tif ok, _ := regexp.Match(pat, []byte(searchIn)); ok {\n\t\tfmt.Println(\"Match found!\")\n\t}\n\n\tre, _ := regexp.Compile(pat)\n\tstr := re.ReplaceAllString(searchIn, \"##.#\") // replace pat with \"##.#\"\n\tfmt.Println(str)\n\t// using a function :\n\tstr2 := re.ReplaceAllStringFunc(searchIn, f)\n\tfmt.Println(str2)\n\n}\n\n/* Output:\nMatch found!\nJohn: ##.# William: ##.# Steve: ##.#\nJohn: 5156.68 William: 9134.46 Steve: 11264.36\n*/\n"
  },
  {
    "path": "eBook/examples/chapter_9/reboot.go",
    "content": "// reboot.go\n// compile errors (Windows):\n//undefined: syscall.SYS_REBOOT\n// reboot.go:13: not enough arguments in call to syscall.Syscall\n// Linux: compileert, uitvoeren met sudo ./6.out --> systeem herstart\npackage main\n\nimport (\n\t\"syscall\"\n)\n\nconst LINUX_REBOOT_MAGIC1 uintptr = 0xfee1dead\nconst LINUX_REBOOT_MAGIC2 uintptr = 672274793\nconst LINUX_REBOOT_CMD_RESTART uintptr = 0x1234567\n\nfunc main() {\n\tsyscall.Syscall(syscall.SYS_REBOOT,\n\t\tLINUX_REBOOT_MAGIC1,\n\t\tLINUX_REBOOT_MAGIC2,\n\t\tLINUX_REBOOT_CMD_RESTART)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_9/uc.go",
    "content": "package uc\n\nimport \"strings\"\n\nfunc UpperCase(str string) string {\n\treturn strings.ToUpper(str)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_9/uc_test.go",
    "content": "package uc\n\nimport \"testing\"\n\ntype ucTest struct {\n\tin, out string\n}\n\nvar ucTests = []ucTest{\n\t{\"abc\", \"ABC\"},\n\t{\"cvo-az\", \"CVO-AZ\"},\n\t{\"Antwerp\", \"ANTWERP\"},\n}\n\nfunc TestUC(t *testing.T) {\n\tfor _, ut := range ucTests {\n\t\tuc := UpperCase(ut.in)\n\t\tif uc != ut.out {\n\t\t\tt.Errorf(\"UpperCase(%s) = %s, must be %s\", ut.in, uc,\n\t\t\t\tut.out)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_9/ucmain.go",
    "content": "package main\n\nimport (\n\t\"./uc/uc\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tstr1 := \"USING package uc!\"\n\tfmt.Println(uc.UpperCase(str1))\n}\n"
  },
  {
    "path": "eBook/examples/chapter_9/uppercase/uc/uc.go",
    "content": "// uc.go\npackage uc\n\nimport \"strings\"\n\nfunc UpperCase(str string) string {\n\treturn strings.ToUpper(str)\n}\n"
  },
  {
    "path": "eBook/examples/chapter_9/uppercase/uc/uc_test.go",
    "content": "// uc_test.go\npackage uc\n\nimport \"testing\"\n\ntype ucTest struct {\n\tin, out string\n}\n\nvar ucTests = []ucTest{\n\t{\"abc\", \"ABC\"},\n\t{\"cvo-az\", \"CVO-AZ\"},\n\t{\"Antwerp\", \"ANTWERP\"},\n}\n\nfunc TestUC(t *testing.T) {\n\tfor _, ut := range ucTests {\n\t\tuc := UpperCase(ut.in)\n\t\tif uc != ut.out {\n\t\t\tt.Errorf(\"UpperCase(%s) = %s, must be %s.\", ut.in, uc, ut.out)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "eBook/examples/chapter_9/uppercase/uc_main/ucmain.go",
    "content": "// main.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"uppercase/uc\"\n)\n\nfunc main() {\n\tstr1 := \"USING package uc!\"\n\tfmt.Println(uc.UpperCase(str1))\n}\n"
  },
  {
    "path": "eBook/examples/chapter_9/use_urlshortener.go",
    "content": "// use_urlshortener.go\npackage main\n\nimport (\n\turlshortener \"code.google.com/p/google-api-go-client/urlshortener/v1\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"text/template\"\n)\n\nfunc main() {\n\thttp.HandleFunc(\"/\", root)\n\thttp.HandleFunc(\"/short\", short)\n\thttp.HandleFunc(\"/long\", long)\n\thttp.ListenAndServe(\"localhost:8080\", nil)\n}\n\n// the template used to show the forms and the results web page to the user\nvar rootHtmlTmpl = template.Must(template.New(\"rootHtml\").Parse(`\n<html><body>\n<h1>URL SHORTENER</h1>\n{{if .}}{{.}}<br /><br />{{end}}\n<form action=\"/short\" type=\"POST\">\nShorten this: <input type=\"text\" name=\"longUrl\" />\n<input type=\"submit\" value=\"Give me the short URL\" />\n</form>\n<br />\n<form action=\"/long\" type=\"POST\">\nExpand this: http://goo.gl/<input type=\"text\" name=\"shortUrl\" />\n<input type=\"submit\" value=\"Give me the long URL\" />\n</form>\n</body></html>\n`))\n\nfunc root(w http.ResponseWriter, r *http.Request) {\n\trootHtmlTmpl.Execute(w, nil)\n}\n\nfunc short(w http.ResponseWriter, r *http.Request) {\n\tlongUrl := r.FormValue(\"longUrl\")\n\turlshortenerSvc, _ := urlshortener.New(http.DefaultClient)\n\turl, _ := urlshortenerSvc.Url.Insert(&urlshortener.Url{LongUrl: longUrl}).Do()\n\trootHtmlTmpl.Execute(w, fmt.Sprintf(\"Shortened version of %s is : %s\", longUrl, url.Id))\n}\n\nfunc long(w http.ResponseWriter, r *http.Request) {\n\tshortUrl := \"http://goo.gl/\" + r.FormValue(\"shortUrl\")\n\turlshortenerSvc, _ := urlshortener.New(http.DefaultClient)\n\turl, err := urlshortenerSvc.Url.Get(shortUrl).Do()\n\tif err != nil {\n\t\tfmt.Println(\"error: %v\", err)\n\t\treturn\n\t}\n\trootHtmlTmpl.Execute(w, fmt.Sprintf(\"Longer version of %s is : %s\", shortUrl, url.LongUrl))\n}\n"
  },
  {
    "path": "eBook/examples/server.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net\"\n)\n\nfunc main() {\n\tfmt.Println(\"Starting the server ...\")\n\t// 创建 listener\n\tlistener, err := net.Listen(\"tcp\", \"localhost:50000\")\n\tif err != nil {\n\t\tfmt.Println(\"Error listening\", err.Error())\n\t\treturn //终止程序\n\t}\n\t// 监听并接受来自客户端的连接\n\tfor {\n\t\tconn, err := listener.Accept()\n\t\tif err != nil {\n\t\t\tfmt.Println(\"Error accepting\", err.Error())\n\t\t\treturn // 终止程序\n\t\t}\n\t\tgo doServerStuff(conn)\n\t}\n}\n\nfunc doServerStuff(conn net.Conn) {\n\tfor {\n\t\tbuf := make([]byte, 512)\n\t\t_, err := conn.Read(buf)\n\t\tif err != nil {\n\t\t\tfmt.Println(\"Error reading\", err.Error())\n\t\t\treturn //终止程序\n\t\t}\n\t\tfmt.Printf(\"Received data: %v\", string(buf))\n\t}\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_10/anonymous_struct.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype C struct {\n\tx float32\n\tint\n\tstring\n}\n\nfunc main() {\n\tc := C{3.14, 7, \"hello\"}\n\tfmt.Println(c.x, c.int, c.string) // output: 3.14 7 hello\n\tfmt.Println(c)                    // output: {3.14 7 hello}\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_10/celsius.go",
    "content": "// celsius.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n)\n\ntype Celsius float64\n\nfunc (c Celsius) String() string {\n\treturn \"The temperature is: \" + strconv.FormatFloat(float64(c), 'f', 1, 32) + \" °C\"\n}\n\nfunc main() {\n\tvar c Celsius = 18.36\n\tfmt.Println(c)\n}\n\n// The temperature is: 18.4 °C\n"
  },
  {
    "path": "eBook/exercises/chapter_10/days.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype Day int\n\nconst (\n\tMO Day = iota\n\tTU\n\tWE\n\tTH\n\tFR\n\tSA\n\tSU\n)\n\nvar dayName = []string{\"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\", \"Sunday\"}\n\nfunc (day Day) String() string {\n\treturn dayName[day]\n}\n\nfunc main() {\n\tvar th Day = 3\n\tfmt.Printf(\"The 3rd day is: %s\\n\", th)\n\t// If index > 6: panic: runtime error: index out of range\n\t// but use the enumerated type to work with valid values:\n\tvar day = SU\n\tfmt.Println(day) // prints Sunday\n\tfmt.Println(0, MO, 1, TU)\n}\n\n/* Output:\nThe 3rd day is: Thursday\nSunday\n0 Monday 1 Tuesday\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_10/employee_salary.go",
    "content": "// methods1.go\npackage main\n\nimport \"fmt\"\n\n/* basic data structure upon with we'll define methods */\ntype employee struct {\n\tsalary float32\n}\n\n/* a method which will add a specified percent to an\n   employees salary */\nfunc (this *employee) giveRaise(pct float32) {\n\tthis.salary += this.salary * pct\n}\n\nfunc main() {\n\t/* create an employee instance */\n\tvar e = new(employee)\n\te.salary = 100000\n\t/* call our method */\n\te.giveRaise(0.04)\n\tfmt.Printf(\"Employee now makes %f\", e.salary)\n}\n\n// Employee now makes 104000.000000\n"
  },
  {
    "path": "eBook/exercises/chapter_10/inherit_methods.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype Base struct {\n\tid string\n}\n\nfunc (b *Base) Id() string {\n\treturn b.id\n}\n\nfunc (b *Base) SetId(id string) {\n\tb.id = id\n}\n\ntype Person struct {\n\tBase\n\tFirstName string\n\tLastName  string\n}\n\ntype Employee struct {\n\tPerson\n\tsalary float32\n}\n\nfunc main() {\n\tidjb := Base{\"007\"}\n\tjb := Person{idjb, \"James\", \"Bond\"}\n\te := &Employee{jb, 100000.}\n\tfmt.Printf(\"ID of our hero: %v\\n\", e.Id())\n\t// Change the id:\n\te.SetId(\"007B\")\n\tfmt.Printf(\"The new ID of our hero: %v\\n\", e.Id())\n}\n\n/* Output:\nID of our hero: 007\nThe new ID of our hero: 007B\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_10/inheritance_car.go",
    "content": "// inheritance_car.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\ntype Engine interface {\n\tStart()\n\tStop()\n}\n\ntype Car struct {\n\twheelCount int\n\tEngine\n}\n\n// define a behavior for Car\nfunc (car Car) numberOfWheels() int {\n\treturn car.wheelCount\n}\n\ntype Mercedes struct {\n\tCar //anonymous field Car\n}\n\n// a behavior only available for the Mercedes\nfunc (m *Mercedes) sayHiToMerkel() {\n\tfmt.Println(\"Hi Angela!\")\n}\n\nfunc (c *Car) Start() {\n\tfmt.Println(\"Car is started\")\n}\n\nfunc (c *Car) Stop() {\n\tfmt.Println(\"Car is stopped\")\n}\n\nfunc (c *Car) GoToWorkIn() {\n\t// get in car\n\tc.Start()\n\t// drive to work\n\tc.Stop()\n\t// get out of car\n}\n\nfunc main() {\n\tm := Mercedes{Car{4, nil}}\n\tfmt.Println(\"A Mercedes has this many wheels: \", m.numberOfWheels())\n\tm.GoToWorkIn()\n\tm.sayHiToMerkel()\n}\n\n/* Output:\nA Mercedes has this many wheels:  4\nCar is started\nCar is stopped\nHi Angela!\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_10/iteration_list.go",
    "content": "/*\niteration_list.go:12: cannot define new methods on non-local type list.List\niteration_list.go:17: lst.Iter undefined (type *list.List has no field or method Iter)\n---- Build file exited with code 1\n*/\npackage main\n\nimport \"container/list\"\n\n// cannot define new methods on non-local type list.List\n// List iterator:\nfunc (p *list.List) Iter() {\n}\n\nfunc main() {\n\tlst := new(list.List)\n\tfor _ = range lst.Iter() {\n\t}\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_10/magic.go",
    "content": "// magic.go\npackage main\n\nimport \"fmt\"\n\ntype Base struct{}\n\nfunc (Base) Magic() { fmt.Print(\"base magic \") }\n\nfunc (self Base) MoreMagic() {\n\tself.Magic()\n\tself.Magic()\n}\n\ntype Voodoo struct {\n\tBase\n}\n\nfunc (Voodoo) Magic() { fmt.Println(\"voodoo magic\") }\n\nfunc main() {\n\tv := new(Voodoo)\n\tv.Magic()\n\tv.MoreMagic()\n}\n\n/* Output:\nvoodoo magic\nbase magic base magic\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_10/main_stack.go",
    "content": "// Q15.go\npackage main\n\nimport (\n\t\"./stack/stack\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tst1 := new(stack.Stack)\n\tfmt.Printf(\"%v\\n\", st1)\n\tst1.Push(3)\n\tfmt.Printf(\"%v\\n\", st1)\n\tst1.Push(7)\n\tfmt.Printf(\"%v\\n\", st1)\n\tst1.Push(10)\n\tfmt.Printf(\"%v\\n\", st1)\n\tst1.Push(99)\n\tfmt.Printf(\"%v\\n\", st1)\n\tp := st1.Pop()\n\tfmt.Printf(\"Popped %d\\n\", p)\n\tfmt.Printf(\"%v\\n\", st1)\n\tp = st1.Pop()\n\tfmt.Printf(\"Popped %d\\n\", p)\n\tfmt.Printf(\"%v\\n\", st1)\n\tp = st1.Pop()\n\tfmt.Printf(\"Popped %d\\n\", p)\n\tfmt.Printf(\"%v\\n\", st1)\n\tp = st1.Pop()\n\tfmt.Printf(\"Popped %d\\n\", p)\n\tfmt.Printf(\"%v\\n\", st1)\n}\n\n/* Output:\n[0:3]\n[0:3] [1:7]\n[0:3] [1:7] [2:10]\n[0:3] [1:7] [2:10] [3:99]\nPopped 99\n[0:3] [1:7] [2:10]\nPopped 10\n[0:3] [1:7]\nPopped 7\n[0:3]\nPopped 3\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_10/personex1.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\ntype Person struct {\n\tfirstName string\n\tlastName  string\n}\n\nfunc upPerson(p Person) {\n\tp.firstName = strings.ToUpper(p.firstName)\n\tp.lastName = strings.ToUpper(p.lastName)\n}\n\nfunc main() {\n\t// 1- struct as a value type:\n\tvar pers1 Person\n\tpers1.firstName = \"Chris\"\n\tpers1.lastName = \"Woodward\"\n\tupPerson(pers1)\n\tfmt.Printf(\"The name of the person is %s %s\\n\", pers1.firstName, pers1.lastName)\n\t// 2 - struct as a pointer:\n\tpers2 := new(Person)\n\tpers2.firstName = \"Chris\"\n\tpers2.lastName = \"Woodward\"\n\tupPerson(*pers2)\n\tfmt.Printf(\"The name of the person is %s %s\\n\", pers2.firstName, pers2.lastName)\n\t// 3 - struct as a literal:\n\tpers3 := &Person{\"Chris\", \"Woodward\"}\n\tupPerson(*pers3)\n\tfmt.Printf(\"The name of the person is %s %s\\n\", pers3.firstName, pers3.lastName)\n}\n\n/* Output:\nThe name of the person is Chris Woodward\nThe name of the person is Chris Woodward\nThe name of the person is Chris Woodward\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_10/point.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n)\n\ntype Point struct {\n\tX, Y float64\n}\n\ntype Point3 struct {\n\tX, Y, Z float64\n}\n\ntype Polar struct {\n\tR, T float64\n}\n\nfunc Abs(p *Point) float64 {\n\treturn math.Sqrt(float64(p.X*p.X + p.Y*p.Y))\n}\n\nfunc Scale(p *Point, s float64) (q Point) {\n\tq.X = p.X * s\n\tq.Y = p.Y * s\n\treturn\n}\n\nfunc main() {\n\tp1 := new(Point)\n\tp1.X = 3\n\tp1.Y = 4\n\tfmt.Printf(\"The length of the vector p1 is: %f\\n\", Abs(p1))\n\n\tp2 := &Point{4, 5}\n\tfmt.Printf(\"The length of the vector p2 is: %f\\n\", Abs(p2))\n\n\tq := Scale(p1, 5)\n\tfmt.Printf(\"The length of the vector q is: %f\\n\", Abs(&q))\n\tfmt.Printf(\"Point p1 scaled by 5 has the following coordinates: X %f - Y %f\", q.X, q.Y)\n}\n\n/* Output:\nThe length of the vector p1 is: 5.000000\nThe length of the vector p2 is: 6.403124\nThe length of the vector q is: 25.000000\nPoint p1 scaled by 5 has the following coordinates: X 15.000000 - Y 20.000000\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_10/point_methods.go",
    "content": "// float64 is necessary as input to math.Sqrt()\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n)\n\ntype Point struct {\n\tX, Y float64\n}\n\nfunc (p *Point) Scale(s float64) {\n\tp.X *= s\n\tp.Y *= s\n}\n\nfunc (p *Point) Abs() float64 {\n\treturn math.Sqrt(float64(p.X*p.X + p.Y*p.Y))\n}\n\ntype Point3 struct {\n\tX, Y, Z float64\n}\n\nfunc (p *Point3) Abs() float64 {\n\treturn math.Sqrt(float64(p.X*p.X + p.Y*p.Y + p.Z*p.Z))\n}\n\ntype Polar struct {\n\tR, T float64\n}\n\nfunc (p Polar) Abs() float64 { return p.R }\n\nfunc main() {\n\tp1 := new(Point)\n\tp1.X = 3\n\tp1.Y = 4\n\tfmt.Printf(\"The length of the vector p1 is: %f\\n\", p1.Abs())\n\n\tp2 := &Point{4, 5}\n\tfmt.Printf(\"The length of the vector p2 is: %f\\n\", p2.Abs())\n\n\tp1.Scale(5)\n\tfmt.Printf(\"The length of the vector p1 after scaling is: %f\\n\", p1.Abs())\n\tfmt.Printf(\"Point p1 after scaling has the following coordinates: X %f - Y %f\", p1.X, p1.Y)\n}\n\n/* Output:\nThe length of the vector p1 is: 5.000000\nThe length of the vector p2 is: 6.403124\nThe length of the vector p1 after scaling is: 25.000000\nPoint p1 after scaling has the following coordinates: X 15.000000 - Y 20.000000\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_10/rectangle.go",
    "content": "// rectangle.go\npackage main\n\nimport \"fmt\"\n\ntype Rectangle struct {\n\tlength, width int\n}\n\nfunc (r *Rectangle) Area() int {\n\treturn r.length * r.width\n}\n\nfunc (r *Rectangle) Perimeter() int {\n\treturn 2 * (r.length + r.width)\n}\n\nfunc main() {\n\tr1 := Rectangle{4, 3}\n\tfmt.Println(\"Rectangle is: \", r1)\n\tfmt.Println(\"Rectangle area is: \", r1.Area())\n\tfmt.Println(\"Rectangle perimeter is: \", r1.Perimeter())\n}\n\n/* Output:\nRectangle is:  {4 3}\nRectangle area is:  12\nRectangle perimeter is:  14\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_10/stack/stack_struct.go",
    "content": "// stack_struct.go\npackage stack\n\nimport \"strconv\"\n\nconst LIMIT = 10\n\ntype Stack struct {\n\tix   int // first free position, so data[ix] == 0\n\tdata [LIMIT]int\n}\n\nfunc (st *Stack) Push(n int) {\n\tif st.ix+1 > LIMIT {\n\t\treturn // stack is full!\n\t}\n\tst.data[st.ix] = n\n\tst.ix++\n}\n\nfunc (st *Stack) Pop() int {\n\tst.ix--\n\treturn st.data[st.ix]\n}\n\nfunc (st Stack) String() string {\n\tstr := \"\"\n\tfor ix := 0; ix < st.ix; ix++ {\n\t\tstr += \"[\" + strconv.Itoa(ix) + \":\" + strconv.Itoa(st.data[ix]) + \"] \"\n\t}\n\treturn str\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_10/stack_arr.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n)\n\nconst LIMIT = 4\n\ntype Stack [LIMIT]int\n\nfunc main() {\n\tst1 := new(Stack)\n\tfmt.Printf(\"%v\\n\", st1)\n\tst1.Push(3)\n\tfmt.Printf(\"%v\\n\", st1)\n\tst1.Push(7)\n\tfmt.Printf(\"%v\\n\", st1)\n\tst1.Push(10)\n\tfmt.Printf(\"%v\\n\", st1)\n\tst1.Push(99)\n\tfmt.Printf(\"%v\\n\", st1)\n\tp := st1.Pop()\n\tfmt.Printf(\"Popped %d\\n\", p)\n\tfmt.Printf(\"%v\\n\", st1)\n\tp = st1.Pop()\n\tfmt.Printf(\"Popped %d\\n\", p)\n\tfmt.Printf(\"%v\\n\", st1)\n\tp = st1.Pop()\n\tfmt.Printf(\"Popped %d\\n\", p)\n\tfmt.Printf(\"%v\\n\", st1)\n\tp = st1.Pop()\n\tfmt.Printf(\"Popped %d\\n\", p)\n\tfmt.Printf(\"%v\\n\", st1)\n}\n\n// put value on first position which contains 0, starting from bottom\nfunc (st *Stack) Push(n int) {\n\tfor ix, v := range st {\n\t\tif v == 0 {\n\t\t\tst[ix] = n\n\t\t\tbreak\n\t\t}\n\t}\n}\n\n// take value from first position which contains !=0, starting from top\nfunc (st *Stack) Pop() int {\n\tv := 0\n\tfor ix := len(st) - 1; ix >= 0; ix-- {\n\t\tif v = st[ix]; v != 0 {\n\t\t\tst[ix] = 0\n\t\t\treturn v\n\t\t}\n\t}\n\treturn 0\n}\n\nfunc (st Stack) String() string {\n\tstr := \"\"\n\tfor ix, v := range st {\n\t\tstr += \"[\" + strconv.Itoa(ix) + \":\" + strconv.Itoa(v) + \"] \"\n\t}\n\treturn str\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_10/stack_struct.go",
    "content": "// stack_struct.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n)\n\nconst LIMIT = 4\n\ntype Stack struct {\n\tix   int // first free position, so data[ix] == 0\n\tdata [LIMIT]int\n}\n\nfunc main() {\n\tst1 := new(Stack)\n\tfmt.Printf(\"%v\\n\", st1)\n\tst1.Push(3)\n\tfmt.Printf(\"%v\\n\", st1)\n\tst1.Push(7)\n\tfmt.Printf(\"%v\\n\", st1)\n\tst1.Push(10)\n\tfmt.Printf(\"%v\\n\", st1)\n\tst1.Push(99)\n\tfmt.Printf(\"%v\\n\", st1)\n\tp := st1.Pop()\n\tfmt.Printf(\"Popped %d\\n\", p)\n\tfmt.Printf(\"%v\\n\", st1)\n\tp = st1.Pop()\n\tfmt.Printf(\"Popped %d\\n\", p)\n\tfmt.Printf(\"%v\\n\", st1)\n\tp = st1.Pop()\n\tfmt.Printf(\"Popped %d\\n\", p)\n\tfmt.Printf(\"%v\\n\", st1)\n\tp = st1.Pop()\n\tfmt.Printf(\"Popped %d\\n\", p)\n\tfmt.Printf(\"%v\\n\", st1)\n}\n\nfunc (st *Stack) Push(n int) {\n\tif st.ix+1 > LIMIT {\n\t\treturn // stack is full!\n\t}\n\tst.data[st.ix] = n\n\tst.ix++\n}\n\nfunc (st *Stack) Pop() int {\n\tst.ix--\n\treturn st.data[st.ix]\n}\n\nfunc (st Stack) String() string {\n\tstr := \"\"\n\tfor ix := 0; ix < st.ix; ix++ {\n\t\tstr += \"[\" + strconv.Itoa(ix) + \":\" + strconv.Itoa(st.data[ix]) + \"] \"\n\t}\n\treturn str\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_10/timezones.go",
    "content": "// Output:\n// Eastern Standard time\n// Universal Greenwich time\n// Central Standard time\npackage main\n\nimport \"fmt\"\n\ntype TZ int\n\nconst (\n\tHOUR TZ = 60 * 60\n\tUTC  TZ = 0 * HOUR\n\tEST  TZ = -5 * HOUR\n\tCST  TZ = -6 * HOUR\n)\n\nvar timeZones = map[TZ]string{\n\tUTC: \"Universal Greenwich time\",\n\tEST: \"Eastern Standard time\",\n\tCST: \"Central Standard time\"}\n\nfunc (tz TZ) String() string { // Method on TZ (not ptr)\n\tif zone, ok := timeZones[tz]; ok {\n\t\treturn zone\n\t}\n\treturn \"\"\n}\n\nfunc main() {\n\tfmt.Println(EST) // Print* knows about method String() of type TZ\n\tfmt.Println(0 * HOUR)\n\tfmt.Println(-6 * HOUR)\n}\n\n/* Output:\nEastern Standard time\nUniversal Greenwich time\nCentral Standard time\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_10/type_string.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype T struct {\n\ta int\n\tb float32\n\tc string\n}\n\nfunc main() {\n\tt := &T{7, -2.35, \"abc\\tdef\"}\n\tfmt.Printf(\"%v\\n\", t)\n}\n\nfunc (t *T) String() string {\n\treturn fmt.Sprintf(\"%d / %f / %q\", t.a, t.b, t.c)\n}\n\n// Output: 7 / -2.350000 / \"abc\\tdef\"\n"
  },
  {
    "path": "eBook/exercises/chapter_10/vcard.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\ntype Address struct {\n\tStreet           string\n\tHouseNumber      uint32\n\tHouseNumberAddOn string\n\tPOBox            string\n\tZipCode          string\n\tCity             string\n\tCountry          string\n}\n\ntype VCard struct {\n\tFirstName string\n\tLastName  string\n\tNickName  string\n\tBirtDate  time.Time\n\tPhoto     string\n\tAddresses map[string]*Address\n}\n\nfunc main() {\n\taddr1 := &Address{\"Elfenstraat\", 12, \"\", \"\", \"2600\", \"Mechelen\", \"België\"}\n\taddr2 := &Address{\"Heideland\", 28, \"\", \"\", \"2640\", \"Mortsel\", \"België\"}\n\taddrs := make(map[string]*Address)\n\taddrs[\"youth\"] = addr1\n\taddrs[\"now\"] = addr2\n\tbirthdt := time.Date(1956, 1, 17, 15, 4, 5, 0, time.Local)\n\tphoto := \"MyDocuments/MyPhotos/photo1.jpg\"\n\tvcard := &VCard{\"Ivo\", \"Balbaert\", \"\", birthdt, photo, addrs}\n\tfmt.Printf(\"Here is the full VCard: %v\\n\", vcard)\n\tfmt.Printf(\"My Addresses are:\\n %v\\n %v\", addr1, addr2)\n}\n\n/* Output:\nHere is the full VCard: &{Ivo Balbaert  Sun Jan 17 15:04:05 +0000 1956 MyDocuments/MyPhotos/photo1.jpg map[now:0x126d57c0 youth:0x126d5500]}\nMy Addresses are:\n &{Elfenstraat 12   2600 Mechelen België}\n &{Heideland 28   2640 Mortsel België}\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_11/float_sort.go",
    "content": "package float64\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n\t\"time\"\n)\n\ntype Sorter interface {\n\tLen() int\n\tLess(i, j int) bool\n\tSwap(i, j int)\n}\n\nfunc Sort(data Sorter) {\n\tfor pass := 1; pass < data.Len(); pass++ {\n\t\tfor i := 0; i < data.Len()-pass; i++ {\n\t\t\tif data.Less(i+1, i) {\n\t\t\t\tdata.Swap(i, i+1)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc IsSorted(data Sorter) bool {\n\tn := data.Len()\n\tfor i := n - 1; i > 0; i-- {\n\t\tif data.Less(i, i-1) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\ntype Float64Array []float64\n\nfunc (p Float64Array) Len() int           { return len(p) }\nfunc (p Float64Array) Less(i, j int) bool { return p[i] < p[j] }\nfunc (p Float64Array) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }\n\nfunc NewFloat64Array() Float64Array {\n\treturn make([]float64, 25)\n}\n\nfunc (p Float64Array) Fill(n int) {\n\trand.Seed(int64(time.Now().Nanosecond()))\n\tfor i := 0; i < n; i++ {\n\t\tp[i] = 100 * (rand.Float64())\n\t}\n}\n\nfunc (p Float64Array) List() string {\n\ts := \"{ \"\n\tfor i := 0; i < p.Len(); i++ {\n\t\tif p[i] == 0 {\n\t\t\tcontinue\n\t\t}\n\t\ts += fmt.Sprintf(\"%3.1f \", p[i])\n\t}\n\ts += \" }\"\n\treturn s\n}\n\nfunc (p Float64Array) String() string {\n\treturn p.List()\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_11/float_sortmain.go",
    "content": "package main\n\nimport (\n\t\"./float64\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tf1 := float64.NewFloat64Array()\n\tf1.Fill(10)\n\tfmt.Printf(\"Before sorting %s\\n\", f1)\n\tfloat64.Sort(f1)\n\tfmt.Printf(\"After sorting %s\\n\", f1)\n\tif float64.IsSorted(f1) {\n\t\tfmt.Println(\"The float64 array is sorted!\")\n\t} else {\n\t\tfmt.Println(\"The float64 array is NOT sorted!\")\n\t}\n}\n\n/* Output:\nBefore sorting { 55.0 82.3 36.4 66.6 25.3 82.7 47.4 21.5 4.6 81.6  }\nAfter sorting { 4.6 21.5 25.3 36.4 47.4 55.0 66.6 81.6 82.3 82.7  }\nThe float64 array is sorted!\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_11/interface_nil.go",
    "content": "// interface_nil.go\npackage main\n\nimport \"fmt\"\n\ntype Any interface{}\ntype Anything struct{}\n\nfunc main() {\n\tany := getAny()\n\tif any == nil {\n\t\tfmt.Println(\"any is nil\")\n\t} else {\n\t\tfmt.Println(\"any is not nil\")\n\t}\n\t/*\n\t\t// to get the inner value:\n\t\tanything := any.(*Anything)\n\t\tif anything == nil {\n\t\t\tfmt.Println(\"anything is nil\")\n\t\t} else {\n\t\t\tfmt.Println(\"anything is not nil\")\n\t\t}\n\t*/\n}\n\nfunc getAny() Any {\n\treturn getAnything()\n}\n\nfunc getAnything() *Anything {\n\treturn nil\n}\n\n/* Output:\nany is not nil\nWHY?\nyou would perhaps expect: any is nil,because getAnything() returns that\nBUT:\nthe interface value any is storing a value, so it is not nil.\nIt just so happens that the particular value it is storing is a nil pointer.\nThe any variable has a type, so it's not a nil interface,\nrather an interface variable with type Any and concrete value (*Anything)(nil).\nTo get the inner value of any, use: anything := any.(*Anything)\nnow anything contains nil !\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_11/interface_poly3.go",
    "content": "// interface_poly3.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n)\n\ntype Shaper interface {\n\tArea() float32\n}\n\ntype Shape struct{}\n\nfunc (sh Shape) Area() float32 {\n\treturn -1 // the shape is indetermined, so we return something impossible\n}\n\ntype Square struct {\n\tside float32\n\tShape\n}\n\nfunc (sq *Square) Area() float32 {\n\treturn sq.side * sq.side\n}\n\ntype Rectangle struct {\n\tlength, width float32\n\tShape\n}\n\nfunc (r *Rectangle) Area() float32 {\n\treturn r.length * r.width\n}\n\ntype Circle struct {\n\tradius float32\n\tShape\n}\n\nfunc (c *Circle) Area() float32 {\n\treturn math.Pi * c.radius * c.radius\n}\n\nfunc main() {\n\ts := Shape{}\n\tr := &Rectangle{5, 3, s} // Area() of Rectangle needs a value\n\tq := &Square{5, s}       // Area() of Square needs a pointer\n\tc := &Circle{2.5, s}\n\tshapes := []Shaper{r, q, c, s}\n\tfmt.Println(\"Looping through shapes for area ...\")\n\tfor n := range shapes {\n\t\tfmt.Println(\"Shape details: \", shapes[n])\n\t\tfmt.Println(\"Area of this shape is: \", shapes[n].Area())\n\t}\n}\n\n/* Output:\nLooping through shapes for area ...\nShape details:  {5 3}\nArea of this shape is:  15\nShape details:  &{5}\nArea of this shape is:  25\nShape details:  &{2.5}\nArea of this shape is:  19.634954\nShape details:  {}\nArea of this shape is:  -1\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_11/interfaces_ext.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype Square struct {\n\tside float32\n}\n\ntype Triangle struct {\n\tbase   float32\n\theight float32\n}\n\ntype AreaInterface interface {\n\tArea() float32\n}\n\ntype PeriInterface interface {\n\tPerimeter() float32\n}\n\nfunc main() {\n\tvar areaIntf AreaInterface\n\tvar periIntf PeriInterface\n\n\tsq1 := new(Square)\n\tsq1.side = 5\n\ttr1 := new(Triangle)\n\ttr1.base = 3\n\ttr1.height = 5\n\n\tareaIntf = sq1\n\tfmt.Printf(\"The square has area: %f\\n\", areaIntf.Area())\n\n\tperiIntf = sq1\n\tfmt.Printf(\"The square has perimeter: %f\\n\", periIntf.Perimeter())\n\n\tareaIntf = tr1\n\tfmt.Printf(\"The triangle has area: %f\\n\", areaIntf.Area())\n}\n\nfunc (sq *Square) Area() float32 {\n\treturn sq.side * sq.side\n}\n\nfunc (sq *Square) Perimeter() float32 {\n\treturn 4 * sq.side\n}\n\nfunc (tr *Triangle) Area() float32 {\n\treturn 0.5 * tr.base * tr.height\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_11/interfaces_poly2.go",
    "content": "// interfaces_poly2.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n)\n\ntype Shaper interface {\n\tArea() float32\n}\n\ntype Square struct {\n\tside float32\n}\n\nfunc (sq *Square) Area() float32 {\n\treturn sq.side * sq.side\n}\n\ntype Rectangle struct {\n\tlength, width float32\n}\n\nfunc (r Rectangle) Area() float32 {\n\treturn r.length * r.width\n}\n\ntype Circle struct {\n\tradius float32\n}\n\nfunc (c *Circle) Area() float32 {\n\treturn math.Pi * c.radius * c.radius\n}\n\nfunc main() {\n\tr := Rectangle{5, 3} // Area() of Rectangle needs a value\n\tq := &Square{5}      // Area() of Square needs a pointer\n\tc := &Circle{2.5}\n\tfmt.Println(\"Looping through shapes for area ...\")\n\t// shapes := []Shaper{Shaper(r), Shaper(q), Shaper(c)}\n\tshapes := []Shaper{r, q, c}\n\tfor n := range shapes {\n\t\tfmt.Println(\"Shape details: \", shapes[n])\n\t\tfmt.Println(\"Area of this shape is: \", shapes[n].Area())\n\t}\n}\n\n/* Output:\nLooping through shapes for area ...\nShape details:  {5 3}\nArea of this shape is:  15\nShape details:  &{5}\nArea of this shape is:  25\nShape details:  &{2.5}\nArea of this shape is:  19.634954\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_11/main_stack.go",
    "content": "// main_stack.go\npackage main\n\nimport (\n\t\"./stack/stack\"\n\t\"fmt\"\n)\n\nvar st1 stack.Stack\n\nfunc main() {\n\tst1.Push(\"Brown\")\n\tst1.Push(3.14)\n\tst1.Push(100)\n\tst1.Push([]string{\"Java\", \"C++\", \"Python\", \"C#\", \"Ruby\"})\n\tfor {\n\t\titem, err := st1.Pop()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tfmt.Println(item)\n\t}\n}\n\n/* Output:\n[Java C++ Python C# Ruby]\n100\n3.14\nBrown\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_11/main_stack_v2.go",
    "content": "// main_stack_v2.go\npackage main\n\nimport (\n\t\"./stack/collection\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tvar s collection.Stack\n\ts.Push(\"world\")\n\ts.Push(\"hello, \")\n\tfor s.Size() > 0 {\n\t\tfmt.Print(s.Pop())\n\t}\n\tfmt.Println()\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_11/map_function_interface.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype obj interface{}\n\nfunc main() {\n\t// define a generic lambda function mf:\n\tmf := func(i obj) obj {\n\t\tswitch i.(type) {\n\t\tcase int:\n\t\t\treturn i.(int) * 2\n\t\tcase string:\n\t\t\treturn i.(string) + i.(string)\n\t\t}\n\t\treturn i\n\t}\n\n\tisl := []obj{0, 1, 2, 3, 4, 5}\n\tres1 := mapFunc(mf, isl)\n\tfor _, v := range res1 {\n\t\tfmt.Println(v)\n\t}\n\tprintln()\n\n\tssl := []obj{\"0\", \"1\", \"2\", \"3\", \"4\", \"5\"}\n\tres2 := mapFunc(mf, ssl)\n\tfor _, v := range res2 {\n\t\tfmt.Println(v)\n\t}\n}\n\nfunc mapFunc(mf func(obj) obj, list []obj) []obj {\n\tresult := make([]obj, len(list))\n\n\tfor ix, v := range list {\n\t\tresult[ix] = mf(v)\n\t}\n\n\t// Equivalent:\n\t/*\n\t\tfor ix := 0; ix<len(list); ix++ {\n\t\t\tresult[ix] = mf(list[ix])\n\t\t}\n\t*/\n\treturn result\n}\n\n/* Output:\n0\n2\n4\n6\n8\n10\n\n00\n11\n22\n33\n44\n55\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_11/map_function_interface_var.go",
    "content": "package main\n\nimport \"fmt\"\n\ntype obj interface{}\n\nfunc main() {\n\t// define a generic lambda function mf:\n\tmf := func(i obj) obj {\n\t\tswitch i.(type) {\n\t\tcase int:\n\t\t\treturn i.(int) * 2\n\t\tcase string:\n\t\t\treturn i.(string) + i.(string)\n\t\t}\n\t\treturn i\n\t}\n\n\tres1 := mapFunc(mf, 0, 1, 2, 3, 4, 5)\n\tfor _, v := range res1 {\n\t\tfmt.Println(v)\n\t}\n\tprintln()\n\tres2 := mapFunc(mf, \"0\", \"1\", \"2\", \"3\", \"4\", \"5\")\n\tfor _, v := range res2 {\n\t\tfmt.Println(v)\n\t}\n}\n\nfunc mapFunc(mf func(obj) obj, list ...obj) []obj {\n\tresult := make([]obj, len(list))\n\n\tfor ix, v := range list {\n\t\tresult[ix] = mf(v)\n\t}\n\n\t// Equivalent:\n\t/*\n\t\tfor ix := 0; ix<len(list); ix++ {\n\t\t\tresult[ix] = mf(list[ix])\n\t\t}\n\t*/\n\treturn result\n}\n\n/* Output:\n0\n2\n4\n6\n8\n10\n\n00\n11\n22\n33\n44\n55\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_11/min_interface.go",
    "content": "// min_interface.go\npackage min\n\ntype Miner interface {\n\tLen() int\n\tElemIx(ix int) interface{}\n\tLess(i, j int) bool\n\tSwap(i, j int)\n}\n\nfunc Min(data Miner) interface{} {\n  min := data.ElemIx(0)\n\tfor i := 1; i < data.Len(); i++ {\n\t\tif data.Less(i, i-1) {\n\t\t\tmin = data.ElemIx(i)\n\t\t} else {\n\t\t\tdata.Swap(i, i-1)\n\t\t}\n\t}\n\treturn min\n}\n\ntype IntArray []int\n\nfunc (p IntArray) Len() int                  { return len(p) }\nfunc (p IntArray) ElemIx(ix int) interface{} { return p[ix] }\nfunc (p IntArray) Less(i, j int) bool        { return p[i] < p[j] }\nfunc (p IntArray) Swap(i, j int)             { p[i], p[j] = p[j], p[i] }\n\ntype StringArray []string\n\nfunc (p StringArray) Len() int                  { return len(p) }\nfunc (p StringArray) ElemIx(ix int) interface{} { return p[ix] }\nfunc (p StringArray) Less(i, j int) bool        { return p[i] < p[j] }\nfunc (p StringArray) Swap(i, j int)             { p[i], p[j] = p[j], p[i] }\n"
  },
  {
    "path": "eBook/exercises/chapter_11/minmain.go",
    "content": "// minmain.go\npackage main\n\nimport (\n\t\"./min\"\n\t\"fmt\"\n)\n\nfunc ints() {\n\tdata := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}\n\ta := min.IntArray(data) //conversion to type IntArray\n\tm := min.Min(a)\n\tfmt.Printf(\"The minimum of the array is: %v\\n\", m)\n}\n\nfunc strings() {\n\tdata := []string{\"ddd\", \"eee\", \"bbb\", \"ccc\", \"aaa\"}\n\ta := min.StringArray(data)\n\tm := min.Min(a)\n\tfmt.Printf(\"The minimum of the array is: %v\\n\", m)\n}\n\nfunc main() {\n\tints()\n\tstrings()\n}\n\n/* Output:\nThe minimum of the array is: -5467984\nThe minimum of the array is: aaa\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_11/point_interfaces.go",
    "content": "// float64 is necessary as input to math.Sqrt()\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n)\n\ntype Magnitude interface {\n\tAbs() float64\n}\n\nvar m Magnitude\n\ntype Point struct {\n\tX, Y float64\n}\n\nfunc (p *Point) Scale(s float64) {\n\tp.X *= s\n\tp.Y *= s\n}\n\nfunc (p *Point) Abs() float64 {\n\treturn math.Sqrt(float64(p.X*p.X + p.Y*p.Y))\n}\n\ntype Point3 struct {\n\tX, Y, Z float64\n}\n\nfunc (p *Point3) Abs() float64 {\n\treturn math.Sqrt(float64(p.X*p.X + p.Y*p.Y + p.Z*p.Z))\n}\n\ntype Polar struct {\n\tR, Theta float64\n}\n\nfunc (p Polar) Abs() float64 { return p.R }\n\nfunc main() {\n\tp1 := new(Point)\n\tp1.X = 3\n\tp1.Y = 4\n\tm = p1 // p1 is type *Point, has method Abs()\n\tfmt.Printf(\"The length of the vector p1 is: %f\\n\", m.Abs())\n\n\tp2 := &Point{4, 5}\n\tm = p2\n\tfmt.Printf(\"The length of the vector p2 is: %f\\n\", m.Abs())\n\n\tp1.Scale(5)\n\tm = p1\n\tfmt.Printf(\"The length of the vector p1 after scaling is: %f\\n\", m.Abs())\n\tfmt.Printf(\"Point p1 after scaling has the following coordinates: X %f - Y %f\\n\", p1.X, p1.Y)\n\n\tmag := m.Abs()\n\tm = &Point3{3, 4, 5}\n\tmag += m.Abs()\n\tm = Polar{2.0, math.Pi / 2}\n\tmag += m.Abs()\n\tfmt.Printf(\"The float64 mag is now: %f\", mag)\n}\n\n/* Output:\nThe length of the vector p1 is: 5.000000\nThe length of the vector p2 is: 6.403124\nThe length of the vector p1 after scaling is: 25.000000\nPoint p1 after scaling has the following coordinates: X 15.000000 - Y 20.000000\nThe float64 mag is now: 34.071068\n\n-- instead of:\nfunc (p *Point) Abs() float64 {\n\treturn math.Sqrt(float64(p.X*p.X + p.Y*p.Y))\n}\nm = p1\nwe can write:\nfunc (p Point) Abs() float64 {\n\treturn math.Sqrt(float64(p.X*p.X + p.Y*p.Y))\n}\nm = p1\nand instead of:\nfunc (p Polar) Abs() float64 { return p.R }\nm = Polar{2.0, math.Pi / 2}\nwe can write:\nfunc (p *Polar) Abs() float64 { return p.R }\nm = &Polar{2.0, math.Pi / 2}\nwith the same output\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_11/print.go",
    "content": "// print.go\npackage main\n\nimport (\n\t\"os\"\n\t\"strconv\"\n)\n\ntype Stringer interface {\n\tString() string\n}\n\ntype Celsius float64\n\nfunc (c Celsius) String() string {\n\treturn strconv.FormatFloat(float64(c), 'f', 1, 64) + \" °C\"\n}\n\ntype Day int\n\nvar dayName = []string{\"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\", \"Sunday\"}\n\nfunc (day Day) String() string {\n\treturn dayName[day]\n}\n\nfunc print(args ...interface{}) {\n\tfor i, arg := range args {\n\t\tif i > 0 {\n\t\t\tos.Stdout.WriteString(\" \")\n\t\t}\n\t\tswitch a := arg.(type) { // type switch\n\t\tcase Stringer:\n\t\t\tos.Stdout.WriteString(a.String())\n\t\tcase int:\n\t\t\tos.Stdout.WriteString(strconv.Itoa(a))\n\t\tcase string:\n\t\t\tos.Stdout.WriteString(a)\n\t\t// more types\n\t\tdefault:\n\t\t\tos.Stdout.WriteString(\"???\")\n\t\t}\n\t}\n}\n\nfunc main() {\n\tprint(Day(1), \"was\", Celsius(18.36)) // Tuesday was 18.4 °C\n}\n\n// Tuesday was 18.4 °C\n"
  },
  {
    "path": "eBook/exercises/chapter_11/simple_interface.go",
    "content": "// simple_interface.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\ntype Simpler interface {\n\tGet() int\n\tPut(int)\n}\n\ntype Simple struct {\n\ti int\n}\n\nfunc (p *Simple) Get() int {\n\treturn p.i\n}\n\nfunc (p *Simple) Put(u int) {\n\tp.i = u\n}\n\nfunc fI(it Simpler) int {\n\tit.Put(5)\n\treturn it.Get()\n}\n\nfunc main() {\n\tvar s Simple\n\tfmt.Println(fI(&s)) // &s is required because Get() is defined with a receiver type pointer\n}\n\n// Output: 5\n"
  },
  {
    "path": "eBook/exercises/chapter_11/simple_interface2.go",
    "content": "// simple_interface2.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\ntype Simpler interface {\n\tGet() int\n\tSet(int)\n}\n\ntype Simple struct {\n\ti int\n}\n\nfunc (p *Simple) Get() int {\n\treturn p.i\n}\n\nfunc (p *Simple) Set(u int) {\n\tp.i = u\n}\n\ntype RSimple struct {\n\ti int\n\tj int\n}\n\nfunc (p *RSimple) Get() int {\n\treturn p.j\n}\n\nfunc (p *RSimple) Set(u int) {\n\tp.j = u\n}\n\nfunc fI(it Simpler) int {\n\tswitch it.(type) {\n\tcase *Simple:\n\t\tit.Set(5)\n\t\treturn it.Get()\n\tcase *RSimple:\n\t\tit.Set(50)\n\t\treturn it.Get()\n\tdefault:\n\t\treturn 99\n\t}\n\treturn 0\n}\n\nfunc main() {\n\tvar s Simple\n\tfmt.Println(fI(&s)) // &s is required because Get() is defined with a receiver type pointer\n\tvar r RSimple\n\tfmt.Println(fI(&r))\n}\n\n/* Output:\n5\n50\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_11/simple_interface3.go",
    "content": "// simple_interface2.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\ntype Simpler interface {\n\tGet() int\n\tSet(int)\n}\n\ntype Simple struct {\n\ti int\n}\n\nfunc (p *Simple) Get() int {\n\treturn p.i\n}\n\nfunc (p *Simple) Set(u int) {\n\tp.i = u\n}\n\ntype RSimple struct {\n\ti int\n\tj int\n}\n\nfunc (p *RSimple) Get() int {\n\treturn p.j\n}\n\nfunc (p *RSimple) Set(u int) {\n\tp.j = u\n}\n\nfunc fI(it Simpler) int {\n\tswitch it.(type) {\n\tcase *Simple:\n\t\tit.Set(5)\n\t\treturn it.Get()\n\tcase *RSimple:\n\t\tit.Set(50)\n\t\treturn it.Get()\n\tdefault:\n\t\treturn 99\n\t}\n\treturn 0\n}\n\nfunc gI(any interface{}) int {\n\t// return any.(Simpler).Get()   // unsafe, runtime panic possible\n\tif v, ok := any.(Simpler); ok {\n\t\treturn v.Get()\n\t}\n\treturn 0 // default value\n}\n\n/* Output:\n6\n60\n*/\n\nfunc main() {\n\tvar s Simple = Simple{6}\n\tfmt.Println(gI(&s)) // &s is required because Get() is defined with a receiver type pointer\n\tvar r RSimple = RSimple{60, 60}\n\tfmt.Println(gI(&r))\n}\n\n/* Output:\n6\n60\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_11/sort/sort.go",
    "content": "// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\npackage sort\n\ntype Sorter interface {\n\tLen() int\n\tLess(i, j int) bool\n\tSwap(i, j int)\n}\n\n/*\nfunc Sort(Sorter Interface) {\n\tfor i := 1; i < data.Len(); i++ {\n\t\tfor j := i; j > 0 && data.Less(j, j-1); j-- {\n\t\t\tdata.Swap(j, j-1)\n\t\t}\n\t}\n}\n*/\n\nfunc Sort(data Sorter) {\n\tfor pass := 1; pass < data.Len(); pass++ {\n\t\tfor i := 0; i < data.Len()-pass; i++ {\n\t\t\tif data.Less(i+1, i) {\n\t\t\t\tdata.Swap(i, i+1)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc IsSorted(data Sorter) bool {\n\tn := data.Len()\n\tfor i := n - 1; i > 0; i-- {\n\t\tif data.Less(i, i-1) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// Convenience types for common cases\ntype IntArray []int\n\nfunc (p IntArray) Len() int           { return len(p) }\nfunc (p IntArray) Less(i, j int) bool { return p[i] < p[j] }\nfunc (p IntArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }\n\ntype StringArray []string\n\nfunc (p StringArray) Len() int           { return len(p) }\nfunc (p StringArray) Less(i, j int) bool { return p[i] < p[j] }\nfunc (p StringArray) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }\n\n// Convenience wrappers for common cases\nfunc SortInts(a []int)       { Sort(IntArray(a)) }\nfunc SortStrings(a []string) { Sort(StringArray(a)) }\n\nfunc IntsAreSorted(a []int) bool       { return IsSorted(IntArray(a)) }\nfunc StringsAreSorted(a []string) bool { return IsSorted(StringArray(a)) }\n"
  },
  {
    "path": "eBook/exercises/chapter_11/sort_persons.go",
    "content": "// sort_persons.go\npackage main\n\nimport (\n\t\"./sort\"\n\t\"fmt\"\n)\n\ntype Person struct {\n\tfirstName string\n\tlastName  string\n}\n\ntype Persons []Person\n\nfunc (p Persons) Len() int { return len(p) }\n\nfunc (p Persons) Less(i, j int) bool {\n\tin := p[i].lastName + \" \" + p[i].firstName\n\tjn := p[j].lastName + \" \" + p[j].firstName\n\treturn in < jn\n}\n\nfunc (p Persons) Swap(i, j int) {\n\tp[i], p[j] = p[j], p[i]\n}\n\nfunc main() {\n\tp1 := Person{\"Xavier\", \"Papadopoulos\"}\n\tp2 := Person{\"Chris\", \"Naegels\"}\n\tp3 := Person{\"John\", \"Doe\"}\n\tarrP := Persons{p1, p2, p3}\n\tfmt.Printf(\"Before sorting: %v\\n\", arrP)\n\tsort.Sort(arrP)\n\tfmt.Printf(\"After sorting: %v\\n\", arrP)\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_11/stack/stack_general.go",
    "content": "// stack.go\npackage stack\n\nimport \"errors\"\n\ntype Stack []interface{}\n\nfunc (stack Stack) Len() int {\n\treturn len(stack)\n}\n\nfunc (stack Stack) Cap() int {\n\treturn cap(stack)\n}\n\nfunc (stack Stack) IsEmpty() bool {\n\treturn len(stack) == 0\n}\n\nfunc (stack *Stack) Push(e interface{}) {\n\t*stack = append(*stack, e)\n}\n\nfunc (stack Stack) Top() (interface{}, error) {\n\tif len(stack) == 0 {\n\t\treturn nil, errors.New(\"stack is empty\")\n\t}\n\treturn stack[len(stack)-1], nil\n}\n\nfunc (stack *Stack) Pop() (interface{}, error) {\n\tstk := *stack // dereference to a local variable stk\n\tif len(stk) == 0 {\n\t\treturn nil, errors.New(\"stack is empty\")\n\t}\n\ttop := stk[len(stk)-1]\n\t*stack = stk[:len(stk)-1] // shrink the stack\n\treturn top, nil\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_11/stack/stack_general_v2.go",
    "content": "// stack_general_v2.go\n// Package collection implements a generic stack.\npackage collection\n\n// The zero value for Stack is an empty stack ready to use.\ntype Stack struct {\n\tdata []interface{}\n}\n\n// Push adds x to the top of the stack.\nfunc (s *Stack) Push(x interface{}) {\n\ts.data = append(s.data, x)\n}\n\n// Pop removes and returns the top element of the stack.\n// It's a run-time error to call Pop on an empty stack.\nfunc (s *Stack) Pop() interface{} {\n\ti := len(s.data) - 1\n\tres := s.data[i]\n\ts.data[i] = nil // to avoid memory leak\n\ts.data = s.data[:i]\n\treturn res\n}\n\n// Size returns the number of elements in the stack.\nfunc (s *Stack) Size() int {\n\treturn len(s.data)\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_12/calculator.go",
    "content": "// calculator.go\n// \texample: calculate 3 + 4 = 7 as input: 3 ENTER 4 ENTER + ENTER --> result = 7,\n\npackage main\n\nimport (\n\t\"./stack/stack\"\n\t\"bufio\"\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n)\n\nfunc main() {\n\tbuf := bufio.NewReader(os.Stdin)\n\tcalc1 := new(stack.Stack)\n\tfmt.Println(\"Give a number, an operator (+, -, *, /), or q to stop:\")\n\tfor {\n\t\ttoken, err := buf.ReadString('\\n')\n\t\tif err != nil {\n\t\t\tfmt.Println(\"Input error !\")\n\t\t\tos.Exit(1)\n\t\t}\n\t\ttoken = token[0 : len(token)-2] // remove \"\\r\\n\"\n\t\t// fmt.Printf(\"--%s--\\n\",token)  // debug statement\n\t\tswitch {\n\t\tcase token == \"q\": // stop als invoer = \"q\"\n\t\t\tfmt.Println(\"Calculator stopped\")\n\t\t\treturn\n\t\tcase token >= \"0\" && token <= \"999999\":\n\t\t\ti, _ := strconv.Atoi(token)\n\t\t\tcalc1.Push(i)\n\t\tcase token == \"+\":\n\t\t\tq, _ := calc1.Pop()\n\t\t\tp, _ := calc1.Pop()\n\t\t\tfmt.Printf(\"The result of %d %s %d = %d\\n\", p, token, q, p.(int)+q.(int))\n\t\tcase token == \"-\":\n\t\t\tq, _ := calc1.Pop()\n\t\t\tp, _ := calc1.Pop()\n\t\t\tfmt.Printf(\"The result of %d %s %d = %d\\n\", p, token, q, p.(int)-q.(int))\n\n\t\tcase token == \"*\":\n\t\t\tq, _ := calc1.Pop()\n\t\t\tp, _ := calc1.Pop()\n\t\t\tfmt.Printf(\"The result of %d %s %d = %d\\n\", p, token, q, p.(int)*q.(int))\n\n\t\tcase token == \"/\":\n\t\t\tq, _ := calc1.Pop()\n\t\t\tp, _ := calc1.Pop()\n\t\t\tfmt.Printf(\"The result of %d %s %d = %d\\n\", p, token, q, p.(int)/q.(int))\n\t\tdefault:\n\t\t\tfmt.Println(\"No valid input\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_12/cat_numbered.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n)\n\nvar numberFlag = flag.Bool(\"n\", false, \"number each line\")\n\nfunc cat(r *bufio.Reader) {\n\ti := 1\n\tfor {\n\t\tbuf, err := r.ReadBytes('\\n')\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t}\n\t\tif *numberFlag {\n\t\t\tfmt.Fprintf(os.Stdout, \"%5d %s\", i, buf)\n\t\t\ti++\n\t\t} else {\n\t\t\tfmt.Fprintf(os.Stdout, \"%s\", buf)\n\t\t}\n\t}\n\treturn\n}\n\nfunc main() {\n\tflag.Parse()\n\tif flag.NArg() == 0 {\n\t\tcat(bufio.NewReader(os.Stdin))\n\t}\n\tfor i := 0; i < flag.NArg(); i++ {\n\t\tf, err := os.Open(flag.Arg(i))\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"%s:error reading from %s: %s\\n\", os.Args[0], flag.Arg(i), err.Error())\n\t\t\tcontinue\n\t\t}\n\t\tcat(bufio.NewReader(f))\n\t\tf.Close()\n\t}\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_12/degob.go",
    "content": "// degob.go\npackage main\n\nimport (\n\t\"bufio\"\n\t\"encoding/gob\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n)\n\ntype Address struct {\n\tType    string\n\tCity    string\n\tCountry string\n}\n\ntype VCard struct {\n\tFirstName string\n\tLastName  string\n\tAddresses []*Address\n\tRemark    string\n}\n\nvar content string\nvar vc VCard\n\nfunc main() {\n\t// using a decoder:\n\tfile, _ := os.Open(\"vcard.gob\")\n\tdefer file.Close()\n\tinReader := bufio.NewReader(file)\n\tdec := gob.NewDecoder(inReader)\n\terr := dec.Decode(&vc)\n\tif err != nil {\n\t\tlog.Println(\"Error in decoding gob\")\n\t}\n\tfmt.Println(vc)\n}\n\n// Output:\n// {Jan Kersschot [0x12642e60 0x12642e80] none}\n"
  },
  {
    "path": "eBook/exercises/chapter_12/goprogram",
    "content": "// goprogram.go\r\npackage main\r\n\r\nimport (\r\n\t\"fmt\"\r\n)\r\n\r\nfunc main() {\r\n\tfmt.Println(\"Hello World!\")\r\n}\r\n"
  },
  {
    "path": "eBook/exercises/chapter_12/hash_md5.go",
    "content": "// hash_md5.go\npackage main\n\nimport (\n\t\"crypto/md5\"\n\t\"fmt\"\n\t\"io\"\n)\n\nfunc main() {\n\thasher := md5.New()\n\tb := []byte{}\n\tio.WriteString(hasher, \"test\")\n\tfmt.Printf(\"Result: %x\\n\", hasher.Sum(b))\n\tfmt.Printf(\"Result: %d\\n\", hasher.Sum(b))\n}\n\n/* Output:\nResult: 098f6bcd4621d373cade4e832627b4f6\nResult: [9 143 107 205 70 33 211 115 202 222 78 131 38 39 180 246]\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_12/hello_who.go",
    "content": "// hello_who.go\npackage main\n \nimport (\n\t\"fmt\"\n\t\"os\"      \n\t\"strings\"\n)\n\nfunc main(){    \n\twho := \"\"\n\tif len(os.Args) > 1 {\n\t\twho += strings.Join(os.Args[1:], \" \")\n\t}\n\tfmt.Printf(\"Hello %s!\\n\",who)\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_12/products.txt",
    "content": "\"The ABC of Go\";25.5;1500\r\n\"Functional Programming with Go\";56;280\r\n\"Go for It\";45.9;356\r\n\"The Go Way\";55;500"
  },
  {
    "path": "eBook/exercises/chapter_12/read_csv.go",
    "content": "// read_csv.go\npackage main\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n)\n\ntype Book struct {\n\ttitle    string\n\tprice    float64\n\tquantity int\n}\n\nfunc main() {\n\tbks := make([]Book, 1)\n\tfile, err := os.Open(\"products.txt\")\n\tif err != nil {\n\t\tlog.Fatalf(\"Error %s opening file products.txt: \", err)\n\t}\n\tdefer file.Close()\n\n\treader := bufio.NewReader(file)\n\tfor {\n\t\t// read one line from the file:\n\t\tline, err := reader.ReadString('\\n')\n\t\treadErr := err\n\t\t// remove \\r and \\n so 2(in Windows, in Linux only \\n, so 1):\n\t\tline = string(line[:len(line)-2])\n\t\t//fmt.Printf(\"The input was: -%s-\", line)\n\n\t\tstrSl := strings.Split(line, \";\")\n\t\tbook := new(Book)\n\t\tbook.title = strSl[0]\n\t\tbook.price, err = strconv.ParseFloat(strSl[1], 32)\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"Error in file: %v\", err)\n\t\t}\n\t\t//fmt.Printf(\"The quan was:-%s-\", strSl[2])\n\t\tbook.quantity, err = strconv.Atoi(strSl[2])\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"Error in file: %v\", err)\n\t\t}\n\t\tif bks[0].title == \"\" {\n\t\t\tbks[0] = *book\n\t\t} else {\n\t\t\tbks = append(bks, *book)\n\t\t}\n\t\tif readErr == io.EOF {\n\t\t\tbreak\n\t\t}\n\t}\n\tfmt.Println(\"We have read the following books from the file: \")\n\tfor _, bk := range bks {\n\t\tfmt.Println(bk)\n\t}\n}\n\n/* Output:\nWe have read the following books from the file:\n{\"The ABC of Go\" 25.5 1500}\n{\"Functional Programming with Go\" 56 280}\n{\"Go for It\" 45.900001525878906 356}\n{\"The Go Way\" 55 5}\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_12/remove_3till5char.go",
    "content": "// remove_first6char.go\npackage main\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n)\n\nfunc main() {\n\tinputFile, _ := os.Open(\"goprogram\")\n\toutputFile, _ := os.OpenFile(\"goprogramT\", os.O_WRONLY|os.O_CREATE, 0666)\n\tdefer inputFile.Close()\n\tdefer outputFile.Close()\n\tinputReader := bufio.NewReader(inputFile)\n\toutputWriter := bufio.NewWriter(outputFile)\n\tvar outputString string\n\tfor {\n\t\t// inputString, readerError := inputReader.ReadString('\\n')\n\t\tinputString, _, readerError := inputReader.ReadLine()\n\t\tif readerError == io.EOF {\n\t\t\tfmt.Println(\"EOF\")\n\t\t\tbreak\n\t\t}\n\t\t//fmt.Printf(\"The input was: --%s--\", inputString)\n\t\tif len(inputString) < 3 {\n\t\t\toutputString = \"\\r\\n\"\n\t\t} else if len(inputString) < 5 {\n\t\t\toutputString = string([]byte(inputString)[2:len(inputString)]) + \"\\r\\n\"\n\t\t} else {\n        \t\toutputString = string([]byte(inputString)[2:5]) + \"\\r\\n\"\n\t\t}\n\t\t//fmt.Printf(\"The output was: --%s--\", outputString)\n\t\t_, err := outputWriter.WriteString(outputString)\n\t\t//fmt.Printf(\"Number of bytes written %d\\n\", n)\n\t\tif err != nil {\n\t\t\tfmt.Println(err)\n\t\t\treturn\n\t\t}\n\t}\n\toutputWriter.Flush()\n\tfmt.Println(\"Conversion done\")\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_12/stack/stack_struct.go",
    "content": "// stack_struct.go\npackage stack\n\nimport \"strconv\"\n\nconst LIMIT = 10\n\ntype Stack struct {\n\tix   int // first free position, so data[ix] == 0\n\tdata [LIMIT]int\n}\n\nfunc (st *Stack) Push(n int) {\n\tif st.ix+1 > LIMIT {\n\t\treturn // stack is full!\n\t}\n\tst.data[st.ix] = n\n\tst.ix++\n}\n\nfunc (st *Stack) Pop() int {\n\tst.ix--\n\treturn st.data[st.ix]\n}\n\nfunc (st Stack) String() string {\n\tstr := \"\"\n\tfor ix := 0; ix < st.ix; ix++ {\n\t\tstr += \"[\" + strconv.Itoa(ix) + \":\" + strconv.Itoa(st.data[ix]) + \"] \"\n\t}\n\treturn str\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_12/test",
    "content": "hello, world in a file\r\nthis is a second line\r\nthis is a third line\r\n"
  },
  {
    "path": "eBook/exercises/chapter_12/wiki_part1.go",
    "content": "// wiki_part1.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"io/ioutil\"\n)\n\ntype Page struct {\n\tTitle string\n\tBody  []byte\n}\n\nfunc (this *Page) save() (err error) {\n\treturn ioutil.WriteFile(this.Title, this.Body, 0666)\n}\n\nfunc (this *Page) load(title string) (err error) {\n\tthis.Title = title\n\tthis.Body, err = ioutil.ReadFile(this.Title)\n\treturn err\n}\n\nfunc main() {\n\tpage := Page{\n\t\t\"Page.md\",\n\t\t[]byte(\"# Page\\n## Section1\\nThis is section1.\"),\n\t}\n\tpage.save()\n\n\t// load from Page.md\n\tvar new_page Page\n\tnew_page.load(\"Page.md\")\n\tfmt.Println(string(new_page.Body))\n}\n\n/* Output:\n * # Page\n * ## Section1\n * This is section1.\n */\n"
  },
  {
    "path": "eBook/exercises/chapter_12/word_letter_count.go",
    "content": "// Q28_word_letter_count.go\npackage main\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n)\n\nvar nrchars, nrwords, nrlines int\n\nfunc main() {\n\tnrchars, nrwords, nrlines = 0, 0, 0\n\tinputReader := bufio.NewReader(os.Stdin)\n\tfmt.Println(\"Please enter some input, type S to stop: \")\n\tfor {\n\t\tinput, err := inputReader.ReadString('\\n')\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"An error occurred: %s\\n\", err)\n\t\t}\n\t\tif input == \"S\\r\\n\" { // Windows, on Linux it is \"S\\n\"\n\t\t\tfmt.Println(\"Here are the counts:\")\n\t\t\tfmt.Printf(\"Number of characters: %d\\n\", nrchars)\n\t\t\tfmt.Printf(\"Number of words: %d\\n\", nrwords)\n\t\t\tfmt.Printf(\"Number of lines: %d\\n\", nrlines)\n\t\t\tos.Exit(0)\n\t\t}\n\t\tCounters(input)\n\t}\n}\n\nfunc Counters(input string) {\n\tnrchars += len(input) - 2 // -2 for \\r\\n\n\tnrwords += len(strings.Fields(input))\n\tnrlines++\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_13/Makefile",
    "content": "include $(GOROOT)/src/Make.inc\r\nTARG=strev\r\nGOFILES=\\\r\n\tstring_reverse.go\\\r\n\r\ninclude $(GOROOT)/src/Make.pkg\r\n"
  },
  {
    "path": "eBook/exercises/chapter_13/panic_defer.go",
    "content": "// panic_defer.go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tf()\n\tfmt.Println(\"Returned normally from f.\")\n}\n\nfunc f() {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tfmt.Println(\"Recovered in f\", r)\n\t\t}\n\t}()\n\tfmt.Println(\"Calling g.\")\n\tg(0)\n\tfmt.Println(\"Returned normally from g.\")\n}\n\nfunc g(i int) {\n\tif i > 3 {\n\t\tfmt.Println(\"Panicking!\")\n\t\tpanic(fmt.Sprintf(\"%v\", i))\n\t}\n\tdefer fmt.Println(\"Defer in g\", i)\n\tfmt.Println(\"Printing in g\", i)\n\tg(i + 1)\n}\n\n/* Output:\nCalling g.\nPrinting in g 0\nPrinting in g 1\nPrinting in g 2\nPrinting in g 3\nPanicking!\nDefer in g 3\nDefer in g 2\nDefer in g 1\nDefer in g 0\nRecovered in f 4\nReturned normally from f.\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_13/panic_defer_convint.go",
    "content": "// panic_defer_convint.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n)\n\nfunc main() {\n\tl := int64(15000)\n\tif i, err := IntFromInt64(l); err != nil {\n\t\tfmt.Printf(\"The conversion of %d to an int32 resulted in an error: %s\", l, err.Error())\n\t} else {\n\t\tfmt.Printf(\"%d converted to an int32 is %d\", l, i)\n\t}\n\tfmt.Println()\n\tl = int64(math.MaxInt32 + 15000)\n\tif i, err := IntFromInt64(l); err != nil {\n\t\tfmt.Printf(\"The conversion of %d to an int32 resulted in an error: %s\", l, err.Error())\n\t} else {\n\t\tfmt.Printf(\"%d converted to an int32 is %d\", l, i)\n\t}\n}\n\nfunc ConvertInt64ToInt(l int64) int {\n\tif math.MinInt32 <= l && l <= math.MaxInt32 {\n\t\treturn int(l)\n\t}\n\tpanic(fmt.Sprintf(\"%d is out of the int32 range\", l))\n}\n\nfunc IntFromInt64(l int64) (i int, err error) {\n\tdefer func() {\n\t\tif e := recover(); e != nil {\n\t\t\terr = fmt.Errorf(\"%v\", e)\n\t\t}\n\t}()\n\ti = ConvertInt64ToInt(l)\n\treturn i, nil\n}\n\n/* Output:\n15000 converted to an int32 is 15000\nThe conversion of 2147498647 to an int32 resulted in an error: 2147498647 is out of the int32 range\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_13/recover_divbyzero.go",
    "content": "// recover_divbyzero.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc badCall() {\n\ta, b := 10, 0\n\tn := a / b\n\tfmt.Println(n)\n}\n\nfunc test() {\n\tdefer func() {\n\t\tif e := recover(); e != nil {\n\t\t\tfmt.Printf(\"Panicing %s\\r\\n\", e)\n\t\t}\n\n\t}()\n\tbadCall()\n\tfmt.Printf(\"After bad call\\r\\n\")\n}\n\nfunc main() {\n\tfmt.Printf(\"Calling test\\r\\n\")\n\ttest()\n\tfmt.Printf(\"Test completed\\r\\n\")\n}\n\n/* Output:\nCalling test\nPanicing runtime error: integer divide by zero\nTest completed\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_13/string_reverse.go",
    "content": "// string_reverse.go\npackage strev\n\nfunc Reverse(s string) string {\n\trunes := []rune(s)\n\tn, h := len(runes), len(runes)/2\n\tfor i := 0; i < h; i++ {\n\t\trunes[i], runes[n-1-i] = runes[n-1-i], runes[i]\n\t}\n\treturn string(runes)\n}\n\n/*\nfunc main() {\n\ts := \"My Test String!\"\n\tfmt.Println(s, \" --> \", Reverse(s))\n}\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_13/string_reverse_test.go",
    "content": "// string_reverse_test.go\npackage strev\n\nimport \"testing\"\nimport \"./strev\"\n\ntype ReverseTest struct {\n\tin, out string\n}\n\nvar ReverseTests = []ReverseTest{\n\t{\"ABCD\", \"DCBA\"},\n\t{\"CVO-AZ\", \"ZA-OVC\"},\n\t{\"Hello 世界\", \"界世 olleH\"},\n}\n\nfunc TestReverse(t *testing.T) {\n\t/*\n\t\tin := \"CVO-AZ\"\n\t\tout := Reverse(in)\n\t\texp := \"ZA-OVC\"\n\t\tif out != exp {\n\t\t\tt.Errorf(\"Reverse of %s expects %s, but got %s\", in, exp, out)\n\t\t}\n\t*/\n\t// testing with a battery of testdata:\n\tfor _, r := range ReverseTests {\n\t\texp := strev.Reverse(r.in)\n\t\tif r.out != exp {\n\t\t\tt.Errorf(\"Reverse of %s expects %s, but got %s\", r.in, exp, r.out)\n\t\t}\n\t}\n}\n\nfunc BenchmarkReverse(b *testing.B) {\n\ts := \"ABCD\"\n\tfor i := 0; i < b.N; i++ {\n\t\tstrev.Reverse(s)\n\t}\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_14/blocking.go",
    "content": "// blocking.go\n// throw: all goroutines are asleep - deadlock!\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc f1(in chan int) {\n\tfmt.Println(<-in)\n}\n\nfunc main() {\n\tout := make(chan int)\n\t//out := make(chan int, 1) // solution 2\n\t// go f1(out)  // solution 1\n\tout <- 2\n\tgo f1(out)\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_14/channel_block3.go",
    "content": "package main\n\nimport \"fmt\"\nimport \"time\"\n\nfunc main() {\n\tc := make(chan int)\n\tgo func() {\n\t\ttime.Sleep(15 * 1e9)\n\t\tx := <-c\n\t\tfmt.Println(\"received\", x)\n\t}()\n\tfmt.Println(\"sending\", 10)\n\tc <- 10\n\tfmt.Println(\"sent\", 10)\n}\n\n/* Output:\nsending 10\n(15 s later):\nreceived 10\nsent 10\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_14/channel_buffer.go",
    "content": "package main\n\nimport \"fmt\"\nimport \"time\"\n\nfunc main() {\n\tc := make(chan int, 50)\n\tgo func() {\n\t\ttime.Sleep(15 * 1e9)\n\t\tx := <-c\n\t\tfmt.Println(\"received\", x)\n\t}()\n\tfmt.Println(\"sending\", 10)\n\tc <- 10\n\tfmt.Println(\"sent\", 10)\n}\n\n/* Output:\nsending 10\nsent 10   // prints immediately\nno further output, because main() then stops\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_14/concurrent_pi.go",
    "content": "// Concurrent computation of pi.\n// See http://goo.gl/ZuTZM. - Comparison with Scala!\n//\n// This demonstrates Go's ability to handle\n// large numbers of concurrent processes.\n// It is an unreasonable way to calculate pi.\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"time\"\n)\n\nfunc main() {\n\tstart := time.Now()\n\tfmt.Println(CalculatePi(5000))\n\tend := time.Now()\n\tdelta := end.Sub(start)\n\tfmt.Printf(\"longCalculation took this amount of time: %s\\n\", delta)\n}\n\n// CalculatePi launches n goroutines to compute an\n// series-approximation of pi.\nfunc CalculatePi(n int) float64 {\n\tch := make(chan float64)\n\tfor k := 0; k <= n; k++ {\n\t\t// calculate k-th term in the series\n\t\tgo term(ch, float64(k))\n\t}\n\tf := 0.0\n\t//wait for all goroutines to complete, get and sum up their results:\n\tfor k := 0; k <= n; k++ {\n\t\tf += <-ch\n\t}\n\treturn f\n}\n\nfunc term(ch chan float64, k float64) {\n\tch <- 4 * math.Pow(-1, k) / (2*k + 1)\n}\n\n/* Output:\n3.14179261359579\nThe calculation took this amount of time: 0.028002\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_14/concurrent_pi2.go",
    "content": "// concurrent_pi2.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"runtime\"\n\t\"time\"\n)\n\nconst NCPU = 2\n\nfunc main() {\n\tstart := time.Now()\n\truntime.GOMAXPROCS(2)\n\tfmt.Println(CalculatePi(5000))\n\tend := time.Now()\n\tdelta := end.Sub(start)\n\tfmt.Printf(\"longCalculation took this amount of time: %s\\n\", delta)\n}\n\nfunc CalculatePi(end int) float64 {\n\tch := make(chan float64)\n\tfor i := 0; i < NCPU; i++ {\n\t\tgo term(ch, i*end/NCPU, (i+1)*end/NCPU)\n\t}\n\tresult := 0.0\n\tfor i := 0; i < NCPU; i++ {\n\t\tresult += <-ch\n\t}\n\treturn result\n}\n\nfunc term(ch chan float64, start, end int) {\n\tresult := 0.0\n\tfor i := start; i < end; i++ {\n\t\tx := float64(i)\n\t\tresult += 4 * (math.Pow(-1, x) / (2.0*x + 1.0))\n\t}\n\tch <- result\n}\n\n/* Output:\n3.1413926535917938\nThe calculation took this amount of time: 0.002000\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_14/general_lazy_evalution2.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n)\n\ntype Any interface{}\ntype EvalFunc func(Any) (Any, Any)\n\nfunc main() {\n\tfibFunc := func(state Any) (Any, Any) {\n\t\tos := state.([]uint64)\n\t\tv1 := os[0]\n\t\tv2 := os[1]\n\t\tns := []uint64{v2, v1 + v2}\n\t\treturn v1, ns\n\t}\n\tfib := BuildLazyUInt64Evaluator(fibFunc, []uint64{0, 1})\n\n\tfor i := 0; i < 10; i++ {\n\t\tfmt.Printf(\"Fib nr %v: %v\\n\", i, fib())\n\t}\n}\n\nfunc BuildLazyEvaluator(evalFunc EvalFunc, initState Any) func() Any {\n\tretValChan := make(chan Any)\n\tloopFunc := func() {\n\t\tvar actState Any = initState\n\t\tvar retVal Any\n\t\tfor {\n\t\t\tretVal, actState = evalFunc(actState)\n\t\t\tretValChan <- retVal\n\t\t}\n\t}\n\tretFunc := func() Any {\n\t\treturn <-retValChan\n\t}\n\tgo loopFunc()\n\treturn retFunc\n}\n\nfunc BuildLazyUInt64Evaluator(evalFunc EvalFunc, initState Any) func() uint64 {\n\tef := BuildLazyEvaluator(evalFunc, initState)\n\treturn func() uint64 {\n\t\treturn ef().(uint64)\n\t}\n}\n\n/* Output:\nFib nr 0: 0\nFib nr 1: 1\nFib nr 2: 1\nFib nr 3: 2\nFib nr 4: 3\nFib nr 5: 5\nFib nr 6: 8\nFib nr 7: 13\nFib nr 8: 21\nFib nr 9: 34\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_14/gofibonacci.go",
    "content": "// Q26_fibonacci_go.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n)\n\nfunc main() {\n\tterm := 25\n\ti := 0\n\tc := make(chan int)\n\tstart := time.Now()\n\n\tgo fibnterms(term, c)\n\tfor {\n\t\tif result, ok := <-c; ok {\n\t\t\tfmt.Printf(\"fibonacci(%d) is: %d\\n\", i, result)\n\t\t\ti++\n\t\t} else {\n\t\t\tend := time.Now()\n\t\t\tdelta := end.Sub(start)\n\t\t\tfmt.Printf(\"longCalculation took this amount of time: %s\\n\", delta)\n\t\t\tos.Exit(0)\n\t\t}\n\t}\n}\n\nfunc fibnterms(term int, c chan int) {\n\tfor i := 0; i <= term; i++ {\n\t\tc <- fibonacci(i)\n\t}\n\tclose(c)\n}\n\nfunc fibonacci(n int) (res int) {\n\tif n <= 1 {\n\t\tres = 1\n\t} else {\n\t\tres = fibonacci(n-1) + fibonacci(n-2)\n\t}\n\treturn\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_14/gofibonacci2.go",
    "content": "// gofibonacci2.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc fibonacci(n int, c chan int) {\n\tx, y := 1, 1\n\tfor i := 0; i < n; i++ {\n\t\tc <- x\n\t\tx, y = y, x+y\n\t}\n\tclose(c)\n}\n\nfunc main() {\n\tc := make(chan int, 10)\n\tgo fibonacci(cap(c), c)\n\tfor i := range c {\n\t\tfmt.Println(i)\n\t}\n}\n\n/* Output:\n1\n1\n2\n3\n5\n8\n13\n21\n34\n55\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_14/gofibonacci3.go",
    "content": "// courtesy of: http://sdh33b.blogspot.com/2009/12/fibonacci-in-go.html\npackage main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc dup3(in <-chan int) (<-chan int, <-chan int, <-chan int) {\n\ta, b, c := make(chan int, 2), make(chan int, 2), make(chan int, 2)\n\tgo func() {\n\t\tfor {\n\t\t\tx := <-in\n\t\t\ta <- x\n\t\t\tb <- x\n\t\t\tc <- x\n\t\t}\n\t}()\n\treturn a, b, c\n}\n\nfunc fib() <-chan int {\n\tx := make(chan int, 2)\n\ta, b, out := dup3(x)\n\tgo func() {\n\t\tx <- 0\n\t\tx <- 1\n\t\t<-a\n\t\tfor {\n\t\t\tx <- <-a + <-b\n\t\t}\n\t}()\n\t<- out\n\treturn out\n}\n\nfunc main() {\n\tstart := time.Now()\n\tx := fib()\n\tfor i := 0; i < 10; i++ {\n\t\tfmt.Println(<-x)\n\t}\n\tend := time.Now()\n\tdelta := end.Sub(start)\n\tfmt.Printf(\"longCalculation took this amount of time: %s\\n\", delta)\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_14/gofibonacci_select.go",
    "content": "// gofibonacci_select.go\npackage main\n\nimport \"fmt\"\n\nfunc fibonacci(c, quit chan int) {\n\tx, y := 1, 1\n\tfor {\n\t\tselect {\n\t\tcase c <- x:\n\t\t\tx, y = y, x+y\n\t\tcase <-quit:\n\t\t\tfmt.Println(\"quit\")\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc main() {\n\tc := make(chan int)\n\tquit := make(chan int)\n\tgo func() {\n\t\tfor i := 0; i < 10; i++ {\n\t\t\tfmt.Println(<-c)\n\t\t}\n\t\tquit <- 0\n\t}()\n\tfibonacci(c, quit)\n}\n\n/* Output:\n1\n1\n2\n3\n5\n8\n13\n21\n34\n55\nquit\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_14/goroutine_close.go",
    "content": "// Q20_goroutine.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc tel(ch chan int) {\n\tfor i := 0; i < 15; i++ {\n\t\tch <- i\n\t}\n\tclose(ch) // if this is ommitted: panic: all goroutines are asleep - deadlock!\n}\n\nfunc main() {\n\tvar ok = true\n\tvar i int\n\tch := make(chan int)\n\n\tgo tel(ch)\n\tfor ok {\n\t\tif i, ok = <-ch; ok {\n\t\t\tfmt.Printf(\"ok is %t and the counter is at %d\\n\", ok, i)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_14/goroutine_panic.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n)\n\nfunc tel(ch chan int) {\n\tfor i := 0; i < 15; i++ {\n\t\tch <- i\n\t}\n}\n\nfunc main() {\n\tvar ok = true\n\tch := make(chan int)\n\n\tgo tel(ch)\n\tfor ok {\n\t\ti := <-ch\n\t\tfmt.Printf(\"ok is %t and the counter is at %d\\n\", ok, i)\n\t}\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_14/goroutine_select.go",
    "content": "// Q20b_goroutine.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n)\n\nfunc tel(ch chan int, quit chan bool) {\n\tfor i := 0; i < 15; i++ {\n\t\tch <- i\n\t}\n\tquit <- true\n}\n\nfunc main() {\n\tvar ok = true\n\tch := make(chan int)\n\tquit := make(chan bool)\n\n\tgo tel(ch, quit)\n\tfor ok {\n\t\tselect {\n\t\tcase i := <-ch:\n\t\t\tfmt.Printf(\"The counter is at %d\\n\", i)\n\t\tcase <-quit:\n\t\t\tos.Exit(0)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_14/gosum.go",
    "content": "// gosum.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc sum(x, y int, c chan int) {\n\tc <- x + y\n}\n\nfunc main() {\n\tc := make(chan int)\n\tgo sum(12, 13, c)\n\tfmt.Println(<-c) // 25\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_14/multiplex_server3.go",
    "content": "// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\npackage main\n\nimport \"fmt\"\n\ntype Request struct {\n\ta, b   int\n\treplyc chan int // reply channel inside the Request\n}\n\ntype binOp func(a, b int) int\n\nfunc run(op binOp, req *Request) {\n\treq.replyc <- op(req.a, req.b)\n}\n\nfunc (r *Request) String() string {\n\treturn fmt.Sprintf(\"%d+%d=%d\", r.a, r.b, <-r.replyc)\n}\n\nfunc server(op binOp, service chan *Request, quit chan bool) {\n\tfor {\n\t\tselect {\n\t\tcase req := <-service:\n\t\t\tgo run(op, req)\n\t\tcase <-quit:\n\t\t\treturn // stop infinite loop\n\t\t}\n\t}\n}\n\nfunc startServer(op binOp) (service chan *Request, quit chan bool) {\n\tservice = make(chan *Request)\n\tquit = make(chan bool)\n\tgo server(op, service, quit)\n\treturn service, quit\n}\n\nfunc main() {\n\tadder, quit := startServer(func(a, b int) int { return a + b })\n\t// make requests:\n\treq1 := &Request{3, 4, make(chan int)}\n\treq2 := &Request{150, 250, make(chan int)}\n\t// send requests on the service channel\n\tadder <- req1\n\tadder <- req2\n\t// ask for the results: ( method String() is called )\n\tfmt.Println(req1, req2)\n\t// shutdown server:\n\tquit <- true\n\tfmt.Print(\"done\")\n}\n\n/* output:\n3+4=7 150+250=400\ndone\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_14/polar_to_cartesian.go",
    "content": "// polartocartesian.go\npackage main\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"math\"\n\t\"os\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n)\n\ntype polar struct {\n\tradius float64\n\tΘ      float64\n}\n\ntype cartesian struct {\n\tx float64\n\ty float64\n}\n\nconst result = \"Polar: radius=%.02f angle=%.02f degrees -- Cartesian: x=%.02f y=%.02f\\n\"\n\nvar prompt = \"Enter a radius and an angle (in degrees), e.g., 12.5 90, \" + \"or %s to quit.\"\n\nfunc init() {\n\tif runtime.GOOS == \"windows\" {\n\t\tprompt = fmt.Sprintf(prompt, \"Ctrl+Z, Enter\")\n\t} else { // Unix-like\n\t\tprompt = fmt.Sprintf(prompt, \"Ctrl+D\")\n\t}\n}\n\nfunc main() {\n\tquestions := make(chan polar)\n\tdefer close(questions)\n\tanswers := createSolver(questions)\n\tdefer close(answers)\n\tinteract(questions, answers)\n}\n\nfunc createSolver(questions chan polar) chan cartesian {\n\tanswers := make(chan cartesian)\n\tgo func() {\n\t\tfor {\n\t\t\tpolarCoord := <-questions\n\t\t\tΘ := polarCoord.Θ * math.Pi / 180.0 // degrees to radians\n\t\t\tx := polarCoord.radius * math.Cos(Θ)\n\t\t\ty := polarCoord.radius * math.Sin(Θ)\n\t\t\tanswers <- cartesian{x, y}\n\t\t}\n\t}()\n\treturn answers\n}\n\nfunc interact(questions chan polar, answers chan cartesian) {\n\treader := bufio.NewReader(os.Stdin)\n\tfmt.Println(prompt)\n\tfor {\n\t\tfmt.Printf(\"Radius and angle: \")\n\t\tline, err := reader.ReadString('\\n')\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tline = line[:len(line)-1] // chop of newline character\n\t\tif numbers := strings.Fields(line); len(numbers) == 2 {\n\t\t\tpolars, err := floatsForStrings(numbers)\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(os.Stderr, \"invalid number\")\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tquestions <- polar{polars[0], polars[1]}\n\t\t\tcoord := <-answers\n\t\t\tfmt.Printf(result, polars[0], polars[1], coord.x, coord.y)\n\t\t} else {\n\t\t\tfmt.Fprintln(os.Stderr, \"invalid input\")\n\t\t}\n\t}\n\tfmt.Println()\n}\n\nfunc floatsForStrings(numbers []string) ([]float64, error) {\n\tvar floats []float64\n\tfor _, number := range numbers {\n\t\tif x, err := strconv.ParseFloat(number, 64); err != nil {\n\t\t\treturn nil, err\n\t\t} else {\n\t\t\tfloats = append(floats, x)\n\t\t}\n\t}\n\treturn floats, nil\n}\n\n/* Output:\nEnter a radius and an angle (in degrees), e.g., 12.5 90, or Ctrl+Z, Enter to qui\nt.\nRadius and angle: 12.5 90\nPolar: radius=12.50 angle=90.00 degrees -- Cartesian: x=0.00 y=12.50\nRadius and angle: ^Z\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_14/producer_consumer.go",
    "content": "// goroutines2.go\npackage main\n\nimport \"fmt\"\n\n// integer producer:\nfunc numGen(start, count int, out chan<- int) {\n\tfor i := 0; i < count; i++ {\n\t\tout <- start\n\t\tstart = start + count\n\t}\n\tclose(out)\n}\n\n// integer consumer:\nfunc numEchoRange(in <-chan int, done chan<- bool) {\n\tfor num := range in {\n\t\tfmt.Printf(\"%d\\n\", num)\n\t}\n\tdone <- true\n}\n\nfunc main() {\n\tnumChan := make(chan int)\n\tdone := make(chan bool)\n\tgo numGen(0, 10, numChan)\n\tgo numEchoRange(numChan, done)\n\n\t<-done\n}\n\n/* Output:\n0\n10\n20\n30\n40\n50\n60\n70\n80\n90\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_14/producer_consumer2.go",
    "content": "// prod_cons.go\n/* producer-consumer problem in Go */\npackage main\n\nimport \"fmt\"\n\nvar done = make(chan bool)\nvar msgs = make(chan int)\n\nfunc produce() {\n\tfor i := 0; i < 10; i++ {\n\t\tmsgs <- i\n\t}\n\tdone <- true\n}\n\nfunc consume() {\n\tfor {\n\t\tmsg := <-msgs\n\t\tfmt.Print(msg, \" \")\n\t}\n}\n\nfunc main() {\n\tgo produce()\n\tgo consume()\n\t<-done\n}\n\n// Output: 0 1 2 3 4 5 6 7 8 9\n"
  },
  {
    "path": "eBook/exercises/chapter_14/random_bitgen.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tc := make(chan int)\n\t// consumer:\n\tgo func() {\n\t\tfor {\n\t\t\tfmt.Print(<-c, \" \")\n\t\t}\n\t}()\n\t// producer:\n\tfor {\n\t\tselect {\n\t\tcase c <- 0:\n\t\tcase c <- 1:\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_15/client1.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"strings\"\n)\n\nfunc main() {\n\tvar conn net.Conn\n\tvar error error\n\tvar inputReader *bufio.Reader\n\tvar input string\n\tvar clientName string\n\n\t// maak connectie met de server:\n\tconn, error = net.Dial(\"tcp\", \"localhost:50000\")\n\tcheckError(error)\n\n\tinputReader = bufio.NewReader(os.Stdin)\n\tfmt.Println(\"First, what is your name?\")\n\tclientName, _ = inputReader.ReadString('\\n')\n\t// fmt.Printf(\"CLIENTNAME %s\",clientName)\n\ttrimmedClient := strings.Trim(clientName, \"\\r\\n\") // \"\\r\\n\" voor Windows, \"\\n\" voor Linux\n\n\tfor {\n\t\tfmt.Println(\"What to send to the server? Type Q to quit. Type SH to shutdown server.\")\n\t\tinput, _ = inputReader.ReadString('\\n')\n\t\ttrimmedInput := strings.Trim(input, \"\\r\\n\")\n\t\t// fmt.Printf(\"input:--%s--\",input)\n\t\t// fmt.Printf(\"trimmedInput:--%s--\",trimmedInput)\n\t\tif trimmedInput == \"Q\" {\n\t\t\treturn\n\t\t}\n\t\t_, error = conn.Write([]byte(trimmedClient + \" says: \" + trimmedInput))\n\t\tcheckError(error)\n\t}\n}\n\nfunc checkError(error error) {\n\tif error != nil {\n\t\tpanic(\"Error: \" + error.Error()) // terminate program\n\t}\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_15/hello_server.go",
    "content": "// hello_server.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n)\n\ntype Hello struct{}\n\nfunc (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tfmt.Fprint(w, \"Hello!\")\n}\n\nfunc main() {\n\tvar h Hello\n\thttp.ListenAndServe(\"localhost:4000\", h)\n}\n\n// Output in browser-window with url http://localhost:4000:  Hello!\n"
  },
  {
    "path": "eBook/exercises/chapter_15/http_fetch2.go",
    "content": "// httpfetch.go\npackage main\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"strings\"\n)\n\nfunc main() {\n\tfmt.Print(\"Give the url from which to read: \")\n\tiread := bufio.NewReader(os.Stdin)\n\turl, _ := iread.ReadString('\\n')\n\turl = strings.Trim(url, \" \\n\\r\") // trimming space,etc.\n\t// fmt.Println(\"***\", url,\"***\") // debugging\n\tres, err := http.Get(url)\n\tCheckError(err)\n\tdata, err := ioutil.ReadAll(res.Body)\n\tCheckError(err)\n\tfmt.Printf(\"Got: %q\", string(data))\n}\n\nfunc CheckError(err error) {\n\tif err != nil {\n\t\tlog.Fatalf(\"Get: %v\", err)\n\t}\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_15/server1.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"strings\"\n)\n\n// Map of the clients: contains: clientname - 1 (active) / 0 - (inactive)\nvar mapUsers map[string]int\n\nfunc main() {\n\tvar listener net.Listener\n\tvar error error\n\tvar conn net.Conn\n\tmapUsers = make(map[string]int)\n\n\tfmt.Println(\"Starting the server ...\")\n\n\t// create listener:\n\tlistener, error = net.Listen(\"tcp\", \"localhost:50000\")\n\tcheckError(error)\n\t// listen and accept connections from clients:\n\tfor {\n\t\tconn, error = listener.Accept()\n\t\tcheckError(error)\n\t\tgo doServerStuff(conn)\n\t}\n}\n\nfunc doServerStuff(conn net.Conn) {\n\tvar buf []byte\n\tvar error error\n\n\tfor {\n\t\tbuf = make([]byte, 512)\n\t\t_, error = conn.Read(buf)\n\t\tcheckError(error)\n\t\tinput := string(buf)\n\t\tif strings.Contains(input, \": SH\") {\n\t\t\tfmt.Println(\"Server shutting down.\")\n\t\t\tos.Exit(0)\n\t\t}\n\t\t// op commando WHO:  write out mapUsers\n\t\tif strings.Contains(input, \": WHO\") {\n\t\t\tDisplayList()\n\t\t}\n\t\t// extract clientname:\n\t\tix := strings.Index(input, \"says\")\n\t\tclName := input[0 : ix-1]\n\t\t//fmt.Printf(\"The clientname  is ---%s---\\n\", string(clName))\n\t\t// set clientname active in mapUsers:\n\t\tmapUsers[string(clName)] = 1\n\t\tfmt.Printf(\"Received data: --%v--\", string(buf))\n\t}\n}\n\n// advantage: code is cleaner,\n// disadvantage:  the server process has to stop at any error:\n//                a simple return continues in the function where we came from!\nfunc checkError(error error) {\n\tif error != nil {\n\t\tpanic(\"Error: \" + error.Error()) // terminate program\n\t}\n}\n\nfunc DisplayList() {\n\tfmt.Println(\"--------------------------------------------\")\n\tfmt.Println(\"This is the client list: 1=active, 0=inactive\")\n\tfor key, value := range mapUsers {\n\t\tfmt.Printf(\"User %s is %d\\n\", key, value)\n\t}\n\tfmt.Println(\"--------------------------------------------\")\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_15/statistics.go",
    "content": "// statistics.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n)\n\ntype statistics struct {\n\tnumbers []float64\n\tmean    float64\n\tmedian  float64\n}\n\nconst form = `<html><body><form action=\"/\" method=\"POST\">\n<label for=\"numbers\">Numbers (comma or space-separated):</label><br>\n<input type=\"text\" name=\"numbers\" size=\"30\"><br />\n<input type=\"submit\" value=\"Calculate\">\n</form></html></body>`\n\nconst error = `<p class=\"error\">%s</p>`\n\nvar pageTop = \"\"\nvar pageBottom = \"\"\n\nfunc main() {\n\thttp.HandleFunc(\"/\", homePage)\n\tif err := http.ListenAndServe(\":9001\", nil); err != nil {\n\t\tlog.Fatal(\"failed to start server\", err)\n\t}\n}\n\nfunc homePage(writer http.ResponseWriter, request *http.Request) {\n\twriter.Header().Set(\"Content-Type\", \"text/html\")\n\terr := request.ParseForm() // Must be called before writing response\n\tfmt.Fprint(writer, pageTop, form)\n\tif err != nil {\n\t\tfmt.Fprintf(writer, error, err)\n\t} else {\n\t\tif numbers, message, ok := processRequest(request); ok {\n\t\t\tstats := getStats(numbers)\n\t\t\tfmt.Fprint(writer, formatStats(stats))\n\t\t} else if message != \"\" {\n\t\t\tfmt.Fprintf(writer, error, message)\n\t\t}\n\t}\n\tfmt.Fprint(writer, pageBottom)\n}\n\nfunc processRequest(request *http.Request) ([]float64, string, bool) {\n\tvar numbers []float64\n\tvar text string\n\tif slice, found := request.Form[\"numbers\"]; found && len(slice) > 0 {\n\t\t//处理如果网页中输入的是中文逗号\n\t\tif strings.Contains(slice[0], \"&#65292\") {\n\t\t\ttext = strings.Replace(slice[0], \"&#65292;\", \" \", -1)\n\t\t} else {\n\t\t\ttext = strings.Replace(slice[0], \",\", \" \", -1)\n\t\t}\n\t\tfor _, field := range strings.Fields(text) {\n\t\t\tif x, err := strconv.ParseFloat(field, 64); err != nil {\n\t\t\t\treturn numbers, \"'\" + field + \"' is invalid\", false\n\t\t\t} else {\n\t\t\t\tnumbers = append(numbers, x)\n\t\t\t}\n\t\t}\n\t}\n\tif len(numbers) == 0 {\n\t\treturn numbers, \"\", false // no data first time form is shown\n\t}\n\treturn numbers, \"\", true\n}\n\nfunc getStats(numbers []float64) (stats statistics) {\n\tstats.numbers = numbers\n\tsort.Float64s(stats.numbers)\n\tstats.mean = sum(numbers) / float64(len(numbers))\n\tstats.median = median(numbers)\n\treturn\n}\n\nfunc sum(numbers []float64) (total float64) {\n\tfor _, x := range numbers {\n\t\ttotal += x\n\t}\n\treturn\n}\n\nfunc median(numbers []float64) float64 {\n\tmiddle := len(numbers) / 2\n\tresult := numbers[middle]\n\tif len(numbers)%2 == 0 {\n\t\tresult = (result + numbers[middle-1]) / 2\n\t}\n\treturn result\n}\n\nfunc formatStats(stats statistics) string {\n\treturn fmt.Sprintf(`<table border=\"1\">\n<tr><th colspan=\"2\">Results</th></tr>\n<tr><td>Numbers</td><td>%v</td></tr>\n<tr><td>Count</td><td>%d</td></tr>\n<tr><td>Mean</td><td>%f</td></tr>\n<tr><td>Median</td><td>%f</td></tr>\n</table>`, stats.numbers, len(stats.numbers), stats.mean, stats.median)\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_15/template_validation_recover.go",
    "content": "// template_validation_recover.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"text/template\"\n)\n\nfunc main() {\n\ttOk := template.New(\"ok\")\n\ttErr := template.New(\"error_template\")\n\tdefer func() {\n\t\tif err := recover(); err != nil {\n\t\t\tlog.Printf(\"run time panic: %v\", err)\n\t\t}\n\t}()\n\n\t//a valid template, so no panic with Must:\n\ttemplate.Must(tOk.Parse(\"/* and a comment */ some static text: {{ .Name }}\"))\n\tfmt.Println(\"The first one parsed OK.\")\n\tfmt.Println(\"The next one ought to fail.\")\n\ttemplate.Must(tErr.Parse(\" some static text {{ .Name }\"))\n}\n\n/* Output:\nThe first one parsed OK.\nThe next one ought to fail.\n2011/10/27 10:56:27 run time panic: template: error_template:1: unexpected \"}\" in command\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_15/twitter_status_json.go",
    "content": "// twitter_status_json.go\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net/http\"\n)\n\ntype Status struct {\n\tText string\n}\n\ntype User struct {\n\tStatus Status\n}\n\nfunc main() {\n\t/* perform an HTTP request for the twitter status of user: Googland */\n\tres, _ := http.Get(\"http://twitter.com/users/Googland.json\")/*http://twitter.com/users/Googland.json页面不存在了*/\n\t/* initialize the structure of the JSON response */\n\tuser := User{Status{\"\"}}\n\t/* unmarshal the JSON into our structures */\n\ttemp, _ := ioutil.ReadAll(res.Body)\n\tbody := []byte(temp)\n\tjson.Unmarshal(body, &user)\n\tfmt.Printf(\"status: %s\", user.Status.Text)\n}\n\n/* Output:\nstatus: Robot cars invade California, on orders from Google:\nGoogle has been testing self-driving cars ... http://bit.ly/cbtpUN http://retwt.me/97p\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_15/webhello2.go",
    "content": "// webhello2.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc helloHandler(w http.ResponseWriter, r *http.Request) {\n\tremPartOfURL := r.URL.Path[len(\"/hello/\"):] //get everything after the /hello/ part of the URL\n\tfmt.Fprintf(w, \"Hello %s!\", remPartOfURL)\n}\n\nfunc shouthelloHandler(w http.ResponseWriter, r *http.Request) {\n\tremPartOfURL := r.URL.Path[len(\"/shouthello/\"):] //get everything after the /shouthello/ part of the URL\n\tfmt.Fprintf(w, \"Hello %s!\", strings.ToUpper(remPartOfURL))\n}\n\nfunc main() {\n\thttp.HandleFunc(\"/hello/\", helloHandler)\n\thttp.HandleFunc(\"/shouthello/\", shouthelloHandler)\n\thttp.ListenAndServe(\"localhost:9999\", nil)\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_4/count_characters.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"unicode/utf8\"\n)\n\nfunc main() {\n\t// count number of characters:\n\tstr1 := \"asSASA ddd dsjkdsjs dk\"\n\tfmt.Printf(\"The number of bytes in string str1 is %d\\n\", len(str1))\n\tfmt.Printf(\"The number of characters in string str1 is %d\\n\", utf8.RuneCountInString(str1))\n\tstr2 := \"asSASA ddd dsjkdsjsこん dk\"\n\tfmt.Printf(\"The number of bytes in string str2 is %d\\n\", len(str2))\n\tfmt.Printf(\"The number of characters in string str2 is %d\", utf8.RuneCountInString(str2))\n}\n\n/* Output:\nThe number of bytes in string str1 is 22\nThe number of characters in string str1 is 22\nThe number of bytes in string str2 is 28\nThe number of characters in string str2 is 24\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_4/divby0.go",
    "content": "package main\n\nfunc main() {\n\ta, b := 10, 0\n\tc := a / b // panic: runtime error: integer divide by zero\n\n\tprint(c)\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_4/function_calls_function.go",
    "content": "package main\n\nvar a string // global scope\n\nfunc main() {\n\ta = \"G\"\n\tprint(a)\n\tf1()\n}\nfunc f1() {\n\ta := \"O\" // new local variable a, only scoped within f1() !\n\tprint(a)\n\tf2()\n}\nfunc f2() {\n\tprint(a) // global variable is taken\n}\n\n// GOG\n"
  },
  {
    "path": "eBook/exercises/chapter_4/global_scope.go",
    "content": "package main\n\nvar a = \"G\" // global scope\n\nfunc main() {\n\tn()\n\tm()\n\tn()\n}\n\nfunc n() {\n\tprint(a)\n}\n\nfunc m() {\n\ta = \"O\" // simple assignment: global a gets a new value\n\tprint(a)\n}\n\n// GOO\n"
  },
  {
    "path": "eBook/exercises/chapter_4/local_scope.go",
    "content": "package main\n\nvar a = \"G\" // global (package) scope\n\nfunc main() {\n\tn()\n\tm()\n\tn()\n}\nfunc n() {\n\tprint(a)\n}\nfunc m() {\n\ta := \"O\" // new local variable a is declared\n\tprint(a)\n}\n\n// GOG\n"
  },
  {
    "path": "eBook/exercises/chapter_5/bitwise_complement.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfor i := 0; i <= 10; i++ {\n\t\tfmt.Printf(\"the complement of %b is: %b\\n\", i, ^i)\n\t}\n}\n\n/* Output:\nthe complement of 0 is: -1\nthe complement of 1 is: -10\nthe complement of 10 is: -11\nthe complement of 11 is: -100\nthe complement of 100 is: -101\nthe complement of 101 is: -110\nthe complement of 110 is: -111\nthe complement of 111 is: -1000\nthe complement of 1000 is: -1001\nthe complement of 1001 is: -1010\nthe complement of 1010 is: -1011\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_5/fizzbuzz.go",
    "content": "package main\n\nimport \"fmt\"\n\nconst (\n\tFIZZ     = 3\n\tBUZZ     = 5\n\tFIZZBUZZ = 15\n)\n\nfunc main() {\n\tfor i := 0; i <= 100; i++ {\n\t\tswitch {\n\t\tcase i%FIZZBUZZ == 0:\n\t\t\tfmt.Println(\"FizzBuzz\")\n\t\tcase i%FIZZ == 0:\n\t\t\tfmt.Println(\"Fizz\")\n\t\tcase i%BUZZ == 0:\n\t\t\tfmt.Println(\"Buzz\")\n\t\tdefault:\n\t\t\tfmt.Println(i)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_5/for_character.go",
    "content": "package main\n\nfunc main() {\n\t// 1 - use 2 nested for loops\n\tfor i := 1; i <= 25; i++ {\n\t\tfor j := 1; j <= i; j++ {\n\t\t\tprint(\"G\")\n\t\t}\n\t\tprintln()\n\t}\n\t// 2 -  use only one for loop and string concatenation\n\tstr := \"G\"\n\tfor i := 1; i <= 25; i++ {\n\t\tprintln(str)\n\t\tstr += \"G\"\n\t}\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_5/for_loop.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\t// 1:\n\tfor i := 0; i < 15; i++ {\n\t\tfmt.Printf(\"The counter is at %d\\n\", i)\n\t}\n\t// 2:\n\ti := 0\nSTART:\n\tfmt.Printf(\"The counter is at %d\\n\", i)\n\ti++\n\tif i < 15 {\n\t\tgoto START\n\t}\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_5/i_undefined.go",
    "content": "// i_undefined.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tvar i int\n\tfor i = 0; i < 10; i++ {\n\t\tfmt.Printf(\"%v\\n\", i)\n\t}\n\tfmt.Printf(\"%v\\n\", i) //<-- compile error:  undefined i\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_5/multiple_for.go",
    "content": "// multiple_for.go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\t//multiple initialization; a consolidated bool expression with && and ||; multiple ‘incrementation’\n\tfor i, j, s := 0, 5, \"a\"; i < 3 && j < 100 && s != \"aaaaa\"; i, j, s = i+1,\n\t\tj+1, s+\"a\" {\n\t\tfmt.Println(\"Value of i, j, s:\", i, j, s)\n\t}\n}\n\n/* Output:\nValue of i, j, s: 0 5 a\nValue of i, j, s: 1 6 aa\nValue of i, j, s: 2 7 aaa\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_5/rectangle_stars.go",
    "content": "// rectangle_stars.go\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tw, h := 20, 10\n\tfor y := 0; y < h; y++ {\n\t\tfor x := 0; x < w; x++ {\n\t\t\tfmt.Print(\"*\")\n\t\t}\n\t\tfmt.Println()\n\t}\n}\n\n/* Output:\n********************\n********************\n********************\n********************\n********************\n********************\n********************\n********************\n********************\n********************\n */\n"
  },
  {
    "path": "eBook/exercises/chapter_5/season.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Printf(Season(3))\n}\n\nfunc Season(month int) string {\n\tswitch month {\n\tcase 12, 1, 2:\n\t\treturn \"Winter\"\n\tcase 3, 4, 5:\n\t\treturn \"Spring\"\n\tcase 6, 7, 8:\n\t\treturn \"Summer\"\n\tcase 9, 10, 11:\n\t\treturn \"Autumn\"\n\t}\n\treturn \"Season unknown\"\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_6/10to1_recursive.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tprintrec(1)\n}\n\nfunc printrec(i int) {\n\tif i > 10 {\n\t\treturn\n\t}\n\tprintrec(i + 1)\n\tfmt.Printf(\"%d \", i)\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_6/compose.go",
    "content": "// compose.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n)\n\nfunc Compose(f, g func(x float64) float64) func(x float64) float64 {\n\treturn func(x float64) float64 { // closure\n\t\treturn f(g(x))\n\t}\n}\n\nfunc main() {\n\tfmt.Print(Compose(math.Sin, math.Cos)(0.5)) // output: 0.7691963548410085\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_6/error_returnval.go",
    "content": "// error_returnval.go\npackage main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n)\n\nfunc main() {\n\tfmt.Print(\"First example with -1: \")\n\tret1, err1 := MySqrt(-1)\n\tif err1 != nil {\n\t\tfmt.Println(\"Error! Return values are: \", ret1, err1)\n\t} else {\n\t\tfmt.Println(\"It's ok! Return values are: \", ret1, err1)\n\t}\n\n\tfmt.Print(\"Second example with 5: \")\n\t//you could also write it like this\n\tif ret2, err2 := MySqrt(5); err2 != nil {\n\t\tfmt.Println(\"Error! Return values are: \", ret2, err2)\n\t} else {\n\t\tfmt.Println(\"It's ok! Return values are: \", ret2, err2)\n\t}\n\t// named return variables:\n\tfmt.Println(MySqrt2(5))\n}\n\nfunc MySqrt(f float64) (float64, error) {\n\t//return an error as second parameter if invalid input\n\tif f < 0 {\n\t\treturn float64(math.NaN()), errors.New(\"I won't be able to do a sqrt of negative number!\")\n\t}\n\t//otherwise use default square root function\n\treturn math.Sqrt(f), nil\n}\n\n//name the return variables - by default it will have 'zero-ed' values i.e. numbers are 0, string is empty, etc.\nfunc MySqrt2(f float64) (ret float64, err error) {\n\tif f < 0 {\n\t\t//then you can use those variables in code\n\t\tret = float64(math.NaN())\n\t\terr = errors.New(\"I won't be able to do a sqrt of negative number!\")\n\t} else {\n\t\tret = math.Sqrt(f)\n\t\t//err is not assigned, so it gets default value nil\n\t}\n\t//automatically return the named return variables ret and err\n\treturn\n}\n\n/* Output:\nFirst example with -1: Error! Return values are: NaN I won't be able to do a sqrt of negative number!\nSecond example with 5: It's ok! Return values are: 2.23606797749979 <nil>\n2.23606797749979 <nil>\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_6/factorial.go",
    "content": "// factorial.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tfor i := uint64(0); i < uint64(30); i++ {\n\t\tfmt.Printf(\"Factorial of %d is %d\\n\", i, Factorial(i))\n\t}\n}\n\n/* unnamed return variables:\nfunc Factorial(n uint64) uint64 {\n\tif n > 0 {\n\t\treturn n * Factorial(n-1)\n\t}\n\treturn 1\n}\n*/\n\n// named return variables:\nfunc Factorial(n uint64) (fac uint64) {\n\tfac = 1\n\tif n > 0 {\n\t\tfac = n * Factorial(n-1)\n\t\treturn\n\t}\n\treturn\n}\n\n// int: correct results till 12!\n// uint64: correct results till 21!\n"
  },
  {
    "path": "eBook/exercises/chapter_6/fibonacci2.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tpos := 4\n\tresult, pos := fibonacci(pos)\n\tfmt.Printf(\"the %d-th fibonacci number is: %d\\n\", pos, result)\n\tpos = 10\n\tresult, pos = fibonacci(pos)\n\tfmt.Printf(\"the %d-th fibonacci number is: %d\\n\", pos, result)\n}\n\nfunc fibonacci(n int) (val, pos int) {\n\tif n <= 1 {\n\t\tval = 1\n\t} else {\n\t\tv1, _ := fibonacci(n - 1)\n\t\tv2, _ := fibonacci(n - 2)\n\t\tval = v1 + v2\n\t}\n\tpos = n\n\treturn\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_6/fibonacci_closure.go",
    "content": "package main\n\n// fib returns a function that returns\n// successive Fibonacci numbers.\nfunc fib() func() int {\n\ta, b := 1, 1\n\treturn func() int {\n\t\ta, b = b, a+b\n\t\treturn b\n\t}\n}\n\nfunc main() {\n\tf := fib()\n\t// Function calls are evaluated left-to-right.\n\t// println(f(), f(), f(), f(), f())\n\tfor i := 0; i <= 9; i++ {\n\t\tprintln(i+2, f())\n\t}\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_6/function_filter2.go",
    "content": "// function_filter2.go\npackage main\n\nimport \"fmt\"\n\ntype flt func(int) bool\n\n// func isEven(n int) bool { if n%2 == 0 { return true }; return false }\nfunc isEven(n int) bool {\n\tif n%2 == 0 {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc filter(sl []int, f flt) (yes, no []int) {\n\tfor _, val := range sl {\n\t\tif f(val) {\n\t\t\tyes = append(yes, val)\n\t\t} else {\n\t\t\tno = append(no, val)\n\t\t}\n\t}\n\treturn\n}\n\nfunc main() {\n\tslice := []int{1, 2, 3, 4, 5, 7}\n\tfmt.Println(\"slice = \", slice)\n\teven, odd := filter(slice, isEven)\n\tfmt.Println(\"The even elements of slice are: \", even)\n\tfmt.Println(\"The odd elements of slice are: \", odd)\n}\n\n/*\n\n */\n"
  },
  {
    "path": "eBook/exercises/chapter_6/lambda_value.go",
    "content": "// lambda_value.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tfv := func() {\n\t\tfmt.Println(\"Hello World!\")\n\t}\n\tfv()\n\tfmt.Printf(\"The type of fv is %T\", fv)\n}\n\n/* Output:\nHello World!\nThe type of fv is func()\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_6/mult_returnval.go",
    "content": "// mult_returnval.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc SumProductDiff(i, j int) (int, int, int) {\n\treturn i + j, i * j, i - j\n}\n\nfunc SumProductDiffN(i, j int) (s int, p int, d int) {\n\ts, p, d = i+j, i*j, i-j\n\treturn\n}\n\nfunc main() {\n\tsum, prod, diff := SumProductDiff(3, 4)\n\tfmt.Println(\"Sum:\", sum, \"| Product:\", prod, \"| Diff:\", diff)\n\tsum, prod, diff = SumProductDiffN(3, 4)\n\tfmt.Println(\"Sum:\", sum, \"| Product:\", prod, \"| Diff:\", diff)\n}\n\n// Sum: 7 | Product: 12 | Diff: -1\n// Sum: 7 | Product: 12 | Diff: -1\n"
  },
  {
    "path": "eBook/exercises/chapter_6/strings_map.go",
    "content": "// strings_map.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc main() {\n\tasciiOnly := func(c rune) rune {\n\t\tif c > 127 {\n\t\t\treturn ' '\n\t\t}\n\t\treturn c\n\t}\n\tfmt.Println(strings.Map(asciiOnly, \"Jérôme Österreich\"))\n}\n\n// J r me  sterreich\n"
  },
  {
    "path": "eBook/exercises/chapter_6/varargs.go",
    "content": "// Q10_varargs.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tprintInts()\n\tprintln()\n\tprintInts(2, 3)\n\tprintln()\n\tprintInts(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)\n}\n\nfunc printInts(list ...int) {\n\t//\tfor i:=0; i<len(list); i++ {\n\t//\t\tfmt.Printf(\"%d\\n\", list[i])\n\t//\t}\n\tfor _, v := range list {\n\t\tfmt.Printf(\"%d\\n\", v)\n\t}\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_7/array_value.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar arr1 [5]int\n\n\tfor i := 0; i < len(arr1); i++ {\n\t\tarr1[i] = i * 2\n\t}\n\n\tarr2 := arr1\n\tarr2[2] = 100\n\n\tfor i := 0; i < len(arr1); i++ {\n\t\tfmt.Printf(\"Array arr1 at index %d is %d\\n\", i, arr1[i])\n\t}\n\tfmt.Println()\n\tfor i := 0; i < len(arr2); i++ {\n\t\tfmt.Printf(\"Array arr2 at index %d is %d\\n\", i, arr2[i])\n\t}\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_7/bubblesort.go",
    "content": "// Q14_Bubblesort.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tsla := []int{2, 6, 4, -10, 8, 89, 12, 68, -45, 37}\n\tfmt.Println(\"before sort: \", sla)\n\t// sla is passed via call by value, but since sla is a reference type\n\t// the underlying slice is array is changed (sorted)\n\tbubbleSort(sla)\n\tfmt.Println(\"after sort:  \", sla)\n}\n\nfunc bubbleSort(sl []int) {\n\t// passes through the slice:\n\tfor pass := 1; pass < len(sl); pass++ {\n\t\t// one pass:\n\t\tfor i := 0; i < len(sl)-pass; i++ { // the bigger value 'bubbles up' to the last position\n\t\t\tif sl[i] > sl[i+1] {\n\t\t\t\tsl[i], sl[i+1] = sl[i+1], sl[i]\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_7/fibonacci_array.go",
    "content": "package main\n\nimport \"fmt\"\n\nvar fibs [50]int64\n\nfunc main() {\n\tfibs[0] = 1\n\tfibs[1] = 1\n\n\tfor i := 2; i < 50; i++ {\n\t\tfibs[i] = fibs[i-1] + fibs[i-2]\n\t}\n\n\tfor i := 0; i < 50; i++ {\n\t\tfmt.Printf(\"The %d-th Fibonacci number is: %d\\n\", i, fibs[i])\n\t}\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_7/fibonacci_funcarray.go",
    "content": "package main\n\nimport \"fmt\"\n\nvar term = 15\n\nfunc main() {\n\tresult := fibarray(term)\n\tfor ix, fib := range result {\n\t\tfmt.Printf(\"The %d-th Fibonacci number is: %d\\n\", ix, fib)\n\t}\n}\n\nfunc fibarray(term int) []int {\n\tfarr := make([]int, term)\n\tfarr[0], farr[1] = 1, 1\n\n\tfor i := 2; i < term; i++ {\n\t\tfarr[i] = farr[i-1] + farr[i-2]\n\t}\n\treturn farr\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_7/filter_slice.go",
    "content": "// filter_slice.go\r\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\ts := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}\n\ts = Filter(s, even)\n\tfmt.Println(s)\n}\n\n// Filter returns a new slice holding only\r\n// the elements of s that satisfy f()\r\nfunc Filter(s []int, fn func(int) bool) []int {\n\tvar p []int // == nil\r\n\tfor _, i := range s {\n\t\tif fn(i) {\n\t\t\tp = append(p, i)\n\t\t}\n\t}\n\treturn p\n}\n\nfunc even(n int) bool {\n\tif n%2 == 0 {\n\t\treturn true\n\t}\n\treturn false\n}\n/* [0 2 4 6 8] */\n"
  },
  {
    "path": "eBook/exercises/chapter_7/for_array.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tvar arr [15]int\n\tfor i := 0; i < 15; i++ {\n\t\tarr[i] = i\n\t}\n\tfmt.Println(arr) // [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14]\n\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_7/insert_slice.go",
    "content": "// insert_slice.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\ts := []string{\"M\", \"N\", \"O\", \"P\", \"Q\", \"R\"}\n\tin := []string{\"A\", \"B\", \"C\"}\n\tres := InsertStringSlice(s, in, 0) // at the front\n\tfmt.Println(res)                   // [A B C M N O P Q R]\n\tres = InsertStringSlice(s, in, 3)  // [M N O A B C P Q R]\n\tfmt.Println(res)\n}\n\nfunc InsertStringSlice(slice, insertion []string, index int) []string {\n\tresult := make([]string, len(slice)+len(insertion))\n\tat := copy(result, slice[:index])\n\tat += copy(result[at:], insertion)\n\tcopy(result[at:], slice[index:])\n\treturn result\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_7/magnify_slice.go",
    "content": "package main\n\nimport \"fmt\"\n\nvar s []int\n\nfunc main() {\n\ts = []int{1, 2, 3}\n\tfmt.Println(\"The length of s before enlarging is:\", len(s))\n\tfmt.Println(s)\n\ts = enlarge(s, 5)\n\tfmt.Println(\"The length of s after enlarging is:\", len(s))\n\tfmt.Println(s)\n}\n\nfunc enlarge(s []int, factor int) []int {\n\tns := make([]int, len(s)*factor)\n\t// fmt.Println(\"The length of ns  is:\", len(ns))\n\tcopy(ns, s)\n\t//fmt.Println(ns)\n\ts = ns\n\t//fmt.Println(s)\n\t//fmt.Println(\"The length of s after enlarging is:\", len(s))\n\treturn s\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_7/map_function.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tlist := []int{0, 1, 2, 3, 4, 5, 6, 7}\n\tmf := func(i int) int {\n\t\treturn i * 10\n\t}\n\t/*\n\t\tresult := mapFunc(mf, list)\n\t\tfor _, v := range result {\n\t\t\tfmt.Println(v)\n\t\t}\n\t*/\n\tprintln()\n\t// shorter:\n\tfmt.Printf(\"%v\", mapFunc(mf, list))\n}\n\nfunc mapFunc(mf func(int) int, list []int) []int {\n\tresult := make([]int, len(list))\n\tfor ix, v := range list {\n\t\tresult[ix] = mf(v)\n\t}\n\t/*\n\t\tfor ix := 0; ix<len(list); ix++ {\n\t\t\tresult[ix] = mf(list[ix])\n\t\t}\n\t*/\n\treturn result\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_7/min_max.go",
    "content": "// Q13_1_max.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n)\n\nfunc main() {\n\tsl1 := []int{78, 34, 643, 12, 90, 492, 13, 2}\n\tmax := maxSlice(sl1)\n\tfmt.Printf(\"The maximum is %d\\n\", max)\n\tmin := minSlice(sl1)\n\tfmt.Printf(\"The minimum is %d\\n\", min)\n}\n\nfunc maxSlice(sl []int) (max int) {\n\tfor _, v := range sl {\n\t\tif v > max {\n\t\t\tmax = v\n\t\t}\n\t}\n\treturn\n}\n\nfunc minSlice(sl []int) (min int) {\n\t// min = int(^uint(0) >> 1)\n\tmin = math.MaxInt32\n\tfor _, v := range sl {\n\t\tif v < min {\n\t\t\tmin = v\n\t\t}\n\t}\n\treturn\n}\n\n/* Output:\nThe maximum is 643\nThe minimum is 2\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_7/remove_slice.go",
    "content": "// remove_slice.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\ts := []string{\"M\", \"N\", \"O\", \"P\", \"Q\", \"R\"}\n\tres := RemoveStringSlice(s, 2, 4)\n\tfmt.Println(res) // [M N Q R]\n}\n\nfunc RemoveStringSlice(slice []string, start, end int) []string {\n\tresult := make([]string, len(slice)-(end-start))\n\tat := copy(result, slice[:start])\n\tcopy(result[at:], slice[end:])\n\treturn result\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_7/split_string.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tstr := \"Google\"\n\tfor i := 0; i <= len(str); i++ {\n\t\ta, b := Split(str, i)\n\t\tfmt.Printf(\"The string %s split at position %d is: %s / %s\\n\", str, i, a, b)\n\t}\n\n}\n\nfunc Split(s string, pos int) (string, string) {\n\treturn s[0:pos], s[pos:]\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_7/string_reverse.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc reverse(s string) string {\n\trunes := []rune(s)\n\tn, h := len(runes), len(runes)/2\n\tfor i := 0; i < h; i++ {\n\t\trunes[i], runes[n-1-i] = runes[n-1-i], runes[i]\n\t}\n\treturn string(runes)\n}\n\nfunc main() {\n\t// reverse a string:\n\tstr := \"Google\"\n\tsl := []byte(str)\n\tvar rev [100]byte\n\tj := 0\n\tfor i := len(sl) - 1; i >= 0; i-- {\n\t\trev[j] = sl[i]\n\t\tj++\n\t}\n\tstr_rev := string(rev[:])\n\tfmt.Printf(\"The reversed string is -%s-\\n\", str_rev)\n\t// variant: \"in place\" using swapping\n\tstr2 := \"Google\"\n\tsl2 := []byte(str2)\n\tfor i, j := 0, len(sl2)-1; i < j; i, j = i+1, j-1 {\n\t\tsl2[i], sl2[j] = sl2[j], sl2[i]\n\t}\n\tfmt.Printf(\"The reversed string is -%s-\\n\", string(sl2))\n\t// variant: using [] int for runes (necessary for Unicode-strings!):\n\ts := \"My Test String!\"\n\tfmt.Println(s, \" --> \", reverse(s))\n}\n\n/* Output:\nThe reversed string is -elgooG-\nThe reversed string is -elgooG-\nMy Test String!  -->  !gnirtS tseT yM\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_7/string_split2.go",
    "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tstr := \"Google\"\n\tstr2 := Split2(str)\n\tfmt.Printf(\"The string %s transformed is: %s\\n\", str, str2)\n}\n\nfunc Split2(s string) string {\n\tmid := len(s) / 2\n\treturn s[mid:] + s[:mid]\n}\n\n// Output: The string Google transformed is: gleGoo\n"
  },
  {
    "path": "eBook/exercises/chapter_7/sum_array.go",
    "content": "// leaving out the length 4 in [4] uses  slice instead of an array\n// which is generally better for performance\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\t// var a = [4]float32 {1.0,2.0,3.0,4.0}\n\tvar a = []float32{1.0, 2.0, 3.0, 4.0}\n\tfmt.Printf(\"The sum of the array is: %f\\n\", Sum(a))\n\tvar b = []int{1, 2, 3, 4, 5}\n\tsum, average := SumAndAverage(b)\n\tfmt.Printf(\"The sum of the array is: %d, and the average is: %f\", sum, average)\n}\n\n/*\nfunc Sum(a [4]float32) (sum float32) {\n\tfor _, item := range a {\n\t\tsum += item\n\t}\n\treturn\n}\n*/\n\nfunc Sum(a []float32) (sum float32) {\n\tfor _, item := range a {\n\t\tsum += item\n\t}\n\treturn\n}\n\nfunc SumAndAverage(a []int) (int, float32) {\n\tsum := 0\n\tfor _, item := range a {\n\t\tsum += item\n\t}\n\treturn sum, float32(sum / len(a))\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_7/uniq.go",
    "content": "// Q29_uniq.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nvar arr []byte = []byte{'a', 'b', 'a', 'a', 'a', 'c', 'd', 'e', 'f', 'g'}\n\nfunc main() {\n\tarru := make([]byte, len(arr)) // this will contain the unique items\n\tixu := 0                       // index in arru\n\ttmp := byte(0)\n\tfor _, val := range arr {\n\t\tif val != tmp {\n\t\t\tarru[ixu] = val\n\t\t\tfmt.Printf(\"%c \", arru[ixu])\n\t\t\tixu++\n\t\t}\n\t\ttmp = val\n\t}\n\t// fmt.Println(arru)\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_8/map_days.go",
    "content": "// map_days.go\npackage main\n\nimport (\n\t\"fmt\"\n)\n\nvar Days = map[int]string{1: \"monday\",\n\t2: \"tuesday\",\n\t3: \"wednesday\",\n\t4: \"thursday\",\n\t5: \"friday\",\n\t6: \"saturday\",\n\t7: \"sunday\"}\n\nfunc main() {\n\tfmt.Println(Days)\n\t// fmt.Printf(\"%v\", Days)\n\tflagHolliday := false\n\tfor k, v := range Days {\n\t\tif v == \"thursday\" || v == \"holliday\" {\n\t\t\tfmt.Println(v, \" is the \", k, \"th day in the week\")\n\t\t\tif v == \"holliday\" {\n\t\t\t\tflagHolliday = true\n\t\t\t}\n\t\t}\n\t}\n\tif !flagHolliday {\n\t\tfmt.Println(\"holliday is not a day!\")\n\t}\n}\n\n/* Output:\nthursday  is the  4 th day in the week\nholliday is not a day!\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_8/map_drinks.go",
    "content": "// map_drinks.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n)\n\nfunc main() {\n\tdrinks := map[string]string{\n\t\t\"beer\":   \"bière\",\n\t\t\"wine\":   \"vin\",\n\t\t\"water\":  \"eau\",\n\t\t\"coffee\": \"café\",\n\t\t\"thea\":   \"thé\"}\n\tsdrinks := make([]string, len(drinks))\n\tix := 0\n\n\tfmt.Printf(\"The following drinks are available:\\n\")\n\tfor eng := range drinks {\n\t\tsdrinks[ix] = eng\n\t\tix++\n\t\tfmt.Println(eng)\n\t}\n\n\tfmt.Println(\"\")\n\tfor eng, fr := range drinks {\n\t\tfmt.Printf(\"The french for %s is %s\\n\", eng, fr)\n\t}\n\n\t// SORTING:\n\tfmt.Println(\"\")\n\tfmt.Println(\"Now the sorted output:\")\n\tsort.Strings(sdrinks)\n\n\tfmt.Printf(\"The following sorted drinks are available:\\n\")\n\tfor _, eng := range sdrinks {\n\t\tfmt.Println(eng)\n\t}\n\n\tfmt.Println(\"\")\n\tfor _, eng := range sdrinks {\n\t\tfmt.Printf(\"The french for %s is %s\\n\", eng, drinks[eng])\n\t}\n}\n\n/* Output:\nThe following drinks are available:\nwine\nbeer\nwater\ncoffee\nthea\n\nThe french for wine is vin\nThe french for beer is bière\nThe french for water is eau\nThe french for coffee is café\nThe french for thea is thé\n\nNow the sorted output:\nThe following sorted drinks are available:\nbeer\ncoffee\nthea\nwater\nwine\n\nThe french for beer is bière\nThe french for coffee is café\nThe french for thea is thé\nThe french for water is eau\nThe french for wine is vin\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_9/dlinked_list.go",
    "content": "// Q20_linked_list.go\npackage main\n\nimport (\n\t\"container/list\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tlst := list.New()\n\tlst.PushBack(100)\n\tlst.PushBack(101)\n\tlst.PushBack(102)\n\t// fmt.Println(\"Here is the double linked list:\\n\", lst)\n\tfor e := lst.Front(); e != nil; e = e.Next() {\n\t\t// fmt.Println(e)\n\t\tfmt.Println(e.Value)\n\t}\n}\n\n/* Example output:\n&{0x12542bc0 <nil> 0x12547590 1}\n&{0x12542ba0 0x12542be0 0x12547590 2}\n&{<nil> 0x12542bc0 0x12547590 4}\n\n100\n101\n102\n*/\n"
  },
  {
    "path": "eBook/exercises/chapter_9/even/even.go",
    "content": "// even.go\npackage even\n\nfunc Even(i int) bool { // exported function\n\treturn i%2 == 0\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_9/fibo/fibonacci.go",
    "content": "package fibo\n\n/*\nfunc Fibonacci(n int) (res int) {\n\tif n <= 1 {\n\t\tres = 1\n\t} else {\n\t\tres = Fibonacci(n-1) + Fibonacci(n-2)\n\t}\n\treturn\n}\n*/\n// accepts a general operation op:\nfunc Fibonacci(op string, n int) (res int) {\n\tif n <= 1 {\n\t\tswitch op {\n\t\tcase \"+\":\n\t\t\tres = 1\n\t\tcase \"*\":\n\t\t\tres = 2\n\t\tdefault:\n\t\t\tres = 0\n\t\t}\n\t} else {\n\t\tswitch op {\n\t\tcase \"+\":\n\t\t\tres = Fibonacci(op, n-1) + Fibonacci(op, n-2)\n\t\tcase \"*\":\n\t\t\tres = Fibonacci(op, n-1) * Fibonacci(op, n-2)\n\t\tdefault:\n\t\t\tres = 0\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_9/greetings/greetings.go",
    "content": "package greetings\n\nimport \"time\"\n\nfunc GoodDay(name string) string {\n\treturn \"Good Day \" + name\n}\n\nfunc GoodNight(name string) string {\n\treturn \"Good Night \" + name\n}\n\nfunc IsAM() bool {\n\tlocalTime := time.Now()\n\treturn localTime.Hour() <= 12\n}\n\nfunc IsAfternoon() bool {\n\tlocalTime := time.Now()\n\treturn localTime.Hour() <= 18\n}\n\nfunc IsEvening() bool {\n\tlocalTime := time.Now()\n\treturn localTime.Hour() <= 22\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_9/main_fibo.go",
    "content": "package main\n\nimport (\n\t\"./fibo/fibo\"\n\t\"fmt\"\n)\n\nvar nextFibo int\nvar op string\n\nfunc main() {\n\t/*\n\t\tresult := 0\n\t\tfor i:=0; i <= 10; i++ {\n\t\t\tresult = fibo.Fibonacci(i)\n\t\t\tfmt.Printf(\"fibonacci(%d) is: %d\\n\", i, result)\n\t\t}\n\t*/\n\top = \"+\"\n\tcalls()\n\tfmt.Println(\"Change of operation from + to *\")\n\tnextFibo = 0\n\top = \"*\"\n\tcalls()\n}\n\nfunc calls() {\n\tnext()\n\tfmt.Println(\"...\")\n\tnext()\n\tfmt.Println(\"...\")\n\tnext()\n\tfmt.Println(\"...\")\n\tnext()\n}\n\nfunc next() {\n\tresult := 0\n\tnextFibo++\n\tresult = fibo.Fibonacci(op, nextFibo)\n\tfmt.Printf(\"fibonacci(%d) is: %d\\n\", nextFibo, result)\n}\n\n/* *****************************************************************\nOutput is:\nfibonacci(1) is: 1\n...\nfibonacci(2) is: 2\n...\nfibonacci(3) is: 3\n...\nfibonacci(4) is: 5\nChange of operation from + to *\nfibonacci(1) is: 2\n...\nfibonacci(2) is: 4\n...\nfibonacci(3) is: 8\n...\nfibonacci(4) is: 32\n********************************************************************/\n"
  },
  {
    "path": "eBook/exercises/chapter_9/main_greetings.go",
    "content": "package main\n\nimport (\n\t\"./greetings\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tname := \"James\"\n\tfmt.Println(greetings.GoodDay(name))\n\tfmt.Println(greetings.GoodNight(name))\n\n\tif greetings.IsAM() {\n\t\tfmt.Println(\"Good morning\", name)\n\t} else if greetings.IsAfternoon() {\n\t\tfmt.Println(\"Good afternoon\", name)\n\t} else if greetings.IsEvening() {\n\t\tfmt.Println(\"Good evening\", name)\n\t} else {\n\t\tfmt.Println(\"Good night\", name)\n\t}\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_9/main_oddeven.go",
    "content": "// test_oddeven.go\npackage main\n\nimport (\n\t\"./even/even\"\n\t\"fmt\"\n)\n\nfunc main() {\n\tfor i := 0; i <= 100; i++ {\n\t\tfmt.Printf(\"Is the integer %d even? %v\\n\", i, even.Even(i))\n\t}\n}\n"
  },
  {
    "path": "eBook/exercises/chapter_9/size_int.go",
    "content": "// size_int.go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"unsafe\"\n)\n\nfunc main() {\n\tvar i int = 10\n\tsize := unsafe.Sizeof(i)\n\tfmt.Println(\"The size of an int is: \", size)\n}\n\n// The size of an int is:  4\n"
  },
  {
    "path": "eBook/preface.md",
    "content": "# 前言\r\n\r\n### 用更少的代码，更短的编译时间，创建运行更快的程序，享受更多的乐趣\t\r\n\r\n对于学习 Go 编程语言的爱好者来说，这本书无疑是最适合你的一本书籍，这里包含了当前最全面的学习资源。本书通过对官方的在线文档、名人博客、书籍、相关文章以及演讲的资料收集和整理，并结合我自身在软件工程、编程语言和数据库开发的授课经验，将这些零碎的知识点组织成系统化的概念和技术分类来进行讲解。\r\n\r\n随着软件规模的不断扩大，诸多的学者和谷歌的开发者们在公司内部的软件开发过程中开始经历大量的挫折，在诸多问题上都不能给出令人满意的解决方案，尤其是在使用 C++ 来开发大型的服务端软件时，情况更是不容乐观。由于二进制文件一般都是非常巨大的，因此需要耗费大量的时间在编译这些文件上，同时编程语言的设计思想也已经非常陈旧，这些情况都充分证明了现有的编程语言已不符合时下的生产环境。尽管硬件在过去的几十年中有了飞速的发展，但人们依旧没有找到机会去改变 C++ 在软件开发的重要地位，并在实际开发过程中忍受着它所带来的令人头疼的一些问题。因此学者们坐下来总结出了现在生产环境与软件开发之间的主要矛盾，并尝试设计一门全新的编程语言来解决这些问题。\r\n\r\n以下就是他们讨论得出的对编程语言的设计要求：\r\n\r\n- 能够以更快的速度开发软件\r\n- 开发出的软件能够很好地在现代的多核计算机上工作\r\n- 开发出的软件能够很好地在网络环境下工作\r\n- 使人们能够享受软件开发的过程\r\n\r\nGo 语言就在这样的环境下诞生了，它让人感觉像是 Python 或 Ruby 这样的动态语言，但却又拥有像 C 或者 Java 这类语言的高性能和安全性。\r\n\r\nGo 语言出现的目的是希望在编程领域创造最实用的方式来进行软件开发。它并不是要用奇怪的语法和晦涩难懂的概念来从根本上推翻已有的编程语言，而是建立并改善了 C、Java、C# 中的许多语法风格。它提倡通过接口来针对面向对象编程，通过 goroutine 和 channel 来支持并发和并行编程。\r\n\r\n这本书是为那些想要学习 Go 这门全新的，迷人的和充满希望的编程语言的开发者量身定做的。当然，你在学习 Go 语言之前需要具备一些关于编程的基础知识和经验，并且拥有合适的学习环境，但你并不需要对 C 或者 Java 或其它类似的语言有非常深入的了解。\r\n\r\n对于那些熟悉 C 或者面向对象编程语言的开发者，我们将会在本书中用 Go 和一些编程语言的相关概念进行比较（书中会使用大家所熟知的缩写 “OO” 来表示面向对象）。\r\n\r\n本书将会从最基础的概念讲起，同时也会讨论一些类似在应用 goroutine 和 channel 时有多少种不同的模式，如何在 Go 语言中使用谷歌 API，如何操作内存，如何在 Go 语言中进行程序测试和如何使用模板来开发 Web 应用这些高级概念和技巧。\r\n\r\n在本书的第一部分，我们将会讨论 Go 语言的起源（第 1 章），以及如何安装 Go 语言（第 2 章）和开发环境（第 3 章）。\r\n\r\n在本书的第二部分，我们将会带领你贯穿 Go 语言的核心思想，譬如简单与复杂类型（第 4、7、8 章），控制结构（第 5 章），函数（第 6 章），结构与方法（第 10 章）和接口（第 11 章）。我们会对 Go 语言的函数式和面向对象编程进行透彻的讲解，包括如何使用 Go 语言来构造大型项目（第 9 章）。\r\n\r\n在本书的第三部分，你将会学习到如何处理不同格式的文件（第 12 章）和如何在 Go 语言中巧妙地使用错误处理机制（第 13 章）。然后我们会对 Go 语言中最值得称赞的设计 goroutine 和 channel 进行并发和多核应用的基本技巧的讲解（第 14 章）。最后，我们会讨论如何将 Go 语言应用到分布式和 Web 应用中的相关网络技巧（第 15 章）。\r\n\r\n我们会在本书的第四部分向你展示许多 Go 语言的开发模式和一些编码规范，以及一些非常有用的代码片段（第 18 章）。在前面章节完成对所有的 Go 语言技巧的学习之后，你将会学习如何构造一个完整 Go 语言项目（第 19 章），然后我们会介绍一些关于 Go 语言在云（Google App Engine）方面的应用（第 20 章）。在本书的最后一章（第 21 章），我们会讨论一些在全世界范围内已经将 Go 语言投入实际开发的公司和组织。本书将会在最后给出一些对 Go 语言爱好者的引用，Go 相关包和工具的参考，以及章节练习的答案和所有参考资源和文献的清单。\r\n\r\nGo 语言有一个被称之为 “没有废物” 的宗旨，就是将一切没有必要的东西都去掉，不能去掉的就无底线地简化，同时追求最大程度的自动化。他完美地诠释了敏捷编程的 KISS 秘诀：短小精悍！\r\n\r\nGo 语言通过改善或去除在 C、C++ 或 Java 中的一些所谓“开放”特性来让开发者们的工作更加便利。这里只举例其中的几个，比如对于变量的默认初始化，内存分配与自动回收，以及更简洁却不失健壮的控制结构。同时我们也会发现 Go 语言旨在减少不必要的编码工作，这使得 Go 语言的代码更加简洁，从而比传统的面向对象语言更容易阅读和理解。\r\n\r\n与 C++ 或 Java 这些有着庞大体系的语言相比，Go 语言简洁到可以将它整个的装入你的大脑中，而且比学习 Scala（Java 的并发语言）有更低的门槛，真可谓是 21 世纪的 C 语言！\r\n\r\n作为一门系统编程语言，你不应该为 Go 语言的大多数代码示例和练习都和控制台有着密不可分的关系而感到惊奇，因为提供平台依赖性的 GUI（用户界面）框架并不是一个简单的任务。有许多由第三方发起的 GUI 框架项目正在如火如荼地进行中，或许我们会在不久的将来看到一些可用的 Go 语言 GUI 框架。不过现阶段的 Go 语言已经提供了大量有关 Web 方面的功能，我们可以通过它强大的 http 和 template 包来达到 Web 应用的 GUI 实现。\r\n\r\n我们会经常涉及到一些关于 Go 语言的编码规范，了解和使用这些已经被广泛认同的规范应该是你学习阶段最重要的实践。我会在书中尽量使用已经讲解的概念或者技巧来解释相关的代码示例，以避免你在不了解某些高级概念的情况下而感到迷茫。\r\n\r\n我们通过 227 个完整的代码示例和书中的解释说明来对所有涉及到的概念和技巧进行彻底的讲解，你可以下载这些代码到你的电脑上运行，从而加深对概念的理解。\r\n\r\n本书会尽可能地将前后章节的内容联系起来，当然这也同时要求你通过学习不同的知识来对一个问题提出尽可能多的解决方案。记住，学习一门新语言的最佳方式就是实践，运行它的代码，修改并尝试更多的方案。因此，你绝对不可以忽略书中的 130 个代码练习，这将对你学习好 Go 语言有很大的帮助。比如，我们就为斐波那契算法提供了 13 个不同的版本，而这些版本都使用了不同的概念和技巧。\r\n\r\n你可以通过访问本书的 [官方网站](https://sites.google.com/site/thewaytogo2012/) 下载书中的代码（**译者注：所有代码文件已经包括在 GitHub 仓库中**），并获得有关本书的勘误情况和内容更新。\r\n\r\n为了让你在成为 Go 语言大师的道路上更加顺利，我们会专注于一些特别的章节以提供 Go 语言开发模式的最佳实践，同时也会帮助初学者逃离一些语言的陷阱。第 18 章可以作为你在开发时的一个参考手册，因为当中包含了众多的有价值的代码片段以及相关的解释说明。\r\n\r\n最后要说明的是，你可以通过完整的索引来快速定位你需要阅读的章节。书中所有的代码都在 Go1.4 版本下测试通过。\r\n\r\n这里有一段来自在 C++、Java 和 Python 领域众所周知的专家 Bruce Eckel 的评论：\r\n\r\n“作为一个有着 C/C++ 背景的开发者，我在使用 Go 语言时仿佛呼吸到了新鲜空气一般，令人心旷神怡。我认为使用 Go 语言进行系统编程开发比使用 C++ 有着更显著的优势，因为它在解决一些很难用 C++ 解决的问题的同时，让我的工作变得更加高效。我并不是说 C++ 的存在是一个错误，相反地，我认为这是历史发展的必然结果。当我深陷在 C 语言这门略微比汇编语言好一点的泥潭时，我坚信任何语言的构造都不可能支持大型项目的开发。像垃圾回收或并发语言支持这类东西，在当时都是极其荒谬的主意，根本没有人在乎。C++ 向大型项目开发迈出了重要的第一步，带领我们走进这个广袤无垠的世界。很庆幸 Stroustrup 做了让 C++ 兼容 C 语言以能够让其编译 C 程序这个正确的决定。我们当时需要 C++ 的出现。”\r\n\r\n“之后我们学到了更多。我们毫无疑问地接受了垃圾回收，异常处理和虚拟机这些当年人们认为只有疯子才会想的东西。C++ 的复杂程度（新版的 C++ 甚至更加复杂）极大的影响了软件开发的高效性，这使得它再也不再适合这个时代。人们不再像过往那样认同在 C++ 中兼容使用 C 语言的方法，认为这些工作只是在浪费时间，牺牲人们的努力。就在此时，Go 语言已经成功地解决了 C++ 中那些本打算解决却未能解决的关键问题。”\r\n\r\n我非常想要向发明这门精湛的语言的 Go 开发团队表示真挚的感谢，尤其是团队的领导者 Rob Pike、Russ Cox 和 Andrew Gerrand，他们阐述的例子和说明都非常的完美。同时，我还要感谢 Miek Gieben、Frank Muller、Ryanne Dolan 和 Satish V.J. 给予我巨大的帮助，还有那些 golang-nuts 邮件列表里的所有的成员。\r\n\r\n欢迎来到 Go 语言开发的奇妙世界！\r\n\r\n## 链接\r\n\r\n- [目录](directory.md)\r\n- 下一部分: [Go 语言的起源，发展与普及](01.1.md)\r\n"
  }
]