├── .gitignore ├── CONTRIBUTORS.md ├── GLOSSARY.md ├── LICENSE ├── Makefile ├── README.md ├── SUMMARY-github.md ├── SUMMARY.md ├── appendix ├── appendix-a-errata.md ├── appendix-b-author.md ├── appendix-c-cpoyright.md ├── appendix-d-translations.md ├── appendix-z-index-good.md ├── appendix-z-index.md └── appendix.md ├── book.json ├── ch0 ├── ch0-01.md ├── ch0-02.md ├── ch0-03.md ├── ch0-04.md └── ch0-05.md ├── ch1 ├── ch1-01.md ├── ch1-02.md ├── ch1-03.md ├── ch1-04.md ├── ch1-05.md ├── ch1-06.md ├── ch1-07.md ├── ch1-08.md └── ch1.md ├── ch10 ├── ch10-01.md ├── ch10-02.md ├── ch10-03.md ├── ch10-04.md ├── ch10-05.md ├── ch10-06.md ├── ch10-07-1.md ├── ch10-07-2.md ├── ch10-07-3.md ├── ch10-07-4.md ├── ch10-07-5.md ├── ch10-07-6.md ├── ch10-07.md └── ch10.md ├── ch11 ├── ch11-01.md ├── ch11-02-1.md ├── ch11-02-2.md ├── ch11-02-3.md ├── ch11-02-4.md ├── ch11-02-5.md ├── ch11-02-6.md ├── ch11-02.md ├── ch11-03.md ├── ch11-04.md ├── ch11-05.md ├── ch11-06.md └── ch11.md ├── ch12 ├── ch12-01.md ├── ch12-02.md ├── ch12-03.md ├── ch12-04.md ├── ch12-05.md ├── ch12-06.md ├── ch12-07.md ├── ch12-08.md ├── ch12-09.md └── ch12.md ├── ch13 ├── ch13-01.md ├── ch13-02.md ├── ch13-03.md ├── ch13-04.md ├── ch13-05.md └── ch13.md ├── ch2 ├── ch2-01.md ├── ch2-02.md ├── ch2-03-1.md ├── ch2-03-2.md ├── ch2-03-3.md ├── ch2-03-4.md ├── ch2-03.md ├── ch2-04-1.md ├── ch2-04-2.md ├── ch2-04.md ├── ch2-05.md ├── ch2-06-1.md ├── ch2-06-2.md ├── ch2-06.md ├── ch2-07.md └── ch2.md ├── ch3 ├── ch3-01.md ├── ch3-02.md ├── ch3-03.md ├── ch3-04.md ├── ch3-05-1.md ├── ch3-05-2.md ├── ch3-05-3.md ├── ch3-05-4.md ├── ch3-05-5.md ├── ch3-05.md ├── ch3-06-1.md ├── ch3-06-2.md ├── ch3-06.md └── ch3.md ├── ch4 ├── ch4-01.md ├── ch4-02-1.md ├── ch4-02-2.md ├── ch4-02.md ├── ch4-03.md ├── ch4-04-1.md ├── ch4-04-2.md ├── ch4-04-3.md ├── ch4-04.md ├── ch4-05.md ├── ch4-06.md └── ch4.md ├── ch5 ├── ch5-01.md ├── ch5-02.md ├── ch5-03.md ├── ch5-04-1.md ├── ch5-04-2.md ├── ch5-04.md ├── ch5-05.md ├── ch5-06-1.md ├── ch5-06.md ├── ch5-07.md ├── ch5-08.md ├── ch5-09.md ├── ch5-10.md └── ch5.md ├── ch6 ├── ch6-01.md ├── ch6-02-1.md ├── ch6-02.md ├── ch6-03.md ├── ch6-04.md ├── ch6-05.md ├── ch6-06.md └── ch6.md ├── ch7 ├── ch7-01.md ├── ch7-02.md ├── ch7-03.md ├── ch7-04.md ├── ch7-05-1.md ├── ch7-05.md ├── ch7-06.md ├── ch7-07.md ├── ch7-08.md ├── ch7-09.md ├── ch7-10.md ├── ch7-11.md ├── ch7-12.md ├── ch7-13.md ├── ch7-14.md ├── ch7-15.md └── ch7.md ├── ch8 ├── ch8-01.md ├── ch8-02.md ├── ch8-03.md ├── ch8-04-1.md ├── ch8-04-2.md ├── ch8-04-3.md ├── ch8-04-4.md ├── ch8-04.md ├── ch8-05.md ├── ch8-06.md ├── ch8-07.md ├── ch8-08.md ├── ch8-09.md ├── ch8-10.md └── ch8.md ├── ch9 ├── ch9-01.md ├── ch9-02.md ├── ch9-03.md ├── ch9-04.md ├── ch9-05.md ├── ch9-06.md ├── ch9-07.md ├── ch9-08-1.md ├── ch9-08-2.md ├── ch9-08-3.md ├── ch9-08-4.md ├── ch9-08.md └── ch9.md ├── cover.jpg ├── cover_bgd.png ├── cover_middle.jpg ├── cover_patch.png ├── cover_small.jpg ├── doc.go ├── docs ├── Alef │ ├── Alef.Language.Reference.Manual.pdf │ └── Alef.User's.Guide.pdf ├── CSP │ └── cspbook.pdf ├── Limbo │ ├── descent.pdf │ └── limbo.pdf ├── Squeak │ ├── Squeak.A.Language.for.Communicating.with.Mice.pdf │ └── newsqueak.pdf └── rio_slides.pdf ├── fixlinks.go ├── gopl-zh-qrcode.png ├── images ├── Alan.Donovan.png ├── Brian.W.Kernighan.png ├── by-nc-sa-4.0-88x31.png ├── ch0-01.png ├── ch1-01.gif ├── ch1-01.png ├── ch1-02.png ├── ch1-03.png ├── ch10-01.png ├── ch11-01.png ├── ch11-02.png ├── ch11-03.png ├── ch11-04.png ├── ch13-01.png ├── ch3-01.png ├── ch3-02.png ├── ch3-03.png ├── ch3-04.png ├── ch3-05.png ├── ch4-01.png ├── ch4-02.png ├── ch4-03.png ├── ch4-04.png ├── ch4-05.png ├── ch4-06.png ├── ch4-xx-01.png ├── ch6-xx-00.png ├── ch7-01.png ├── ch7-02.png ├── ch7-03.png ├── ch7-04.png ├── ch7-05.png ├── ch7-06.png ├── ch7-07.png ├── ch8-01.png ├── ch8-02.png ├── ch8-03.png ├── ch8-04.png ├── ch8-05.png ├── cover.png ├── favicon.ico ├── go-log04.png ├── gopher │ └── go_lang_mascot_by_kirael_art-d7kunhu.gif └── logo │ ├── github.png │ └── gopher-china.png ├── make.bat ├── mkqrcode.go ├── preface.md ├── progress.md ├── tools ├── STPhrases.txt ├── TSCharacters.txt ├── cpdir.go ├── lookpath.go ├── lsdir.go ├── md5.go └── mktable.go ├── vendor ├── github.com │ └── chai2010 │ │ └── image │ │ └── qrencoder │ │ ├── hello.go │ │ ├── internal │ │ ├── coding │ │ │ ├── gen.go │ │ │ └── qr.go │ │ └── gf256 │ │ │ ├── blog_test.go │ │ │ ├── gf256.go │ │ │ └── gf256_test.go │ │ ├── png.go │ │ ├── png_test.go │ │ └── qr.go └── gopl.io │ ├── README.md │ ├── ch1 │ ├── dup1 │ │ └── main.go │ ├── dup2 │ │ └── main.go │ ├── dup3 │ │ └── main.go │ ├── echo1 │ │ └── main.go │ ├── echo2 │ │ └── main.go │ ├── echo3 │ │ └── main.go │ ├── fetch │ │ └── main.go │ ├── fetchall │ │ └── main.go │ ├── helloworld │ │ └── main.go │ ├── lissajous │ │ └── main.go │ ├── server1 │ │ └── main.go │ ├── server2 │ │ └── main.go │ └── server3 │ │ └── main.go │ ├── ch10 │ ├── cross │ │ └── main.go │ └── jpeg │ │ └── main.go │ ├── ch11 │ ├── echo │ │ ├── echo.go │ │ └── echo_test.go │ ├── storage1 │ │ └── storage.go │ ├── storage2 │ │ ├── quota_test.go │ │ └── storage.go │ ├── word1 │ │ ├── word.go │ │ └── word_test.go │ └── word2 │ │ ├── word.go │ │ └── word_test.go │ ├── ch12 │ ├── display │ │ ├── display.go │ │ └── display_test.go │ ├── format │ │ ├── format.go │ │ └── format_test.go │ ├── methods │ │ ├── methods.go │ │ └── methods_test.go │ ├── params │ │ └── params.go │ ├── search │ │ └── main.go │ └── sexpr │ │ ├── decode.go │ │ ├── encode.go │ │ ├── pretty.go │ │ └── sexpr_test.go │ ├── ch13 │ ├── bzip-print │ │ ├── bzip2.c │ │ ├── bzip2.go │ │ └── bzip2_test.go │ ├── bzip │ │ ├── bzip2.c │ │ ├── bzip2.go │ │ └── bzip2_test.go │ ├── bzipper │ │ └── main.go │ ├── equal │ │ ├── equal.go │ │ └── equal_test.go │ └── unsafeptr │ │ └── main.go │ ├── ch2 │ ├── boiling │ │ └── main.go │ ├── cf │ │ └── main.go │ ├── echo4 │ │ └── main.go │ ├── ftoc │ │ └── main.go │ ├── popcount │ │ ├── main.go │ │ └── popcount_test.go │ ├── tempconv │ │ ├── conv.go │ │ └── tempconv.go │ └── tempconv0 │ │ ├── celsius.go │ │ └── tempconv_test.go │ ├── ch3 │ ├── basename1 │ │ └── main.go │ ├── basename2 │ │ └── main.go │ ├── comma │ │ └── main.go │ ├── mandelbrot │ │ └── main.go │ ├── netflag │ │ └── netflag.go │ ├── printints │ │ └── main.go │ └── surface │ │ └── main.go │ ├── ch4 │ ├── append │ │ └── main.go │ ├── autoescape │ │ └── main.go │ ├── charcount │ │ └── main.go │ ├── dedup │ │ └── main.go │ ├── embed │ │ └── main.go │ ├── github │ │ ├── github.go │ │ └── search.go │ ├── graph │ │ └── main.go │ ├── issues │ │ └── main.go │ ├── issueshtml │ │ └── main.go │ ├── issuesreport │ │ └── main.go │ ├── movie │ │ └── main.go │ ├── nonempty │ │ └── main.go │ ├── rev │ │ └── main.go │ ├── sha256 │ │ └── main.go │ └── treesort │ │ ├── sort.go │ │ └── sort_test.go │ ├── ch5 │ ├── defer1 │ │ └── defer.go │ ├── defer2 │ │ └── defer.go │ ├── fetch │ │ └── main.go │ ├── findlinks1 │ │ └── main.go │ ├── findlinks2 │ │ └── main.go │ ├── findlinks3 │ │ └── findlinks.go │ ├── links │ │ └── links.go │ ├── outline │ │ └── main.go │ ├── outline2 │ │ └── outline.go │ ├── squares │ │ └── main.go │ ├── sum │ │ └── main.go │ ├── title1 │ │ └── title.go │ ├── title2 │ │ └── title.go │ ├── title3 │ │ └── title.go │ ├── toposort │ │ └── main.go │ ├── trace │ │ └── main.go │ └── wait │ │ └── wait.go │ ├── ch6 │ ├── coloredpoint │ │ └── main.go │ ├── geometry │ │ └── geometry.go │ ├── intset │ │ ├── intset.go │ │ └── intset_test.go │ └── urlvalues │ │ └── main.go │ ├── ch7 │ ├── bytecounter │ │ └── main.go │ ├── eval │ │ ├── ast.go │ │ ├── check.go │ │ ├── coverage_test.go │ │ ├── eval.go │ │ ├── eval_test.go │ │ ├── parse.go │ │ └── print.go │ ├── http1 │ │ └── main.go │ ├── http2 │ │ └── main.go │ ├── http3 │ │ └── main.go │ ├── http3a │ │ └── main.go │ ├── http4 │ │ └── main.go │ ├── sleep │ │ └── sleep.go │ ├── sorting │ │ └── main.go │ ├── surface │ │ └── surface.go │ ├── tempconv │ │ ├── tempconv.go │ │ └── tempconv.go.~master~ │ ├── tempflag │ │ └── tempflag.go │ └── xmlselect │ │ └── main.go │ ├── ch8 │ ├── cake │ │ ├── cake.go │ │ └── cake_test.go │ ├── chat │ │ ├── chat.go │ │ └── chat.go.~master~ │ ├── clock1 │ │ └── clock.go │ ├── clock2 │ │ └── clock.go │ ├── countdown1 │ │ └── countdown.go │ ├── countdown2 │ │ └── countdown.go │ ├── countdown3 │ │ └── countdown.go │ ├── crawl1 │ │ └── findlinks.go │ ├── crawl2 │ │ └── findlinks.go │ ├── crawl3 │ │ └── findlinks.go │ ├── du1 │ │ └── main.go │ ├── du2 │ │ └── main.go │ ├── du3 │ │ └── main.go │ ├── du4 │ │ └── main.go │ ├── netcat1 │ │ └── netcat.go │ ├── netcat2 │ │ └── netcat.go │ ├── netcat3 │ │ └── netcat.go │ ├── pipeline1 │ │ └── main.go │ ├── pipeline2 │ │ └── main.go │ ├── pipeline3 │ │ └── main.go │ ├── reverb1 │ │ └── reverb.go │ ├── reverb2 │ │ └── reverb.go │ ├── spinner │ │ └── main.go │ └── thumbnail │ │ ├── main.go │ │ ├── thumbnail.go │ │ └── thumbnail_test.go │ └── ch9 │ ├── bank1 │ ├── bank.go │ └── bank_test.go │ ├── bank2 │ ├── bank.go │ └── bank_test.go │ ├── bank3 │ ├── bank.go │ └── bank_test.go │ ├── memo1 │ ├── memo.go │ └── memo_test.go │ ├── memo2 │ ├── memo.go │ └── memo_test.go │ ├── memo3 │ ├── memo.go │ └── memo_test.go │ ├── memo4 │ ├── memo.go │ └── memo_test.go │ ├── memo5 │ ├── memo.go │ └── memo_test.go │ └── memotest │ └── memotest.go └── zh2tw.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Node rules: 2 | ## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 3 | .grunt 4 | 5 | ## Dependency directory 6 | ## Commenting this out is preferred by some people, see 7 | ## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git 8 | node_modules 9 | 10 | # Book build output 11 | _book 12 | 13 | # eBook build output 14 | *.epub 15 | *.mobi 16 | *.pdf -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | # 貢獻者 2 | 3 | 譯者 | 章節 4 | -------------------------------------- | ------------------------- 5 | `chai2010 ` | 前言/第2~4章/第10~13章 6 | `CrazySssst` | 第5章 7 | `foreversmart ` | 第7章 8 | `Xargin ` | 第1章/第6章/第8~9章 9 | 10 | # 譯文授權 11 | 12 | 除特别註明外, 本站內容均采用[知識共享-署名(CC-BY) 3.0協議](http://creativecommons.org/licenses/by/3.0/)授權, 代碼遵循[Go項目的BSD協議](http://golang.org/LICENSE)授權. 13 | 14 | Creative Commons License 15 | 16 | -------------------------------------------------------------------------------- /GLOSSARY.md: -------------------------------------------------------------------------------- 1 | # 術語 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Golang-China. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Golang-China. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | # install gitbook 6 | # npm install gitbook-cli -g 7 | 8 | # https://github.com/GitbookIO 9 | # https://github.com/GitbookIO/gitbook 10 | # https://github.com/GitbookIO/plugin-katex 11 | # https://github.com/wastemobile/gitbook 12 | 13 | # http://www.imagemagick.org/ 14 | 15 | default: 16 | gitbook build 17 | 18 | zh2tw: 19 | go run zh2tw.go . .md$$ 20 | 21 | tw2zh: 22 | go run zh2tw.go . .md$$ tw2zh 23 | 24 | loop: 25 | go run zh2tw.go . .md$$ tw2zh 26 | go run zh2tw.go . .md$$ zh2tw 27 | 28 | review: 29 | go run zh2tw.go . .md$$ tw2zh 30 | gitbook build 31 | go run zh2tw.go . .md$$ zh2tw 32 | 33 | qrcode: 34 | go run mkqrcode.go 35 | 36 | fixlink: 37 | go run fixlinks.go . .md$$ 38 | 39 | cover: 40 | composite cover_patch.png cover_bgd.png cover.jpg 41 | convert -resize 1800x2360! cover.jpg cover.jpg 42 | convert -resize 200x262! cover.jpg cover_small.jpg 43 | convert -resize 400x524! cover.jpg cover_middle.jpg 44 | convert -quality 75% cover.jpg cover.jpg 45 | convert -quality 75% cover_small.jpg cover_small.jpg 46 | convert -quality 75% cover_middle.jpg cover_middle.jpg 47 | convert -strip cover.jpg cover.jpg 48 | convert -strip cover_small.jpg cover_small.jpg 49 | convert -strip cover_middle.jpg cover_middle.jpg 50 | 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go語言聖經(中文版) 2 | 3 | Go語言聖經 [《The Go Programming Language》](http://gopl.io) 中文版本,僅供學習交流之用。 4 | 5 | [![](cover_middle.jpg)](https://github.com/golang-china/gopl-zh) 6 | 7 | - 在線版本:http://golang-china.github.io/gopl-zh 8 | - 離線版本:http://github.com/golang-china/gopl-zh/archive/gh-pages.zip 9 | - 項目主頁:http://github.com/golang-china/gopl-zh 10 | - 原版官網:http://gopl.io 11 | 12 | 13 | ### 從源文件構建 14 | 15 | 先安裝NodeJS和GitBook命令行工具(`npm install gitbook-cli -g`命令)。 16 | 17 | 1. 下載 https://github.com/golang-china/gopl-zh/archive/master.zip ,獲取源文件。 18 | 2. 切換到 `gopl-zh` 目録,運行 `gitbook install`,安裝GitBook插件。 19 | 3. 運行`gitbook build`,生成`_book`目録。 20 | 4. 打開`_book/index.html`文件。 21 | 22 | ### 簡體/繁體轉換 23 | 24 | 切片到 `gopl-zh` 目録: 25 | 26 | - `make zh2tw` 或 `go run zh2tw.go . "\.md$" zh2tw`,轉繁體。 27 | - `make tw2zh` 或 `go run zh2tw.go . "\.md$" tw2zh`,轉簡體。 28 | 29 | ### Markdown 格式預覽 30 | 31 | - [SUMMARY-github.md](SUMMARY-github.md) 32 | 33 | # 版權聲明 34 | 35 | Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License。 36 | 37 | Creative Commons License 38 | 39 | 嚴禁任何商業行爲使用或引用該文檔的全部或部分內容! 40 | 41 | 歡迎大家提供建議! 42 | -------------------------------------------------------------------------------- /appendix/appendix-c-cpoyright.md: -------------------------------------------------------------------------------- 1 | ## 附録C:譯文授權 2 | 3 | 除特别註明外, 本站內容均采用[知識共享-署名(CC-BY) 3.0協議](http://creativecommons.org/licenses/by/3.0/)授權, 代碼遵循[Go項目的BSD協議](http://golang.org/LICENSE)授權. 4 | 5 | Creative Commons License 6 | 7 | -------------------------------------------------------------------------------- /appendix/appendix-d-translations.md: -------------------------------------------------------------------------------- 1 | ## 附録D:其它語言 2 | 3 | 下表是 [The Go Programming Language](http://www.gopl.io/) 其它語言版本: 4 | 5 | 語言 | 鏈接 | 時間 | 譯者 | ISBN 6 | ---- | ---- | ---- | ---- | ---- 7 | 中文 | [《Go語言聖經》][gopl-zh] | 2016/2/1 | [chai2010][chai2010], [Xargin][Xargin], [CrazySssst][CrazySssst], [foreversmart][foreversmart] | ? 8 | 韓語 | [Acorn Publishing (Korea)](http://www.acornpub.co.kr/) | 2016 | ? | ? 9 | 俄語 | [Williams Publishing (Russia)](http://www.williamspublishing.com/) | 2016 | ? | ? 10 | 波蘭語 | [Helion (Poland)](http://helion.pl/) | 2016 | ? | ? 11 | 日語 | [Maruzen Publishing (Japan)](http://www.maruzen.co.jp/corp/en/services/publishing.html) | 2017 | Yoshiki Shibata | ? 12 | 葡萄牙語 | [Novatec Editora (Brazil)](http://novatec.com.br/) |2017 | ? | ? 13 | 中文簡體 | [Pearson Education Asia](http://www.pearsonapac.com/) |2017 | ? | ? 14 | 中文繁體 | [Gotop Information (Taiwan)](http://www.gotop.com.tw/) | 2017 | ? | ? 15 | 16 | 17 | [gopl-zh]: http://golang-china.github.io/gopl-zh/ "《Go語言聖經》" 18 | 19 | [chai2010]: https://github.com/chai2010 20 | [Xargin]: https://github.com/cch123 21 | [CrazySssst]: https://github.com/CrazySssst 22 | [foreversmart]: https://github.com/foreversmart 23 | -------------------------------------------------------------------------------- /appendix/appendix-z-index-good.md: -------------------------------------------------------------------------------- 1 | # 索引 2 | 3 | TODO 4 | -------------------------------------------------------------------------------- /appendix/appendix.md: -------------------------------------------------------------------------------- 1 | # 附録 2 | 3 | 英文原版併沒有包含附録部分,隻有一個索引部分。中文版增加附録部分主要用於收録一些和本書相關的內容,比如英文原版的勘誤(有些讀者可能會對照中文和英文原閲讀)、英文作者和中文譯者、譯文授權等內容。以後還可能會考慮增加一些習題解答相關的內容。 4 | 5 | 需要特别説明的是,中文版附録併沒有包含英文原版的索引信息。因爲英文原版的索引信息主要是記録每個索引所在的英文頁面位置,而中文版是以GitBook方式組織的html網頁形式,將英文頁面位置轉爲章節位置可能會更合理,不過這個會涉及到繁瑣的手工操作。如果大家有更好的建議,請告知我們。 6 | 7 | -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "gitbook": "2.x.x", 3 | "title": "Go语言圣经", 4 | "description": "中文版", 5 | "language": "zh", 6 | "structure": { 7 | "readme": "preface.md" 8 | }, 9 | "plugins": [ 10 | "katex", 11 | "-search" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /ch0/ch0-02.md: -------------------------------------------------------------------------------- 1 | ## Go語言項目 2 | 3 | 所有的編程語言都反映了語言設計者對編程哲學的反思,通常包括之前的語言所暴露的一些不足地方的改進。Go項目是在Google公司維護超級複雜的幾個軟件繫統遇到的一些問題的反思(但是這類問題絶不是Google公司所特有的)。 4 | 5 | 正如[Rob Pike](http://genius.cat-v.org/rob-pike/)所説,“軟件的複雜性是乘法級相關的”,通過增加一個部分的複雜性來脩複問題通常將慢慢地增加其他部分的複雜性。通過增加功能和選項和配置是脩複問題的最快的途徑,但是這很容易讓人忘記簡潔的內涵,卽使從長遠來看,簡潔依然是好軟件的關鍵因素。 6 | 7 | 簡潔的設計需要在工作開始的時候舍棄不必要的想法,併且在軟件的生命週期內嚴格區别好的改變或壞的改變。通過足夠的努力,一個好的改變可以在不破壞原有完整概念的前提下保持自適應,正如[Fred Brooks](http://www.cs.unc.edu/~brooks/)所説的“概念完整性”;而一個壞的改變則不能達到這個效果,它們僅僅是通過膚淺的和簡單的妥協來破壞原有設計的一致性。隻有通過簡潔的設計,才能讓一個繫統保持穩定、安全和持續的進化。 8 | 9 | Go項目包括編程語言本身,附帶了相關的工具和標準庫,最後但併非代表不重要的,關於簡潔編程哲學的宣言。就事後諸葛的角度來看,Go語言的這些地方都做的還不錯:擁有自動垃圾迴收、一個包繫統、函數作爲一等公民、詞法作用域、繫統調用接口、隻讀的UTF8字符串等。但是Go語言本身隻有很少的特性,也不太可能添加太多的特性。例如,它沒有隱式的數值轉換,沒有構造函數和析構函數,沒有運算符重載,沒有默認參數,也沒有繼承,沒有泛型,沒有異常,沒有宏,沒有函數脩飾,更沒有線程局部存儲。但是語言本身是成熟和穩定的,而且承諾保證向後兼容:用之前的Go語言編寫程序可以用新版本的Go語言編譯器和標準庫直接構建而不需要脩改代碼。 10 | 11 | Go語言有足夠的類型繫統以避免動態語言中那些粗心的類型錯誤,但是Go語言的類型繫統相比傳統的強類型語言又要簡潔很多。雖然有時候這會導致一個“無類型”的抽象類型概念,但是Go語言程序員併不需要像C++或Haskell程序員那樣糾結於具體類型的安全屬性。在實踐中Go語言簡潔的類型繫統給了程序員帶來了更多的安全性和更好的運行時性能。 12 | 13 | Go語言鼓勵當代計算機繫統設計的原則,特别是局部的重要性。它的內置數據類型和大多數的準庫數據結構都經過精心設計而避免顯式的初始化或隱式的構造函數,因爲很少的內存分配和內存初始化代碼被隱藏在庫代碼中了。Go語言的聚合類型(結構體和數組)可以直接操作它們的元素,隻需要更少的存儲空間、更少的內存分配,而且指針操作比其他間接操作的語言也更有效率。由於現代計算機是一個併行的機器,Go語言提供了基於CSP的併發特性支持。Go語言的動態棧使得輕量級線程goroutine的初始棧可以很小,因此創建一個goroutine的代價很小,創建百萬級的goroutine完全是可行的。 14 | 15 | Go語言的標準庫(通常被稱爲語言自帶的電池),提供了清晰的構建模塊和公共接口,包含I/O操作、文本處理、圖像、密碼學、網絡和分布式應用程序等,併支持許多標準化的文件格式和編解碼協議。庫和工具使用了大量的約定來減少額外的配置和解釋,從而最終簡化程序的邏輯,而且每個Go程序結構都是如此的相似,因此Go程序也很容易學習。使用Go語言自帶工具構建Go語言項目隻需要使用文件名和標識符名稱, 一個偶爾的特殊註釋來確定所有的庫、可執行文件、測試、基準測試、例子、以及特定於平台的變量、項目的文檔等;Go語言源代碼本身就包含了構建規范。 16 | 17 | -------------------------------------------------------------------------------- /ch0/ch0-03.md: -------------------------------------------------------------------------------- 1 | ## 本書的組織 2 | 3 | 我們假設你已經有一種或多種其他編程語言的使用經歷,不管是類似C、c++或Java的編譯型語言,還是類似Python、Ruby、JavaScript的腳本語言,因此我們不會像對完全的編程語言初學者那樣解釋所有的細節。因爲Go語言的變量、常量、表達式、控製流和函數等基本語法也是類似的。 4 | 5 | 第一章包含了本敎程的基本結構,通過十幾個程序介紹了用Go語言如何實現 類似讀寫文件、文本格式化、創建圖像、網絡客戶端和服務器通訊等日常工作。 6 | 7 | 第二章描述了Go語言程序的基本元素結構、變量、新類型定義、包和文件、以及作用域的概念。第三章討論了數字、布爾值、字符串和常量,併演示了如何顯示和處理Unicode字符。第四章描述了複合類型,從簡單的數組、字典、切片到動態列表。第五章涵蓋了函數,併討論了錯誤處理、panic和recover,還有defer語句。 8 | 9 | 第一章到第五章是基礎部分,主流命令式編程語言這部分都類似。個别之處,Go語言有自己特色的語法和風格,但是大多數程序員能很快適應。其餘章節是Go語言特有的:方法、接口、併發、包、測試和反射等語言特性。 10 | 11 | Go語言的面向對象機製與一般語言不同。它沒有類層次結構,甚至可以説沒有類;僅僅通過組合(而不是繼承)簡單的對象來構建複雜的對象。方法不僅可以定義在結構體上, 而且可以定義在任何用戶自定義的類型上;併且具體類型和抽象類型(接口)之間的關繫是隱式的,所以很多類型的設計者可能併不知道該類型到底實現了哪些接口。方法在第六章討論,接口在第七章討論。 12 | 13 | 第八章討論了基於順序通信進程(CSP)概念的併發編程,使用goroutines和channels處理併發編程。第九章則討論了傳統的基於共享變量的併發編程。 14 | 15 | 第十章描述了包機製和包的組織結構。這一章還展示了如何有效的利用Go自帶的工具,使用單個命令完成編譯、測試、基準測試、代碼格式化、文檔以及其他諸多任務。 16 | 17 | 第十一章討論了單元測試,Go語言的工具和標準庫中集成了輕量級的測試功能,避免了強大但複雜的測試框架。測試庫提供了一些基本構件,必要時可以用來構建複雜的測試構件。 18 | 19 | 第十二章討論了反射,一種程序在運行期間審視自己的能力。反射是一個強大的編程工具,不過要謹慎地使用;這一章利用反射機製實現一些重要的Go語言庫函數, 展示了反射的強大用法。第十三章解釋了底層編程的細節,在必要時,可以使用unsafe包繞過Go語言安全的類型繫統。 20 | 21 | 部分章節的後面有練習題,根據對Go語言的理解脩改書中的例子來探索Go語言的用法。 22 | 23 | 書中所有的代碼都可以從 http://gopl.io 上的Git倉庫下載。go get命令根據每個例子的導入路徑智能地獲取、構建併安裝。隻需要選擇一個目録作爲工作空間,然後將GOPATH環境變量設置爲該路徑。 24 | 25 | 必要時,Go語言工具會創建目録。例如: 26 | 27 | ``` 28 | $ export GOPATH=$HOME/gobook # 選擇工作目録 29 | $ go get gopl.io/ch1/helloworld # 獲取/編譯/安裝 30 | $ $GOPATH/bin/helloworld # 運行程序 31 | Hello, 世界 # 這是中文 32 | ``` 33 | 34 | 運行這些例子需要安裝Go1.5以上的版本。 35 | 36 | ``` 37 | $ go version 38 | go version go1.5 linux/amd64 39 | ``` 40 | 41 | 如果使用其他的操作繫統, 請參考 https://golang.org/doc/install 提供的説明安裝。 42 | 43 | -------------------------------------------------------------------------------- /ch0/ch0-04.md: -------------------------------------------------------------------------------- 1 | ## 更多的信息 2 | 3 | 最佳的幫助信息來自Go語言的官方網站,https://golang.org ,它提供了完善的參考文檔,包括編程語言規范和標準庫等諸多權威的幫助信息。同時也包含了如何編寫更地道的Go程序的基本敎程,還有各種各樣的在線文本資源和視頻資源,它們是本書最有價值的補充。Go語言的官方博客 https://blog.golang.org 會不定期發布一些Go語言最好的實踐文章,包括當前語言的發展狀態、未來的計劃、會議報告和Go語言相關的各種會議的主題等信息(譯註: http://talks.golang.org/ 包含了官方收録的各種報告的講稿)。 4 | 5 | 在線訪問的一個有價值的地方是可以從web頁面運行Go語言的程序(而紙質書則沒有這麽便利了)。這個功能由來自 https://play.golang.org 的 Go Playground 提供,併且可以方便地嵌入到其他頁面中,例如 https://golang.org 的主頁,或 godoc 提供的文檔頁面中。 6 | 7 | Playground可以簡單的通過執行一個小程序來測試對語法、語義和對程序庫的理解,類似其他很多語言提供的REPL卽時運行的工具。同時它可以生成對應的url,非常適合共享Go語言代碼片段,滙報bug或提供反饋意見等。 8 | 9 | 基於 Playground 構建的 Go Tour,https://tour.golang.org ,是一個繫列的Go語言入門敎程,它包含了諸多基本概念和結構相關的併可在線運行的互動小程序。 10 | 11 | 當然,Playground 和 Tour 也有一些限製,它們隻能導入標準庫,而且因爲安全的原因對一些網絡庫做了限製。如果要在編譯和運行時需要訪問互聯網,對於一些更複製的實驗,你可能需要在自己的電腦上構建併運行程序。幸運的是下載Go語言的過程很簡單,從 https://golang.org 下載安裝包應該不超過幾分鐘(譯註:感謝偉大的長城,讓大陸的Gopher們都學會了自己打洞的基本生活技能,下載時間可能會因爲洞的大小等因素從幾分鐘到幾天或更久),然後就可以在自己電腦上編寫和運行Go程序了。 12 | 13 | Go語言是一個開源項目,你可以在 https://golang.org/pkg 閲讀標準庫中任意函數和類型的實現代碼,和下載安裝包的代碼完全一致。這樣你可以知道很多函數是如何工作的, 通過挖掘找出一些答案的細節,或者僅僅是出於欣賞專業級Go代碼。 14 | 15 | -------------------------------------------------------------------------------- /ch0/ch0-05.md: -------------------------------------------------------------------------------- 1 | ## 致謝 2 | 3 | [Rob Pike](http://genius.cat-v.org/rob-pike/)和[Russ Cox](http://research.swtch.com/),以及很多其他Go糰隊的核心成員多次仔細閲讀了本書的手稿,他們對本書的組織結構和表述用詞等給出了很多寶貴的建議。在準備日文版翻譯的時候,Yoshiki Shibata更是仔細地審閲了本書的每個部分,及時發現了諸多英文和代碼的錯誤。我們非常感謝本書的每一位審閲者,併感謝對本書給出了重要的建議的Brian Goetz、Corey Kosak、Arnold Robbins、Josh Bleecher Snyder和Peter Weinberger等人。 4 | 5 | 我們還感謝Sameer Ajmani、Ittai Balaban、David Crawshaw、Billy Donohue、Jonathan Feinberg、Andrew Gerrand、Robert Griesemer、John Linderman、Minux Ma(譯註:中国人,Go糰隊成員。)、Bryan Mills、Bala Natarajan、Cosmos Nicolaou、Paul Staniforth、Nigel Tao(譯註:好像是陶哲軒的兄弟)以及Howard Trickey給出的許多有價值的建議。我們還要感謝David Brailsford和Raph Levien關於類型設置的建議。 6 | 7 | 我們的來自Addison-Wesley的編輯Greg Doench收到了很多幫助,從最開始就得到了越來越多的幫助。來自AW生産糰隊的John Fuller、Dayna Isley、Julie Nahil、Chuti Prasertsith到Barbara Wood,感謝你們的熱心幫助。 8 | 9 | [Alan Donovan](https://github.com/adonovan)特别感謝:Sameer Ajmani、Chris Demetriou、Walt Drummond和Google公司的Reid Tatge允許他有充裕的時間去寫本書;感謝Stephen Donovan的建議和始終如一的鼓勵,以及他的妻子Leila Kazemi併沒有讓他爲了家庭瑣事而分心,併熱情堅定地支持這個項目。 10 | 11 | [Brian Kernighan](http://www.cs.princeton.edu/~bwk/)特别感謝:朋友和同事對他的耐心和寬容,讓他慢慢地梳理本書的寫作思路。同時感謝他的妻子Meg和其他很多朋友對他寫作事業的支持。 12 | 13 | 2015年 10月 於 紐約 14 | 15 | 16 | -------------------------------------------------------------------------------- /ch1/ch1.md: -------------------------------------------------------------------------------- 1 | # 第1章 入門 2 | 3 | 本章介紹Go語言的基礎組件。本章提供了足夠的信息和示例程序,希望可以幫你盡快入門, 寫出有用的程序。本章和之後章節的示例程序都針對你可能遇到的現實案例。先了解幾個Go程序,涉及的主題從簡單的文件處理、圖像處理到互聯網客戶端和服務端併發。當然,第一章不會解釋細枝末節,但用這些程序來學習一門新語言還是很有效的。 4 | 5 | 學習一門新語言時,會有一種自然的傾向, 按照自己熟悉的語言的套路寫新語言程序。學習Go語言的過程中,請警惕這種想法,盡量别這麽做。我們會演示怎麽寫好Go語言程序,所以請使用本書的代碼作爲你自己寫程序時的指南。 6 | -------------------------------------------------------------------------------- /ch10/ch10-01.md: -------------------------------------------------------------------------------- 1 | ## 10.1. 包簡介 2 | 3 | 任何包繫統設計的目的都是爲了簡化大型程序的設計和維護工作,通過將一組相關的特性放進一個獨立的單元以便於理解和更新,在每個單元更新的同時保持和程序中其它單元的相對獨立性。這種模塊化的特性允許每個包可以被其它的不同項目共享和重用,在項目范圍內、甚至全球范圍統一的分發和複用。 4 | 5 | 每個包一般都定義了一個不同的名字空間用於它內部的每個標識符的訪問。每個名字空間關聯到一個特定的包,讓我們給類型、函數等選擇簡短明了的名字,這樣可以避免在我們使用它們的時候減少和其它部分名字的衝突。 6 | 7 | 每個包還通過控製包內名字的可見性和是否導出來實現封裝特性。通過限製包成員的可見性併隱藏包API的具體實現,將允許包的維護者在不影響外部包用戶的前提下調整包的內部實現。通過限製包內變量的可見性,還可以強製用戶通過某些特定函數來訪問和更新內部變量,這樣可以保證內部變量的一致性和併發時的互斥約束。 8 | 9 | 當我們脩改了一個源文件,我們必須重新編譯該源文件對應的包和所有依賴該包的其他包。卽使是從頭構建,Go語言編譯器的編譯速度也明顯快於其它編譯語言。Go語言的閃電般的編譯速度主要得益於三個語言特性。第一點,所有導入的包必須在每個文件的開頭顯式聲明,這樣的話編譯器就沒有必要讀取和分析整個源文件來判斷包的依賴關繫。第二點,禁止包的環狀依賴,因爲沒有循環依賴,包的依賴關繫形成一個有向無環圖,每個包可以被獨立編譯,而且很可能是被併發編譯。第三點,編譯後包的目標文件不僅僅記録包本身的導出信息,目標文件同時還記録了包的依賴關繫。因此,在編譯一個包的時候,編譯器隻需要讀取每個直接導入包的目標文件,而不需要遍歷所有依賴的的文件(譯註:很多都是重複的間接依賴)。 10 | -------------------------------------------------------------------------------- /ch10/ch10-02.md: -------------------------------------------------------------------------------- 1 | ## 10.2. 導入路徑 2 | 3 | 每個包是由一個全局唯一的字符串所標識的導入路徑定位。出現在import語句中的導入路徑也是字符串。 4 | 5 | ```Go 6 | import ( 7 | "fmt" 8 | "math/rand" 9 | "encoding/json" 10 | 11 | "golang.org/x/net/html" 12 | 13 | "github.com/go-sql-driver/mysql" 14 | ) 15 | ``` 16 | 17 | 就像我們在2.6.1節提到過的,Go語言的規范併沒有指明包的導入路徑字符串的具體含義,導入路徑的具體含義是由構建工具來解釋的。在本章,我們將深入討論Go語言工具箱的功能,包括大家經常使用的構建測試等功能。當然,也有第三方擴展的工具箱存在。例如,Google公司內部的Go語言碼農,他們就使用內部的多語言構建繫統(譯註:Google公司使用的是類似[Bazel](http://bazel.io)的構建繫統,支持多種編程語言,目前該構件繫統還不能完整支持Windows環境),用不同的規則來處理包名字和定位包,用不同的規則來處理單元測試等等,因爲這樣可以更緊密適配他們內部環境。 18 | 19 | 如果你計劃分享或發布包,那麽導入路徑最好是全球唯一的。爲了避免衝突,所有非標準庫包的導入路徑建議以所在組織的互聯網域名爲前綴;而且這樣也有利於包的檢索。例如,上面的import語句導入了Go糰隊維護的HTML解析器和一個流行的第三方維護的MySQL驅動。 20 | -------------------------------------------------------------------------------- /ch10/ch10-03.md: -------------------------------------------------------------------------------- 1 | ## 10.3. 包聲明 2 | 3 | 在每個Go語音源文件的開頭都必須有包聲明語句。包聲明語句的主要目的是確定當前包被其它包導入時默認的標識符(也稱爲包名)。 4 | 5 | 例如,math/rand包的每個源文件的開頭都包含`package rand`包聲明語句,所以當你導入這個包,你就可以用rand.Int、rand.Float64類似的方式訪問包的成員。 6 | 7 | ```Go 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "math/rand" 13 | ) 14 | 15 | func main() { 16 | fmt.Println(rand.Int()) 17 | } 18 | ``` 19 | 20 | 通常來説,默認的包名就是包導入路徑名的最後一段,因此卽使兩個包的導入路徑不同,它們依然可能有一個相同的包名。例如,math/rand包和crypto/rand包的包名都是rand。稍後我們將看到如何同時導入兩個有相同包名的包。 21 | 22 | 關於默認包名一般采用導入路徑名的最後一段的約定也有三種例外情況。第一個例外,包對應一個可執行程序,也就是main包,這時候main包本身的導入路徑是無關緊要的。名字爲main的包是給go build(§10.7.3)構建命令一個信息,這個包編譯完之後必須調用連接器生成一個可執行程序。 23 | 24 | 第二個例外,包所在的目録中可能有一些文件名是以_test.go爲後綴的Go源文件(譯註:前面必須有其它的字符,因爲以`_`前綴的源文件是被忽略的),併且這些源文件聲明的包名也是以_test爲後綴名的。這種目録可以包含兩種包:一種普通包,加一種則是測試的外部擴展包。所有以_test爲後綴包名的測試外部擴展包都由go test命令獨立編譯,普通包和測試的外部擴展包是相互獨立的。測試的外部擴展包一般用來避免測試代碼中的循環導入依賴,具體細節我們將在11.2.4節中介紹。 25 | 26 | 第三個例外,一些依賴版本號的管理工具會在導入路徑後追加版本號信息,例如"gopkg.in/yaml.v2"。這種情況下包的名字併不包含版本號後綴,而是yaml。 27 | -------------------------------------------------------------------------------- /ch10/ch10-04.md: -------------------------------------------------------------------------------- 1 | ## 10.4. 導入聲明 2 | 3 | 可以在一個Go語言源文件包聲明語句之後,其它非導入聲明語句之前,包含零到多個導入包聲明語句。每個導入聲明可以單獨指定一個導入路徑,也可以通過圓括號同時導入多個導入路徑。下面兩個導入形式是等價的,但是第二種形式更爲常見。 4 | 5 | ```Go 6 | import "fmt" 7 | import "os" 8 | 9 | import ( 10 | "fmt" 11 | "os" 12 | ) 13 | ``` 14 | 15 | 導入的包之間可以通過添加空行來分組;通常將來自不同組織的包獨自分組。包的導入順序無關緊要,但是在每個分組中一般會根據字符串順序排列。(gofmt和goimports工具都可以將不同分組導入的包獨立排序。) 16 | 17 | ```Go 18 | import ( 19 | "fmt" 20 | "html/template" 21 | "os" 22 | 23 | "golang.org/x/net/html" 24 | "golang.org/x/net/ipv4" 25 | ) 26 | ``` 27 | 28 | 如果我們想同時導入兩個有着名字相同的包,例如math/rand包和crypto/rand包,那麽導入聲明必須至少爲一個同名包指定一個新的包名以避免衝突。這叫做導入包的重命名。 29 | 30 | ```Go 31 | import ( 32 | "crypto/rand" 33 | mrand "math/rand" // alternative name mrand avoids conflict 34 | ) 35 | ``` 36 | 37 | 導入包的重命名隻影響當前的源文件。其它的源文件如果導入了相同的包,可以用導入包原本默認的名字或重命名爲另一個完全不同的名字。 38 | 39 | 導入包重命名是一個有用的特性,它不僅僅隻是爲了解決名字衝突。如果導入的一個包名很笨重,特别是在一些自動生成的代碼中,這時候用一個簡短名稱會更方便。選擇用簡短名稱重命名導入包時候最好統一,以避免包名混亂。選擇另一個包名稱還可以幫助避免和本地普通變量名産生衝突。例如,如果文件中已經有了一個名爲path的變量,那麽我們可以將"path"標準包重命名爲pathpkg。 40 | 41 | 每個導入聲明語句都明確指定了當前包和被導入包之間的依賴關繫。如果遇到包循環導入的情況,Go語言的構建工具將報告錯誤。 42 | -------------------------------------------------------------------------------- /ch10/ch10-06.md: -------------------------------------------------------------------------------- 1 | ## 10.6. 包和命名 2 | 3 | 在本節中,我們將提供一些關於Go語言獨特的包和成員命名的約定。 4 | 5 | 當創建一個包,一般要用短小的包名,但也不能太短導致難以理解。標準庫中最常用的包有bufio、bytes、flag、fmt、http、io、json、os、sort、sync和time等包。 6 | 7 | 它們的名字都簡潔明了。例如,不要將一個類似imageutil或ioutilis的通用包命名爲util,雖然它看起來很短小。要盡量避免包名使用可能被經常用於局部變量的名字,這樣可能導致用戶重命名導入包,例如前面看到的path包。 8 | 9 | 包名一般采用單數的形式。標準庫的bytes、errors和strings使用了複數形式,這是爲了避免和預定義的類型衝突,同樣還有go/types是爲了避免和type關鍵字衝突。 10 | 11 | 要避免包名有其它的含義。例如,2.5節中我們的溫度轉換包最初使用了temp包名,雖然併沒有持續多久。但這是一個糟糕的嚐試,因爲temp幾乎是臨時變量的同義詞。然後我們有一段時間使用了temperature作爲包名,雖然名字併沒有表達包的眞實用途。最後我們改成了和strconv標準包類似的tempconv包名,這個名字比之前的就好多了。 12 | 13 | 現在讓我們看看如何命名包的成員。由於是通過包的導入名字引入包里面的成員,例如fmt.Println,同時包含了包名和成員名信息。因此,我們一般併不需要關註Println的具體內容,因爲fmt包名已經包含了這個信息。當設計一個包的時候,需要考慮包名和成員名兩個部分如何很好地配合。下面有一些例子: 14 | 15 | ``` 16 | bytes.Equal flag.Int http.Get json.Marshal 17 | ``` 18 | 19 | 我們可以看到一些常用的命名模式。strings包提供了和字符串相關的諸多操作: 20 | 21 | ```Go 22 | package strings 23 | 24 | func Index(needle, haystack string) int 25 | 26 | type Replacer struct{ /* ... */ } 27 | func NewReplacer(oldnew ...string) *Replacer 28 | 29 | type Reader struct{ /* ... */ } 30 | func NewReader(s string) *Reader 31 | ``` 32 | 33 | 字符串string本身併沒有出現在每個成員名字中。因爲用戶會這樣引用這些成員strings.Index、strings.Replacer等。 34 | 35 | 其它一些包,可能隻描述了單一的數據類型,例如html/template和math/rand等,隻暴露一個主要的數據結構和與它相關的方法,還有一個以New命名的函數用於創建實例。 36 | 37 | ```Go 38 | package rand // "math/rand" 39 | 40 | type Rand struct{ /* ... */ } 41 | func New(source Source) *Rand 42 | ``` 43 | 44 | 這可能導致一些名字重複,例如template.Template或rand.Rand,這就是爲什麽這些種類的包名往往特别短的原因之一。 45 | 46 | 在另一個極端,還有像net/http包那樣含有非常多的名字和種類不多的數據類型,因爲它們都是要執行一個複雜的複合任務。盡管有將近二十種類型和更多的函數,但是包中最重要的成員名字卻是簡單明了的:Get、Post、Handle、Error、Client、Server等。 47 | -------------------------------------------------------------------------------- /ch10/ch10-07-1.md: -------------------------------------------------------------------------------- 1 | ### 10.7.1. 工作區結構 2 | 3 | 對於大多數的Go語言用戶,隻需要配置一個名叫GOPATH的環境變量,用來指定當前工作目録卽可。當需要切換到不同工作區的時候,隻要更新GOPATH就可以了。例如,我們在編寫本書時將GOPATH設置爲`$HOME/gobook`: 4 | 5 | ``` 6 | $ export GOPATH=$HOME/gobook 7 | $ go get gopl.io/... 8 | ``` 9 | 10 | 當你用前面介紹的命令下載本書全部的例子源碼之後,你的當前工作區的目録結構應該是這樣的: 11 | 12 | ``` 13 | GOPATH/ 14 | src/ 15 | gopl.io/ 16 | .git/ 17 | ch1/ 18 | helloworld/ 19 | main.go 20 | dup/ 21 | main.go 22 | ... 23 | golang.org/x/net/ 24 | .git/ 25 | html/ 26 | parse.go 27 | node.go 28 | ... 29 | bin/ 30 | helloworld 31 | dup 32 | pkg/ 33 | darwin_amd64/ 34 | ... 35 | ``` 36 | 37 | GOPATH對應的工作區目録有三個子目録。其中src子目録用於存儲源代碼。每個包被保存在與$GOPATH/src的相對路徑爲包導入路徑的子目録中,例如gopl.io/ch1/helloworld相對應的路徑目録。我們看到,一個GOPATH工作區的src目録中可能有多個獨立的版本控製繫統,例如gopl.io和golang.org分别對應不同的Git倉庫。其中pkg子目録用於保存編譯後的包的目標文件,bin子目録用於保存編譯後的可執行程序,例如helloworld可執行程序。 38 | 39 | 第二個環境變量GOROOT用來指定Go的安裝目録,還有它自帶的標準庫包的位置。GOROOT的目録結構和GOPATH類似,因此存放fmt包的源代碼對應目録應該爲$GOROOT/src/fmt。用戶一般不需要設置GOROOT,默認情況下Go語言安裝工具會將其設置爲安裝的目録路徑。 40 | 41 | 其中`go env`命令用於査看Go語音工具涉及的所有環境變量的值,包括未設置環境變量的默認值。GOOS環境變量用於指定目標操作繫統(例如android、linux、darwin或windows),GOARCH環境變量用於指定處理器的類型,例如amd64、386或arm等。雖然GOPATH環境變量是唯一必需要設置的,但是其它環境變量也會偶爾用到。 42 | 43 | ``` 44 | $ go env 45 | GOPATH="/home/gopher/gobook" 46 | GOROOT="/usr/local/go" 47 | GOARCH="amd64" 48 | GOOS="darwin" 49 | ... 50 | ``` 51 | -------------------------------------------------------------------------------- /ch10/ch10-07-5.md: -------------------------------------------------------------------------------- 1 | ### 10.7.5. 內部包 2 | 3 | 在Go語音程序中,包的封裝機製是一個重要的特性。沒有導出的標識符隻在同一個包內部可以訪問,而導出的標識符則是面向全宇宙都是可見的。 4 | 5 | 有時候,一個中間的狀態可能也是有用的,對於一小部分信任的包是可見的,但併不是對所有調用者都可見。例如,當我們計劃將一個大的包拆分爲很多小的更容易維護的子包,但是我們併不想將內部的子包結構也完全暴露出去。同時,我們可能還希望在內部子包之間共享一些通用的處理包,或者我們隻是想實驗一個新包的還併不穩定的接口,暫時隻暴露給一些受限製的用戶使用。 6 | 7 | ![](../images/ch10-01.png) 8 | 9 | 爲了滿足這些需求,Go語言的構建工具對包含internal名字的路徑段的包導入路徑做了特殊處理。這種包叫internal包,一個internal包隻能被和internal目録有同一個父目録的包所導入。例如,net/http/internal/chunked內部包隻能被net/http/httputil或net/http包導入,但是不能被net/url包導入。不過net/url包卻可以導入net/http/httputil包。 10 | 11 | ``` 12 | net/http 13 | net/http/internal/chunked 14 | net/http/httputil 15 | net/url 16 | ``` 17 | -------------------------------------------------------------------------------- /ch10/ch10-07.md: -------------------------------------------------------------------------------- 1 | ## 10.7. 工具 2 | 3 | 本章剩下的部分將討論Go語言工具箱的具體功能,包括如何下載、格式化、構建、測試和安裝Go語言編寫的程序。 4 | 5 | Go語言的工具箱集合了一繫列的功能的命令集。它可以看作是一個包管理器(類似於Linux中的apt和rpm工具),用於包的査詢、計算的包依賴關繫、從遠程版本控製繫統和下載它們等任務。它也是一個構建繫統,計算文件的依賴關繫,然後調用編譯器、滙編器和連接器構建程序,雖然它故意被設計成沒有標準的make命令那麽複雜。它也是一個單元測試和基準測試的驅動程序,我們將在第11章討論測試話題。 6 | 7 | Go語言工具箱的命令有着類似“瑞士軍刀”的風格,帶着一打子的子命令,有一些我們經常用到,例如get、run、build和fmt等。你可以運行go或go help命令査看內置的幫助文檔,爲了査詢方便,我們列出了最常用的命令: 8 | 9 | ``` 10 | $ go 11 | ... 12 | build compile packages and dependencies 13 | clean remove object files 14 | doc show documentation for package or symbol 15 | env print Go environment information 16 | fmt run gofmt on package sources 17 | get download and install packages and dependencies 18 | install compile and install packages and dependencies 19 | list list packages 20 | run compile and run Go program 21 | test test packages 22 | version print Go version 23 | vet run go tool vet on packages 24 | 25 | Use "go help [command]" for more information about a command. 26 | ... 27 | ``` 28 | 29 | 爲了達到零配置的設計目標,Go語言的工具箱很多地方都依賴各種約定。例如,根據給定的源文件的名稱,Go語言的工具可以找到源文件對應的包,因爲每個目録隻包含了單一的包,併且到的導入路徑和工作區的目録結構是對應的。給定一個包的導入路徑,Go語言的工具可以找到對應的目録中沒個實體對應的源文件。它還可以根據導入路徑找到存儲代碼倉庫的遠程服務器的URL。 30 | 31 | {% include "./ch10-07-1.md" %} 32 | 33 | {% include "./ch10-07-2.md" %} 34 | 35 | {% include "./ch10-07-3.md" %} 36 | 37 | {% include "./ch10-07-4.md" %} 38 | 39 | {% include "./ch10-07-5.md" %} 40 | 41 | {% include "./ch10-07-6.md" %} 42 | -------------------------------------------------------------------------------- /ch10/ch10.md: -------------------------------------------------------------------------------- 1 | # 第十章 包和工具 2 | 3 | 現在隨便一個小程序的實現都可能包含超過10000個函數。然而作者一般隻需要考慮其中很小的一部分和做很少的設計,因爲絶大部分代碼都是由他人編寫的,它們通過類似包或模塊的方式被重用。 4 | 5 | Go語言有超過100個的標準包(譯註:可以用`go list std | wc -l`命令査看標準包的具體數目),標準庫爲大多數的程序提供了必要的基礎構件。在Go的社區,有很多成熟的包被設計、共享、重用和改進,目前互聯網上已經發布了非常多的Go語音開源包,它們可以通過 http://godoc.org 檢索。在本章,我們將演示如果使用已有的包和創建新的包。 6 | 7 | Go還自帶了工具箱,里面有很多用來簡化工作區和包管理的小工具。在本書開始的時候,我們已經見識過如何使用工具箱自帶的工具來下載、構件和運行我們的演示程序了。在本章,我們將看看這些工具的基本設計理論和嚐試更多的功能,例如打印工作區中包的文檔和査詢相關的元數據等。在下一章,我們將探討探索包的單元測試用法。 8 | -------------------------------------------------------------------------------- /ch11/ch11-01.md: -------------------------------------------------------------------------------- 1 | ## 11.1. go test 2 | 3 | go test命令是一個按照一定的約定和組織的測試代碼的驅動程序。在包目録內,所有以_test.go爲後綴名的源文件併不是go build構建包的一部分,它們是go test測試的一部分。 4 | 5 | 在\*_test.go文件中,有三種類型的函數:測試函數、基準測試函數、示例函數。一個測試函數是以Test爲函數名前綴的函數,用於測試程序的一些邏輯行爲是否正確;go test命令會調用這些測試函數併報告測試結果是PASS或FAIL。基準測試函數是以Benchmark爲函數名前綴的函數,它們用於衡量一些函數的性能;go test命令會多次運行基準函數以計算一個平均的執行時間。示例函數是以Example爲函數名前綴的函數,提供一個由編譯器保證正確性的示例文檔。我們將在11.2節討論測試函數的所有細節,病在11.4節討論基準測試函數的細節,然後在11.6節討論示例函數的細節。 6 | 7 | go test命令會遍歷所有的\*_test.go文件中符合上述命名規則的函數,然後生成一個臨時的main包用於調用相應的測試函數,然後構建併運行、報告測試結果,最後清理測試中生成的臨時文件。 8 | -------------------------------------------------------------------------------- /ch11/ch11-02-1.md: -------------------------------------------------------------------------------- 1 | ### 11.2.1. 隨機測試 2 | 3 | 4 | 表格驅動的測試便於構造基於精心挑選的測試數據的測試用例. 另一種測試思路是隨機測試, 也就是通過構造更廣泛的隨機輸入來測試探索函數的行爲. 5 | 6 | 那麽對於一個隨機的輸入, 我們如何能知道希望的輸出結果呢? 這里有兩種策略. 第一個是編寫另一個函數, 使用簡單和清晰的算法, 雖然效率較低但是行爲和要測試的函數一致, 然後針對相同的隨機輸入檢査兩者的輸出結果. 第二種是生成的隨機輸入的數據遵循特定的模式, 這樣我們就可以知道期望的輸出的模式. 7 | 8 | 下面的例子使用的是第二種方法: randomPalindrome 函數用於隨機生成迴文字符串. 9 | 10 | ```Go 11 | import "math/rand" 12 | 13 | // randomPalindrome returns a palindrome whose length and contents 14 | // are derived from the pseudo-random number generator rng. 15 | func randomPalindrome(rng *rand.Rand) string { 16 | n := rng.Intn(25) // random length up to 24 17 | runes := make([]rune, n) 18 | for i := 0; i < (n+1)/2; i++ { 19 | r := rune(rng.Intn(0x1000)) // random rune up to '\u0999' 20 | runes[i] = r 21 | runes[n-1-i] = r 22 | } 23 | return string(runes) 24 | } 25 | 26 | func TestRandomPalindromes(t *testing.T) { 27 | // Initialize a pseudo-random number generator. 28 | seed := time.Now().UTC().UnixNano() 29 | t.Logf("Random seed: %d", seed) 30 | rng := rand.New(rand.NewSource(seed)) 31 | 32 | 33 | for i := 0; i < 1000; i++ { 34 | p := randomPalindrome(rng) 35 | if !IsPalindrome(p) { 36 | t.Errorf("IsPalindrome(%q) = false", p) 37 | } 38 | } 39 | } 40 | ``` 41 | 42 | 雖然隨機測試有不確定因素, 但是它也是至關重要的, 我們可以從失敗測試的日誌獲取足夠的信息. 在我們的例子中, 輸入 IsPalindrome 的 p 參數將告訴我們眞實的數據, 但是對於函數將接受更複雜的輸入, 不需要保存所有的輸入, 隻要日誌中簡單地記録隨機數種子卽可(像上面的方式). 有了這些隨機數初始化種子, 我們可以很容易脩改測試代碼以重現失敗的隨機測試. 43 | 44 | 通過使用當前時間作爲隨機種子, 在整個過程中的每次運行測試命令時都將探索新的隨機數據. 如果你使用的是定期運行的自動化測試集成繫統, 隨機測試將特别有價值. 45 | 46 | **練習 11.3:** TestRandomPalindromes 隻測試了迴文字符串. 編寫新的隨機測試生成器, 用於測試隨機生成的非迴文字符串. 47 | 48 | **練習 11.4:** 脩改 randomPalindrome 函數, 以探索 IsPalindrome 對標點和空格的處理. 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /ch11/ch11-02-6.md: -------------------------------------------------------------------------------- 1 | ### 11.2.6. 避免的不穩定的測試 2 | 3 | 如果一個應用程序對於新出現的但有效的輸入經常失敗説明程序不夠穩健; 同樣如果一個測試僅僅因爲聲音變化就會導致失敗也是不合邏輯的. 就像一個不夠穩健的程序會挫敗它的用戶一樣, 一個脆弱性測試同樣會激怒它的維護者. 最脆弱的測試代碼會在程序沒有任何變化的時候産生不同的結果, 時好時壞, 處理它們會耗費大量的時間但是併不會得到任何好處. 4 | 5 | 當一個測試函數産生一個複雜的輸出如一個很長的字符串, 或一個精心設計的數據結構, 或一個文件, 它可以用於和預設的‘‘golden’’結果數據對比, 用這種簡單方式寫測試是誘人的. 但是隨着項目的發展, 輸出的某些部分很可能會發生變化, 盡管很可能是一個改進的實現導致的. 而且不僅僅是輸出部分, 函數複雜複製的輸入部分可能也跟着變化了, 因此測試使用的輸入也就不在有效了. 6 | 7 | 避免脆弱測試代碼的方法是隻檢測你眞正關心的屬性. 保存測試代碼的簡潔和內部結構的穩定. 特别是對斷言部分要有所選擇. 不要檢査字符串的全匹配, 但是尋找相關的子字符串, 因爲某些子字符串在項目的發展中是比較穩定不變的. 通常編寫一個重複雜的輸出中提取必要精華信息以用於斷言是值得的, 雖然這可能會帶來很多前期的工作, 但是它可以幫助迅速及時脩複因爲項目演化而導致的不合邏輯的失敗測試. 8 | 9 | -------------------------------------------------------------------------------- /ch11/ch11-06.md: -------------------------------------------------------------------------------- 1 | ## 11.6. 示例函數 2 | 3 | 第三種 `go test` 特别處理的函數是示例函數, 以 Example 爲函數名開頭. 示例函數沒有函數參數和返迴值. 下面是 IsPalindrome 函數對應的示例函數: 4 | 5 | ```Go 6 | func ExampleIsPalindrome() { 7 | fmt.Println(IsPalindrome("A man, a plan, a canal: Panama")) 8 | fmt.Println(IsPalindrome("palindrome")) 9 | // Output: 10 | // true 11 | // false 12 | } 13 | ``` 14 | 15 | 示例函數有三個用處. 最主要的一個是用於文檔: 一個包的例子可以更簡潔直觀的方式來演示函數的用法, 會文字描述會更直接易懂, 特别是作爲一個提醒或快速參考時. 一個例子函數也可以方便展示屬於同一個接口的幾種類型或函數直接的關繫, 所有的文檔都必須關聯到一個地方, 就像一個類型或函數聲明都統一到包一樣. 同時, 示例函數和註釋併不一樣, 示例函數是完整眞是的Go代碼, 需要介紹編譯器的編譯時檢査, 這樣可以保證示例代碼不會腐爛成不能使用的舊代碼. 16 | 17 | 根據示例函數的後綴名部分, godoc 的web文檔會將一個示例函數關聯到某個具體函數或包本身, 因此 ExampleIsPalindrome 示例函數將是 IsPalindrome 函數文檔的一部分, Example 示例函數將是包文檔的一部分. 18 | 19 | 示例文檔的第二個用處是在 `go test` 執行測試的時候也運行示例函數測試. 如果示例函數內含有類似上面例子中的 `/ Output:` 這樣的註釋, 那麽測試工具會執行這個示例函數, 然後檢測這個示例函數的標準輸出和註釋是否匹配. 20 | 21 | 示例函數的第三個目的提供一個眞實的演練場. golang.org 是由 dogoc 提供的服務, 它使用了 Go Playground 技術讓用戶可以在瀏覽器中在線編輯和運行每個示例函數, 就像 圖 11.4 所示的那樣. 這通常是學習函數使用或Go語言特性的最快方式. 22 | 23 | ![](../images/ch11-04.png) 24 | 25 | 本書最後的兩掌是討論 reflect 和 unsafe 包, 一般的Go用於很少需要使用它們. 因此, 如果你還沒有寫過任何眞是的Go程序的話, 現在可以忽略剩餘部分而直接編碼了. 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /ch11/ch11.md: -------------------------------------------------------------------------------- 1 | # 第十一章 測試 2 | 3 | Maurice Wilkes,第一個存儲程序計算機EDSAC的設計者,1949年他在實驗室爬樓梯時有一個頓悟。在《計算機先驅迴憶録》(Memoirs of a Computer Pioneer)里,他迴憶到:“忽然間有一種醍醐灌頂的感覺,我整個後半生的美好時光都將在尋找程序BUG中度過了”。肯定從那之後的大部分正常的碼農都會同情Wilkes過份悲觀的想法,雖然也許不是沒有人睏惑於他對軟件開發的難度的天眞看法。 4 | 5 | 現在的程序已經遠比Wilkes時代的更大也更複雜,也有許多技術可以讓軟件的複雜性可得到控製。其中有兩種技術在實踐中證明是比較有效的。第一種是代碼在被正式部署前需要進行代碼評審。第二種則是測試,也就是本章的討論主題。 6 | 7 | 我們説測試的時候一般是指自動化測試,也就是寫一些小的程序用來檢測被測試代碼(産品代碼)的行爲和預期的一樣,這些通常都是精心設計的執行某些特定的功能或者是通過隨機性的輸入要驗證邊界的處理。 8 | 9 | 軟件測試是一個鉅大的領域。測試的任務可能已經占據了一些程序員的部分時間和另一些程序員的全部時間。和軟件測試技術相關的圖書或博客文章有成韆上萬之多。對於每一種主流的編程語言,都會有一打的用於測試的軟件包,同時也有大量的測試相關的理論,而且每種都吸引了大量技術先驅和追隨者。這些都足以説服那些想要編寫有效測試的程序員重新學習一套全新的技能。 10 | 11 | Go語言的測試技術是相對低級的。它依賴一個go test測試命令和一組按照約定方式編寫的測試函數,測試命令可以運行這些測試函數。編寫相對輕量級的純測試代碼是有效的,而且它很容易延伸到基準測試和示例文檔。 12 | 13 | 在實踐中,編寫測試代碼和編寫程序本身併沒有多大區别。我們編寫的每一個函數也是針對每個具體的任務。我們必須小心處理邊界條件,思考合適的數據結構,推斷合適的輸入應該産生什麽樣的結果輸出。編程測試代碼和編寫普通的Go代碼過程是類似的;它併不需要學習新的符號、規則和工具。 14 | -------------------------------------------------------------------------------- /ch12/ch12-01.md: -------------------------------------------------------------------------------- 1 | ## 12.1. 爲何需要反射? 2 | 3 | 4 | 有時候我們需要編寫一個函數能夠處理一類併不滿足普通公共接口的類型的值, 也可能它們併沒有確定的表示方式, 或者在我們設計該函數的時候還這些類型可能還不存在, 各種情況都有可能. 5 | 6 | 一個大家熟悉的例子是 fmt.Fprintf 函數提供的字符串格式化處理邏輯, 它可以用例對任意類型的值格式化打印, 甚至是用戶自定義的類型. 讓我們來嚐試實現一個類似功能的函數. 簡單起見, 我們的函數隻接收一個參數, 然後返迴和 fmt.Sprint 類似的格式化後的字符串, 我們的函數名也叫 Sprint. 7 | 8 | 我們使用了 switch 分支首先來測試輸入參數是否實現了 String 方法, 如果是的話就使用該方法. 然後繼續增加測試分支, 檢査是否是每個基於 string, int, bool 等基礎類型的動態類型, 併在每種情況下執行適當的格式化操作. 9 | 10 | ```Go 11 | func Sprint(x interface{}) string { 12 | type stringer interface { 13 | String() string 14 | } 15 | switch x := x.(type) { 16 | case stringer: 17 | return x.String() 18 | case string: 19 | return x 20 | case int: 21 | return strconv.Itoa(x) 22 | // ...similar cases for int16, uint32, and so on... 23 | case bool: 24 | if x { 25 | return "true" 26 | } 27 | return "false" 28 | default: 29 | // array, chan, func, map, pointer, slice, struct 30 | return "???" 31 | } 32 | } 33 | ``` 34 | 35 | 但是我們如何處理其它類似 []float64, map[string][]string 等類型呢? 我們當然可以添加更多的測試分支, 但是這些組合類型的數目基本是無窮的. 還有如何處理 url.Values 等命令的類型呢? 雖然類型分支可以識别出底層的基礎類型是 map[string][]string, 但是它併不匹配 url.Values 類型, 因爲這是兩種不同的類型, 而且 switch 分支也不可能包含每個類似 url.Values 的類型, 這會導致對這些庫的依賴. 36 | 37 | 沒有一種方法來檢査未知類型的表示方式, 我們被卡住了. 這就是我們爲何需要反射的原因. 38 | 39 | 40 | -------------------------------------------------------------------------------- /ch12/ch12-08.md: -------------------------------------------------------------------------------- 1 | ## 12.8. 顯示一個類型的方法集 2 | 3 | 我們的最後一個例子是使用reflect.Type來打印任意值的類型和枚舉它的方法: 4 | 5 | ```Go 6 | gopl.io/ch12/methods 7 | 8 | // Print prints the method set of the value x. 9 | func Print(x interface{}) { 10 | v := reflect.ValueOf(x) 11 | t := v.Type() 12 | fmt.Printf("type %s\n", t) 13 | 14 | for i := 0; i < v.NumMethod(); i++ { 15 | methType := v.Method(i).Type() 16 | fmt.Printf("func (%s) %s%s\n", t, t.Method(i).Name, 17 | strings.TrimPrefix(methType.String(), "func")) 18 | } 19 | } 20 | ``` 21 | 22 | reflect.Type和reflect.Value都提供了一個Method方法。每次t.Method(i)調用將一個reflect.Method的實例,對應一個用於描述一個方法的名稱和類型的結構體。每次v.Method(i)方法調用都返迴一個reflect.Value以表示對應的值(§6.4),也就是一個方法是幫到它的接收者的。使用reflect.Value.Call方法(我們之類沒有演示),將可以調用一個Func類型的Value,但是這個例子中隻用到了它的類型。 23 | 24 | 這是屬於time.Duration和`*strings.Replacer`兩個類型的方法: 25 | 26 | ```Go 27 | methods.Print(time.Hour) 28 | // Output: 29 | // type time.Duration 30 | // func (time.Duration) Hours() float64 31 | // func (time.Duration) Minutes() float64 32 | // func (time.Duration) Nanoseconds() int64 33 | // func (time.Duration) Seconds() float64 34 | // func (time.Duration) String() string 35 | 36 | methods.Print(new(strings.Replacer)) 37 | // Output: 38 | // type *strings.Replacer 39 | // func (*strings.Replacer) Replace(string) string 40 | // func (*strings.Replacer) WriteString(io.Writer, string) (int, error) 41 | ```` 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /ch12/ch12-09.md: -------------------------------------------------------------------------------- 1 | ## 12.9. 幾點忠告 2 | 3 | 雖然反射提供的API遠多於我們講到的,我們前面的例子主要是給出了一個方向,通過反射可以實現哪些功能。反射是一個強大併富有表達力的工具,但是它應該被小心地使用,原因有三。 4 | 5 | 第一個原因是,基於反射的代碼是比較脆弱的。對於每一個會導致編譯器報告類型錯誤的問題,在反射中都有與之相對應的問題,不同的是編譯器會在構建時馬上報告錯誤,而反射則是在眞正運行到的時候才會拋出panic異常,可能是寫完代碼很久之後的時候了,而且程序也可能運行了很長的時間。 6 | 7 | 以前面的readList函數(§12.6)爲例,爲了從輸入讀取字符串併填充int類型的變量而調用的reflect.Value.SetString方法可能導致panic異常。絶大多數使用反射的程序都有類似的風險,需要非常小心地檢査每個reflect.Value的對於值的類型、是否可取地址,還有是否可以被脩改等。 8 | 9 | 避免這種因反射而導致的脆弱性的問題的最好方法是將所有的反射相關的使用控製在包的內部,如果可能的話避免在包的API中直接暴露reflect.Value類型,這樣可以限製一些非法輸入。如果無法做到這一點,在每個有風險的操作前指向額外的類型檢査。以標準庫中的代碼爲例,當fmt.Printf收到一個非法的操作數是,它併不會拋出panic異常,而是打印相關的錯誤信息。程序雖然還有BUG,但是會更加容易診斷。 10 | 11 | ```Go 12 | fmt.Printf("%d %s\n", "hello", 42) // "%!d(string=hello) %!s(int=42)" 13 | ``` 14 | 15 | 反射同樣降低了程序的安全性,還影響了自動化重構和分析工具的準確性,因爲它們無法識别運行時才能確認的類型信息。 16 | 17 | 避免使用反射的第二個原因是,卽使對應類型提供了相同文檔,但是反射的操作不能做靜態類型檢査,而且大量反射的代碼通常難以理解。總是需要小心翼翼地爲每個導出的類型和其它接受interface{}或reflect.Value類型參數的函數維護説明文檔。 18 | 19 | 第三個原因,基於反射的代碼通常比正常的代碼運行速度慢一到兩個數量級。對於一個典型的項目,大部分函數的性能和程序的整體性能關繫不大,所以使用反射可能會使程序更加清晰。測試是一個特别適合使用反射的場景,因爲每個測試的數據集都很小。但是對於性能關鍵路徑的函數,最好避免使用反射。 20 | 21 | -------------------------------------------------------------------------------- /ch12/ch12.md: -------------------------------------------------------------------------------- 1 | # 第十二章 反射 2 | 3 | Go提供了一種機製在運行時更新變量和檢査它們的值, 調用它們的方法, 和它們支持的內在操作, 但是在編譯時併不知道這些變量的類型. 這種機製被稱爲反射. 反射也可以讓我們將類型本身作爲第一類的值類型處理. 4 | 5 | 在本章, 我們將探討Go語言的反射特性, 看看它可以給語言增加哪些表達力, 以及在兩個至關重要的API是如何用反射機製的: 一個是 fmt 包提供的字符串格式功能, 另一個是類似 encoding/json 和 encoding/xml 提供的針對特定協議的編解碼功能. 對於我們在4.6節中看到過的 text/template 和 html/template 包, 它們的實現也是依賴反射技術的. 然後, 反射是一個複雜的內省技術, 不應該隨意使用, 因此, 盡管上面這些包都是用反射技術實現的, 但是它們自己的API都沒有公開反射相關的接口. 6 | 7 | -------------------------------------------------------------------------------- /ch13/ch13-05.md: -------------------------------------------------------------------------------- 1 | ## 13.5. 幾點忠告 2 | 3 | 我們在前一章結尾的時候,我們警告要謹慎使用reflect包。那些警告同樣適用於本章的unsafe包。 4 | 5 | 高級語言使得程序員不用在關心眞正運行程序的指令細節,同時也不再需要關註許多如內存布局之類的實現細節。因爲高級語言這個絶緣的抽象層,我們可以編寫安全健壯的,併且可以運行在不同操作繫統上的具有高度可移植性的程序。 6 | 7 | 但是unsafe包,它讓程序員可以透過這個絶緣的抽象層直接使用一些必要的功能,雖然可能是爲了獲得更好的性能。但是代價就是犧牲了可移植性和程序安全,因此使用unsafe包是一個危險的行爲。我們對何時以及如何使用unsafe包的建議和我們在11.5節提到的Knuth對過早優化的建議類似。大多數Go程序員可能永遠不會需要直接使用unsafe包。當然,也永遠都會有一些需要使用unsafe包實現會更簡單的場景。如果確實認爲使用unsafe包是最理想的方式,那麽應該盡可能將它限製在較小的范圍,那樣其它代碼就忽略unsafe的影響。 8 | 9 | 現在,趕緊將最後兩章拋入腦後吧。編寫一些實實在在的應用是眞理。請遠離reflect的unsafe包,除非你確實需要它們。 10 | 11 | 最後,用Go快樂地編程。我們希望你能像我們一樣喜歡Go語言。 12 | 13 | -------------------------------------------------------------------------------- /ch13/ch13.md: -------------------------------------------------------------------------------- 1 | # 第13章 底層編程 2 | 3 | Go語言的設計包含了諸多安全策略,限製了可能導致程序運行出現錯誤的用法。編譯時類型檢査檢査可以發現大多數類型不匹配的操作,例如兩個字符串做減法的錯誤。字符串、map、slice和chan等所有的內置類型,都有嚴格的類型轉換規則。 4 | 5 | 對於無法靜態檢測到的錯誤,例如數組訪問越界或使用空指針,運行時動態檢測可以保證程序在遇到問題的時候立卽終止併打印相關的錯誤信息。自動內存管理(垃圾內存自動迴收)可以消除大部分野指針和內存洩漏相關的問題。 6 | 7 | Go語言的實現刻意隱藏了很多底層細節。我們無法知道一個結構體眞實的內存布局,也無法獲取一個運行時函數對應的機器碼,也無法知道當前的goroutine是運行在哪個操作繫統線程之上。事實上,Go語言的調度器會自己決定是否需要將某個goroutine從一個操作繫統線程轉移到另一個操作繫統線程。一個指向變量的指針也併沒有展示變量眞實的地址。因爲垃圾迴收器可能會根據需要移動變量的內存位置,當然變量對應的地址也會被自動更新。 8 | 9 | 總的來説,Go語言的這些特性使得Go程序相比較低級的C語言來説更容易預測和理解,程序也不容易崩潰。通過隱藏底層的實現細節,也使得Go語言編寫的程序具有高度的可移植性,因爲語言的語義在很大程度上是獨立於任何編譯器實現、操作繫統和CPU繫統結構的(當然也不是完全絶對獨立:例如int等類型就依賴於CPU機器字的大小,某些表達式求值的具體順序,還有編譯器實現的一些額外的限製等)。 10 | 11 | 有時候我們可能會放棄使用部分語言特性而優先選擇更好具有更好性能的方法,例如需要與其他語言編寫的庫互操作,或者用純Go語言無法實現的某些函數。 12 | 13 | 在本章,我們將展示如何使用unsafe包來襬脫Go語言規則帶來的限製,講述如何創建C語言函數庫的綁定,以及如何進行繫統調用。 14 | 15 | 本章提供的方法不應該輕易使用(譯註:屬於黑魔法,雖然可能功能很強大,但是也容易誤傷到自己)。如果沒有處理好細節,它們可能導致各種不可預測的併且隱晦的錯誤,甚至連有經驗的的C語言程序員也無法理解這些錯誤。使用unsafe包的同時也放棄了Go語言保證與未來版本的兼容性的承諾,因爲它必然會在有意無意中會使用很多實現的細節,而這些實現的細節在未來的Go語言中很可能會被改變。 16 | 17 | 要註意的是,unsafe包是一個采用特殊方式實現的包。雖然它可以和普通包一樣的導入和使用,但它實際上是由編譯器實現的。它提供了一些訪問語言內部特性的方法,特别是內存布局相關的細節。將這些特性封裝到一個獨立的包中,是爲在極少數情況下需要使用的時候,同時引起人們的註意(譯註:因爲看包的名字就知道使用unsafe包是不安全的)。此外,有一些環境因爲安全的因素可能限製這個包的使用。 18 | 19 | 不過unsafe包被廣泛地用於比較低級的包, 例如runtime、os、syscall還有net包等,因爲它們需要和操作繫統密切配合,但是對於普通的程序一般是不需要使用unsafe包的。 20 | 21 | -------------------------------------------------------------------------------- /ch2/ch2-01.md: -------------------------------------------------------------------------------- 1 | ## 2.1. 命名 2 | 3 | Go語言中的函數名、變量名、常量名、類型名、語句標號和包名等所有的命名,都遵循一個簡單的命名規則:一個名字必須以一個字母(Unicode字母)或下劃線開頭,後面可以跟任意數量的字母、數字或下劃線。大寫字母和小寫字母是不同的:heapSort和Heapsort是兩個不同的名字。 4 | 5 | Go語言中類似if和switch的關鍵字有25個;關鍵字不能用於自定義名字,隻能在特定語法結構中使用。 6 | 7 | ``` 8 | break default func interface select 9 | case defer go map struct 10 | chan else goto package switch 11 | const fallthrough if range type 12 | continue for import return var 13 | ``` 14 | 15 | 此外,還有大約30多個預定義的名字,比如int和true等,主要對應內建的常量、類型和函數。 16 | 17 | ``` 18 | 內建常量: true false iota nil 19 | 20 | 內建類型: int int8 int16 int32 int64 21 | uint uint8 uint16 uint32 uint64 uintptr 22 | float32 float64 complex128 complex64 23 | bool byte rune string error 24 | 25 | 內建函數: make len cap new append copy close delete 26 | complex real imag 27 | panic recover 28 | ``` 29 | 30 | 這些內部預先定義的名字併不是關鍵字,你可以再定義中重新使用它們。在一些特殊的場景中重新定義它們也是有意義的,但是也要註意避免過度而引起語義混亂。 31 | 32 | 如果一個名字是在函數內部定義,那麽它的就隻在函數內部有效。如果是在函數外部定義,那麽將在當前包的所有文件中都可以訪問。名字的開頭字母的大小寫決定了名字在包外的可見性。如果一個名字是大寫字母開頭的(譯註:必須是在函數外部定義的包級名字;包級函數名本身也是包級名字),那麽它將是導出的,也就是説可以被外部的包訪問,例如fmt包的Printf函數就是導出的,可以在fmt包外部訪問。包本身的名字一般總是用小寫字母。 33 | 34 | 名字的長度沒有邏輯限製,但是Go語言的風格是盡量使用短小的名字,對於局部變量尤其是這樣;你會經常看到i之類的短名字,而不是冗長的theLoopIndex命名。通常來説,如果一個名字的作用域比較大,生命週期也比較長,那麽用長的名字將會更有意義。 35 | 36 | 在習慣上,Go語言程序員推薦使用 **駝峯式** 命名,當名字有幾個單詞組成的時優先使用大小寫分隔,而不是優先用下劃線分隔。因此,在標準庫有QuoteRuneToASCII和parseRequestLine這樣的函數命名,但是一般不會用quote_rune_to_ASCII和parse_request_line這樣的命名。而像ASCII和HTML這樣的縮略詞則避免使用大小寫混合的寫法,它們可能被稱爲htmlEscape、HTMLEscape或escapeHTML,但不會是escapeHtml。 37 | -------------------------------------------------------------------------------- /ch2/ch2-02.md: -------------------------------------------------------------------------------- 1 | ## 2.2. 聲明 2 | 3 | 聲明語句定義了程序的各種實體對象以及部分或全部的屬性。Go語言主要有四種類型的聲明語句:var、const、type和func,分别對應變量、常量、類型和函數實體對象的聲明。這一章我們重點討論變量和類型的聲明,第三章將討論常量的聲明,第五章將討論函數的聲明。 4 | 5 | 一個Go語言編寫的程序對應一個或多個以.go爲文件後綴名的源文件中。每個源文件以包的聲明語句開始,説明該源文件是屬於哪個包。包聲明語句之後是import語句導入依賴的其它包,然後是包一級的類型、變量、常量、函數的聲明語句,包一級的各種類型的聲明語句的順序無關緊要(譯註:函數內部的名字則必須先聲明之後才能使用)。例如,下面的例子中聲明了一個常量、一個函數和兩個變量: 6 | 7 | ```Go 8 | gopl.io/ch2/boiling 9 | // Boiling prints the boiling point of water. 10 | package main 11 | 12 | import "fmt" 13 | 14 | const boilingF = 212.0 15 | 16 | func main() { 17 | var f = boilingF 18 | var c = (f - 32) * 5 / 9 19 | fmt.Printf("boiling point = %g°F or %g°C\n", f, c) 20 | // Output: 21 | // boiling point = 212°F or 100°C 22 | } 23 | ``` 24 | 25 | 其中常量boilingF是在包一級范圍聲明語句聲明的,然後f和c兩個變量是在main函數內部聲明的聲明語句聲明的。在包一級聲明語句聲明的名字可在整個包對應的每個源文件中訪問,而不是僅僅在其聲明語句所在的源文件中訪問。相比之下,局部聲明的名字就隻能在函數內部很小的范圍被訪問。 26 | 27 | 一個函數的聲明由一個函數名字、參數列表(由函數的調用者提供參數變量的具體值)、一個可選的返迴值列表和包含函數定義的函數體組成。如果函數沒有返迴值,那麽返迴值列表是省略的。執行函數從函數的第一個語句開始,依次順序執行直到遇到renturn返迴語句,如果沒有返迴語句則是執行到函數末尾,然後返迴到函數調用者。 28 | 29 | 我們已經看到過很多函數聲明和函數調用的例子了,在第五章將深入討論函數的相關細節,這里隻簡單解釋下。下面的fToC函數封裝了溫度轉換的處理邏輯,這樣它隻需要被定義一次,就可以在多個地方多次被使用。在這個例子中,main函數就調用了兩次fToC函數,分别是使用在局部定義的兩個常量作爲調用函數的參數。 30 | 31 | 32 | ```Go 33 | gopl.io/ch2/ftoc 34 | // Ftoc prints two Fahrenheit-to-Celsius conversions. 35 | package main 36 | 37 | import "fmt" 38 | 39 | func main() { 40 | const freezingF, boilingF = 32.0, 212.0 41 | fmt.Printf("%g°F = %g°C\n", freezingF, fToC(freezingF)) // "32°F = 0°C" 42 | fmt.Printf("%g°F = %g°C\n", boilingF, fToC(boilingF)) // "212°F = 100°C" 43 | } 44 | 45 | func fToC(f float64) float64 { 46 | return (f - 32) * 5 / 9 47 | } 48 | ``` 49 | 50 | -------------------------------------------------------------------------------- /ch2/ch2-03-3.md: -------------------------------------------------------------------------------- 1 | ### 2.3.3. new函數 2 | 3 | 另一個創建變量的方法是調用用內建的new函數。表達式new(T)將創建一個T類型的匿名變量,初始化爲T類型的零值,然後返迴變量地址,返迴的指針類型爲`*T`。 4 | 5 | ```Go 6 | p := new(int) // p, *int 類型, 指向匿名的 int 變量 7 | fmt.Println(*p) // "0" 8 | *p = 2 // 設置 int 匿名變量的值爲 2 9 | fmt.Println(*p) // "2" 10 | ``` 11 | 12 | 用new創建變量和普通變量聲明語句方式創建變量沒有什麽區别,除了不需要聲明一個臨時變量的名字外,我們還可以在表達式中使用new(T)。換言之,new函數類似是一種語法糖,而不是一個新的基礎概念。 13 | 14 | 下面的兩個newInt函數有着相同的行爲: 15 | 16 | ```Go 17 | func newInt() *int { 18 | return new(int) 19 | } 20 | 21 | func newInt() *int { 22 | var dummy int 23 | return &dummy 24 | } 25 | ``` 26 | 27 | 每次調用new函數都是返迴一個新的變量的地址,因此下面兩個地址是不同的: 28 | 29 | ```Go 30 | p := new(int) 31 | q := new(int) 32 | fmt.Println(p == q) // "false" 33 | ``` 34 | 35 | 當然也可能有特殊情況:如果兩個類型都是空的,也就是説類型的大小是0,例如`struct{}`和 `[0]int`, 有可能有相同的地址(依賴具體的語言實現)(譯註:請謹慎使用大小爲0的類型,因爲如果類型的大小位0好話,可能導致Go語言的自動垃圾迴收器有不同的行爲,具體請査看`runtime.SetFinalizer`函數相關文檔)。 36 | 37 | new函數使用常見相對比較少,因爲對應結構體來説,可以直接用字面量語法創建新變量的方法會更靈活(§4.4.1)。 38 | 39 | 由於new隻是一個預定義的函數,它併不是一個關鍵字,因此我們可以將new名字重新定義爲别的類型。例如下面的例子: 40 | 41 | ```Go 42 | func delta(old, new int) int { return new - old } 43 | ``` 44 | 45 | 由於new被定義爲int類型的變量名,因此在delta函數內部是無法使用內置的new函數的。 46 | 47 | -------------------------------------------------------------------------------- /ch2/ch2-03.md: -------------------------------------------------------------------------------- 1 | ## 2.3. 變量 2 | 3 | var聲明語句可以創建一個特定類型的變量,然後給變量附加一個名字,併且設置變量的初始值。變量聲明的一般語法如下: 4 | 5 | ```Go 6 | var 變量名字 類型 = 表達式 7 | ``` 8 | 9 | 其中“*類型*”或“*= 表達式*”兩個部分可以省略其中的一個。如果省略的是類型信息,那麽將根據初始化表達式來推導變量的類型信息。如果初始化表達式被省略,那麽將用零值初始化該變量。 數值類型變量對應的零值是0,布爾類型變量對應的零值是false,字符串類型對應的零值是空字符串,接口或引用類型(包括slice、map、chan和函數)變量對應的零值是nil。數組或結構體等聚合類型對應的零值是每個元素或字段都是對應該類型的零值。 10 | 11 | 零值初始化機製可以確保每個聲明的變量總是有一個良好定義的值,因此在Go語言中不存在未初始化的變量。這個特性可以簡化很多代碼,而且可以在沒有增加額外工作的前提下確保邊界條件下的合理行爲。例如: 12 | 13 | ```Go 14 | var s string 15 | fmt.Println(s) // "" 16 | ``` 17 | 18 | 這段代碼將打印一個空字符串,而不是導致錯誤或産生不可預知的行爲。Go語言程序員應該讓一些聚合類型的零值也具有意義,這樣可以保證不管任何類型的變量總是有一個合理有效的零值狀態。 19 | 20 | 也可以在一個聲明語句中同時聲明一組變量,或用一組初始化表達式聲明併初始化一組變量。如果省略每個變量的類型,將可以聲明多個類型不同的變量(類型由初始化表達式推導): 21 | 22 | ```Go 23 | var i, j, k int // int, int, int 24 | var b, f, s = true, 2.3, "four" // bool, float64, string 25 | ``` 26 | 27 | 初始化表達式可以是字面量或任意的表達式。在包級别聲明的變量會在main入口函數執行前完成初始化(§2.6.2),局部變量將在聲明語句被執行到的時候完成初始化。 28 | 29 | 一組變量也可以通過調用一個函數,由函數返迴的多個返迴值初始化: 30 | 31 | ```Go 32 | var f, err = os.Open(name) // os.Open returns a file and an error 33 | ``` 34 | 35 | {% include "./ch2-03-1.md" %} 36 | 37 | {% include "./ch2-03-2.md" %} 38 | 39 | {% include "./ch2-03-3.md" %} 40 | 41 | {% include "./ch2-03-4.md" %} 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /ch2/ch2-04-2.md: -------------------------------------------------------------------------------- 1 | ### 2.4.2. 可賦值性 2 | 3 | 賦值語句是顯式的賦值形式,但是程序中還有很多地方會發生隱式的賦值行爲:函數調用會隱式地將調用參數的值賦值給函數的參數變量,一個返迴語句將隱式地將返迴操作的值賦值給結果變量,一個複合類型的字面量(§4.2)也會産生賦值行爲。例如下面的語句: 4 | 5 | ```Go 6 | medals := []string{"gold", "silver", "bronze"} 7 | ``` 8 | 9 | 隱式地對slice的每個元素進行賦值操作,類似這樣寫的行爲: 10 | 11 | ```Go 12 | medals[0] = "gold" 13 | medals[1] = "silver" 14 | medals[2] = "bronze" 15 | ``` 16 | 17 | map和chan的元素,雖然不是普通的變量,但是也有類似的隱式賦值行爲。 18 | 19 | 不管是隱式還是顯式地賦值,在賦值語句左邊的變量和右邊最終的求到的值必須有相同的數據類型。更直白地説,隻有右邊的值對於左邊的變量是可賦值的,賦值語句才是允許的。 20 | 21 | 可賦值性的規則對於不同類型有着不同要求,對每個新類型特殊的地方我們會專門解釋。對於目前我們已經討論過的類型,它的規則是簡單的:類型必須完全匹配,nil可以賦值給任何指針或引用類型的變量。常量(§3.6)則有更靈活的賦值規則,因爲這樣可以避免不必要的顯式的類型轉換。 22 | 23 | 對於兩個值是否可以用`==`或`!=`進行相等比較的能力也和可賦值能力有關繫:對於任何類型的值的相等比較,第二個值必須是對第一個值類型對應的變量是可賦值的,反之依然。和前面一樣,我們會對每個新類型比較特殊的地方做專門的解釋。 24 | 25 | 26 | -------------------------------------------------------------------------------- /ch2/ch2-04.md: -------------------------------------------------------------------------------- 1 | ## 2.4. 賦值 2 | 3 | 使用賦值語句可以更新一個變量的值,最簡單的賦值語句是將要被賦值的變量放在=的左邊,新值的表達式放在=的右邊。 4 | 5 | ```Go 6 | x = 1 // 命令變量的賦值 7 | *p = true // 通過指針間接賦值 8 | person.name = "bob" // 結構體字段賦值 9 | count[x] = count[x] * scale // 數組、slice或map的元素賦值 10 | ``` 11 | 12 | 特定的二元算術運算符和賦值語句的複合操作有一個簡潔形式,例如上面最後的語句可以重寫爲: 13 | 14 | ```Go 15 | count[x] *= scale 16 | ``` 17 | 18 | 這樣可以省去對變量表達式的重複計算。 19 | 20 | 數值變量也可以支持`++`遞增和`--`遞減語句(譯註:自增和自減是語句,而不是表達式,因此`x = i++`之類的表達式是錯誤的): 21 | 22 | ```Go 23 | v := 1 24 | v++ // 等價方式 v = v + 1;v 變成 2 25 | v-- // 等價方式 v = v - 1;v 變成 1 26 | ``` 27 | 28 | {% include "./ch2-04-1.md" %} 29 | 30 | {% include "./ch2-04-2.md" %} 31 | 32 | -------------------------------------------------------------------------------- /ch2/ch2.md: -------------------------------------------------------------------------------- 1 | # 第2章 程序結構 2 | 3 | Go語言和其他編程語言一樣,一個大的程序是由很多小的基礎構件組成的。變量保存值,簡單的加法和減法運算被組合成較複雜的表達式。基礎類型被聚合爲數組或結構體等更複雜的數據結構。然後使用if和for之類的控製語句來組織和控製表達式的執行流程。然後多個語句被組織到一個個函數中,以便代碼的隔離和複用。函數以源文件和包的方式被組織。 4 | 5 | 我們已經在前面章節的例子中看到了很多例子。在本章中,我們將深入討論Go程序基礎結構方面的一些細節。每個示例程序都是刻意寫的簡單,這樣我們可以減少複雜的算法或數據結構等不相關的問題帶來的榦擾,從而可以專註於Go語言本身的學習。 6 | -------------------------------------------------------------------------------- /ch3/ch3-04.md: -------------------------------------------------------------------------------- 1 | ## 3.4. 布爾型 2 | 3 | 一個布爾類型的值隻有兩種:true和false。if和for語句的條件部分都是布爾類型的值,併且==和<等比較操作也會産生布爾型的值。一元操作符`!`對應邏輯非操作,因此`!true`的值爲`false`,更羅嗦的説法是`(!true==false)==true`,雖然表達方式不一樣,不過我們一般會采用簡潔的布爾表達式,就像用x來表示`x==true`。 4 | 5 | 布爾值可以和&&(AND)和||(OR)操作符結合,併且可能會有短路行爲:如果運算符左邊值已經可以確定整個布爾表達式的值,那麽運算符右邊的值將不在被求值,因此下面的表達式總是安全的: 6 | 7 | ```Go 8 | s != "" && s[0] == 'x' 9 | ``` 10 | 11 | 其中s[0]操作如果應用於空字符串將會導致panic異常。 12 | 13 | 因爲`&&`的優先級比`||`高(助記:`&&`對應邏輯乘法,`||`對應邏輯加法,乘法比加法優先級要高),下面形式的布爾表達式是不需要加小括弧的: 14 | 15 | ```Go 16 | if 'a' <= c && c <= 'z' || 17 | 'A' <= c && c <= 'Z' || 18 | '0' <= c && c <= '9' { 19 | // ...ASCII letter or digit... 20 | } 21 | ``` 22 | 23 | 布爾值併不會隱式轉換爲數字值0或1,反之亦然。必須使用一個顯式的if語句輔助轉換: 24 | 25 | ```Go 26 | i := 0 27 | if b { 28 | i = 1 29 | } 30 | ``` 31 | 32 | 如果需要經常做類似的轉換, 包裝成一個函數會更方便: 33 | 34 | ```Go 35 | // btoi returns 1 if b is true and 0 if false. 36 | func btoi(b bool) int { 37 | if b { 38 | return 1 39 | } 40 | return 0 41 | } 42 | ``` 43 | 44 | 數字到布爾型的逆轉換則非常簡單, 不過爲了保持對稱, 我們也可以包裝一個函數: 45 | 46 | ```Go 47 | // itob reports whether i is non-zero. 48 | func itob(i int) bool { return i != 0 } 49 | ``` 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /ch3/ch3-05-1.md: -------------------------------------------------------------------------------- 1 | ### 3.5.1. 字符串面值 2 | 3 | 字符串值也可以用字符串面值方式編寫,隻要將一繫列字節序列包含在雙引號卽可: 4 | 5 | ``` 6 | "Hello, 世界" 7 | ``` 8 | 9 | ![](../images/ch3-04.png) 10 | 11 | 因爲Go語言源文件總是用UTF8編碼,併且Go語言的文本字符串也以UTF8編碼的方式處理,因此我們可以將Unicode碼點也寫到字符串面值中。 12 | 13 | 在一個雙引號包含的字符串面值中,可以用以反斜槓`\`開頭的轉義序列插入任意的數據。下面的換行、迴車和製表符等是常見的ASCII控製代碼的轉義方式: 14 | 15 | ``` 16 | \a 響鈴 17 | \b 退格 18 | \f 換頁 19 | \n 換行 20 | \r 迴車 21 | \t 製表符 22 | \v 垂直製表符 23 | \' 單引號 (隻用在 '\'' 形式的rune符號面值中) 24 | \" 雙引號 (隻用在 "..." 形式的字符串面值中) 25 | \\ 反斜槓 26 | ``` 27 | 28 | 可以通過十六進製或八進製轉義在字符串面值包含任意的字節。一個十六進製的轉義形式是\xhh,其中兩個h表示十六進製數字(大寫或小寫都可以)。一個八進製轉義形式是\ooo,包含三個八進製的o數字(0到7),但是不能超過`\377`(譯註:對應一個字節的范圍,十進製爲255)。每一個單一的字節表達一個特定的值。稍後我們將看到如何將一個Unicode碼點寫到字符串面值中。 29 | 30 | 一個原生的字符串面值形式是`...`,使用反引號```代替雙引號。在原生的字符串面值中,沒有轉義操作;全部的內容都是字面的意思,包含退格和換行,因此一個程序中的原生字符串面值可能跨越多行(譯註:在原生字符串面值內部是無法直接寫```字符的,可以用八進製或十六進製轉義或+"```"鏈接字符串常量完成)。唯一的特殊處理是會刪除迴車以保證在所有平台上的值都是一樣的,包括那些把迴車也放入文本文件的繫統(譯註:Windows繫統會把迴車和換行一起放入文本文件中)。 31 | 32 | 原生字符串面值用於編寫正則表達式會很方便,因爲正則表達式往往會包含很多反斜槓。原生字符串面值同時被廣泛應用於HTML模闆、JSON面值、命令行提示信息以及那些需要擴展到多行的場景。 33 | 34 | ```Go 35 | const GoUsage = `Go is a tool for managing Go source code. 36 | 37 | Usage: 38 | go command [arguments] 39 | ...` 40 | ``` 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ch3/ch3-05-2.md: -------------------------------------------------------------------------------- 1 | ### 3.5.2. Unicode 2 | 3 | 在很久以前,世界還是比較簡單的,起碼計算機世界就隻有一個ASCII字符集:美国信息交換標準代碼。ASCII,更準確地説是美国的ASCII,使用7bit來表示128個字符:包含英文字母的大小寫、數字、各種標點符號和設置控製符。對於早期的計算機程序來説,這些就足夠了,但是這也導致了世界上很多其他地區的用戶無法直接使用自己的符號繫統。隨着互聯網的發展,混合多種語言的數據變得很常見(譯註:比如本身的英文原文或中文翻譯都包含了ASCII、中文、日文等多種語言字符)。如何有效處理這些包含了各種語言的豐富多樣的文本數據呢? 4 | 5 | 答案就是使用Unicode( http://unicode.org ),它收集了這個世界上所有的符號繫統,包括重音符號和其它變音符號,製表符和迴車符,還有很多神祕的符號,每個符號都分配一個唯一的Unicode碼點,Unicode碼點對應Go語言中的rune整數類型(譯註:rune是int32等價類型)。 6 | 7 | 在第八版本的Unicode標準收集了超過120,000個字符,涵蓋超過100多種語言。這些在計算機程序和數據中是如何體現的呢?通用的表示一個Unicode碼點的數據類型是int32,也就是Go語言中rune對應的類型;它的同義詞rune符文正是這個意思。 8 | 9 | 我們可以將一個符文序列表示爲一個int32序列。這種編碼方式叫UTF-32或UCS-4,每個Unicode碼點都使用同樣的大小32bit來表示。這種方式比較簡單統一,但是它會浪費很多存儲空間,因爲大數據計算機可讀的文本是ASCII字符,本來每個ASCII字符隻需要8bit或1字節就能表示。而且卽使是常用的字符也遠少於65,536個,也就是説用16bit編碼方式就能表達常用字符。但是,還有其它更好的編碼方法嗎? 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /ch3/ch3-05-5.md: -------------------------------------------------------------------------------- 1 | ### 3.5.5. 字符串和數字的轉換 2 | 3 | 除了字符串、字符、字節之間的轉換,字符串和數值之間的轉換也比較常見。由strconv包提供這類轉換功能。 4 | 5 | 將一個整數轉爲字符串,一種方法是用fmt.Sprintf返迴一個格式化的字符串;另一個方法是用strconv.Itoa(“整數到ASCII”): 6 | 7 | ```Go 8 | x := 123 9 | y := fmt.Sprintf("%d", x) 10 | fmt.Println(y, strconv.Itoa(x)) // "123 123" 11 | ``` 12 | 13 | FormatInt和FormatUint函數可以用不同的進製來格式化數字: 14 | 15 | ```Go 16 | fmt.Println(strconv.FormatInt(int64(x), 2)) // "1111011" 17 | ``` 18 | 19 | fmt.Printf函數的%b、%d、%o和%x等參數提供功能往往比strconv包的Format函數方便很多,特别是在需要包含附加額外信息的時候: 20 | 21 | ```Go 22 | s := fmt.Sprintf("x=%b", x) // "x=1111011" 23 | ``` 24 | 25 | 如果要將一個字符串解析爲整數,可以使用strconv包的Atoi或ParseInt函數,還有用於解析無符號整數的ParseUint函數: 26 | 27 | ```Go 28 | x, err := strconv.Atoi("123") // x is an int 29 | y, err := strconv.ParseInt("123", 10, 64) // base 10, up to 64 bits 30 | ``` 31 | 32 | ParseInt函數的第三個參數是用於指定整型數的大小;例如16表示int16,0則表示int。在任何情況下,返迴的結果y總是int64類型,你可以通過強製類型轉換將它轉爲更小的整數類型。 33 | 34 | 有時候也會使用fmt.Scanf來解析輸入的字符串和數字,特别是當字符串和數字混合在一行的時候,它可以靈活處理不完整或不規則的輸入。 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /ch3/ch3.md: -------------------------------------------------------------------------------- 1 | # 第3章 基礎數據類型 2 | 3 | 雖然從底層而言,所有的數據都是由比特組成,但計算機一般操作的是固定大小的數,如整數、浮點數、比特數組、內存地址等。進一步將這些數組織在一起,就可表達更多的對象,例如數據包、像素點、詩歌,甚至其他任何對象。Go語言提供了豐富的數據組織形式,這依賴於Go語言內置的數據類型。這些內置的數據類型,兼顧了硬件的特性和表達複雜數據結構的便捷性。 4 | 5 | Go語言將數據類型分爲四類:基礎類型、複合類型、引用類型和接口類型。本章介紹基礎類型,包括:數字、字符串和布爾型。複合數據類型——數組(§4.1)和結構體(§4.2)——是通過組合簡單類型,來表達更加複雜的數據結構。引用類型包括指針(§2.3.2)、切片(§4.2))字典(§4.3)、函數(§5)、通道(§8),雖然數據種類很多,但它們都是對程序中一個變量或狀態的間接引用。這意味着對任一引用類型數據的脩改都會影響所有該引用的拷貝。我們將在第7章介紹接口類型。 6 | -------------------------------------------------------------------------------- /ch4/ch4-04-2.md: -------------------------------------------------------------------------------- 1 | ### 4.4.2. 結構體比較 2 | 3 | 如果結構體的全部成員都是可以比較的,那麽結構體也是可以比較的,那樣的話兩個結構體將可以使用==或!=運算符進行比較。相等比較運算符==將比較兩個結構體的每個成員,因此下面兩個比較的表達式是等價的: 4 | 5 | ```Go 6 | type Point struct{ X, Y int } 7 | 8 | p := Point{1, 2} 9 | q := Point{2, 1} 10 | fmt.Println(p.X == q.X && p.Y == q.Y) // "false" 11 | fmt.Println(p == q) // "false" 12 | ``` 13 | 14 | 可比較的結構體類型和其他可比較的類型一樣,可以用於map的key類型。 15 | 16 | ```Go 17 | type address struct { 18 | hostname string 19 | port int 20 | } 21 | 22 | hits := make(map[address]int) 23 | hits[address{"golang.org", 443}]++ 24 | ``` 25 | 26 | -------------------------------------------------------------------------------- /ch4/ch4.md: -------------------------------------------------------------------------------- 1 | # 第四章 複合數據類型 2 | 3 | 在第三章我們討論了基本數據類型,它們可以用於構建程序中數據結構,是Go語言的世界的原子。在本章,我們將討論複合數據類型,它是以不同的方式組合基本類型可以構造出來的複合數據類型。我們主要討論四種類型——數組、slice、map和結構體——同時在本章的最後,我們將演示如何使用結構體來解碼和編碼到對應JSON格式的數據,併且通過結合使用模闆來生成HTML頁面。 4 | 5 | 數組和結構體是聚合類型;它們的值由許多元素或成員字段的值組成。數組是由同構的元素組成——每個數組元素都是完全相同的類型——結構體則是由異構的元素組成的。數組和結構體都是有固定內存大小的數據結構。相比之下,slice和map則是動態的數據結構,它們將根據需要動態增長。 6 | 7 | -------------------------------------------------------------------------------- /ch5/ch5-04-2.md: -------------------------------------------------------------------------------- 1 | ### 5.4.2. 文件結尾錯誤(EOF) 2 | 3 | 函數經常會返迴多種錯誤,這對終端用戶來説可能會很有趣,但對程序而言,這使得情況變得複雜。很多時候,程序必須根據錯誤類型,作出不同的響應。讓我們考慮這樣一個例子:從文件中讀取n個字節。如果n等於文件的長度,讀取過程的任何錯誤都表示失敗。如果n小於文件的長度,調用者會重複的讀取固定大小的數據直到文件結束。這會導致調用者必須分别處理由文件結束引起的各種錯誤。基於這樣的原因,io包保證任何由文件結束引起的讀取失敗都返迴同一個錯誤——io.EOF,該錯誤在io包中定義: 4 | 5 | ```Go 6 | package io 7 | 8 | import "errors" 9 | 10 | // EOF is the error returned by Read when no more input is available. 11 | var EOF = errors.New("EOF") 12 | ``` 13 | 14 | 調用者隻需通過簡單的比較,就可以檢測出這個錯誤。下面的例子展示了如何從標準輸入中讀取字符,以及判斷文件結束。(4.3的chartcount程序展示了更加複雜的代碼) 15 | 16 | ```Go 17 | in := bufio.NewReader(os.Stdin) 18 | for { 19 | r, _, err := in.ReadRune() 20 | if err == io.EOF { 21 | break // finished reading 22 | } 23 | if err != nil { 24 | return fmt.Errorf("read failed:%v", err) 25 | } 26 | // ...use r… 27 | } 28 | ``` 29 | 30 | 因爲文件結束這種錯誤不需要更多的描述,所以io.EOF有固定的錯誤信息——“EOF”。對於其他錯誤,我們可能需要在錯誤信息中描述錯誤的類型和數量,這使得我們不能像io.EOF一樣采用固定的錯誤信息。在7.11節中,我們會提出更繫統的方法區分某些固定的錯誤值。 31 | -------------------------------------------------------------------------------- /ch5/ch5-06-1.md: -------------------------------------------------------------------------------- 1 | ### 5.6.1. 警告:捕獲迭代變量 2 | 3 | 本節,將介紹Go詞法作用域的一個陷阱。請務必仔細的閲讀,弄清楚發生問題的原因。卽使是經驗豐富的程序員也會在這個問題上犯錯誤。 4 | 5 | 考慮這個樣一個問題:你被要求首先創建一些目録,再將目録刪除。在下面的例子中我們用函數值來完成刪除操作。下面的示例代碼需要引入os包。爲了使代碼簡單,我們忽略了所有的異常處理。 6 | 7 | ```Go 8 | var rmdirs []func() 9 | for _, d := range tempDirs() { 10 | dir := d // NOTE: necessary! 11 | os.MkdirAll(dir, 0755) // creates parent directories too 12 | rmdirs = append(rmdirs, func() { 13 | os.RemoveAll(dir) 14 | }) 15 | } 16 | // ...do some work… 17 | for _, rmdir := range rmdirs { 18 | rmdir() // clean up 19 | } 20 | ``` 21 | 22 | 你可能會感到睏惑,爲什麽要在循環體中用循環變量d賦值一個新的局部變量,而不是像下面的代碼一樣直接使用循環變量dir。需要註意,下面的代碼是錯誤的。 23 | 24 | ```go 25 | var rmdirs []func() 26 | for _, dir := range tempDirs() { 27 | os.MkdirAll(dir, 0755) 28 | rmdirs = append(rmdirs, func() { 29 | os.RemoveAll(dir) // NOTE: incorrect! 30 | }) 31 | } 32 | ``` 33 | 34 | 問題的原因在於循環變量的作用域。在上面的程序中,for循環語句引入了新的詞法塊,循環變量dir在這個詞法塊中被聲明。在該循環中生成的所有函數值都共享相同的循環變量。需要註意,函數值中記録的是循環變量的內存地址,而不是循環變量某一時刻的值。以dir爲例,後續的迭代會不斷更新dir的值,當刪除操作執行時,for循環已完成,dir中存儲的值等於最後一次迭代的值。這意味着,每次對os.RemoveAll的調用刪除的都是相同的目録。 35 | 36 | 通常,爲了解決這個問題,我們會引入一個與循環變量同名的局部變量,作爲循環變量的副本。比如下面的變量dir,雖然這看起來很奇怪,但卻很有用。 37 | 38 | ```Go 39 | for _, dir := range tempDirs() { 40 | dir := dir // declares inner dir, initialized to outer dir 41 | // ... 42 | } 43 | ``` 44 | 45 | 這個問題不僅存在基於range的循環,在下面的例子中,對循環變量i的使用也存在同樣的問題: 46 | 47 | ```Go 48 | var rmdirs []func() 49 | dirs := tempDirs() 50 | for i := 0; i < len(dirs); i++ { 51 | os.MkdirAll(dirs[i], 0755) // OK 52 | rmdirs = append(rmdirs, func() { 53 | os.RemoveAll(dirs[i]) // NOTE: incorrect! 54 | }) 55 | } 56 | ``` 57 | 58 | 如果你使用go語句(第八章)或者defer語句(5.8節)會經常遇到此類問題。這不是go或defer本身導致的,而是因爲它們都會等待循環結束後,再執行函數值。 59 | -------------------------------------------------------------------------------- /ch5/ch5.md: -------------------------------------------------------------------------------- 1 | # 第五章 函數 2 | 3 | 函數可以讓我們將一個語句序列打包爲一個單元,然後可以從程序中其它地方多次調用。函數的機製可以讓我們將一個大的工作分解爲小的任務,這樣的小任務可以讓不同程序員在不同時間、不同地方獨立完成。一個函數同時對用戶隱藏了其實現細節。由於這些因素,對於任何編程語言來説,函數都是一個至關重要的部分。 4 | 5 | 我們已經見過許多函數了。現在,讓我們多花一點時間來徹底地討論函數特性。本章的運行示例是一個網絡蜘蛛,也就是web蒐索引擎中負責抓取網頁部分的組件,它們根據抓取網頁中的鏈接繼續抓取鏈接指向的頁面。一個網絡蜘蛛的例子給我們足夠的機會去探索遞歸函數、匿名函數、錯誤處理和函數其它的很多特性。 6 | 7 | -------------------------------------------------------------------------------- /ch6/ch6.md: -------------------------------------------------------------------------------- 1 | # 第六章 方法 2 | 3 | 從90年代早期開始,面向對象編程(OOP)就成爲了稱霸工程界和敎育界的編程范式,所以之後幾乎所有大規模被應用的語言都包含了對OOP的支持,go語言也不例外。 4 | 5 | 盡管沒有被大衆所接受的明確的OOP的定義,從我們的理解來講,一個對象其實也就是一個簡單的值或者一個變量,在這個對象中會包含一些方法,而一個方法則是一個一個和特殊類型關聯的函數。一個面向對象的程序會用方法來表達其屬性和對應的操作,這樣使用這個對象的用戶就不需要直接去操作對象,而是借助方法來做這些事情。 6 | 7 | 在早些的章節中,我們已經使用了標準庫提供的一些方法,比如time.Duration這個類型的Seconds方法: 8 | 9 | ```Go 10 | const day = 24 * time.Hour 11 | fmt.Println(day.Seconds()) // "86400" 12 | ``` 13 | 14 | 併且在2.5節中,我們定義了一個自己的方法,Celsius類型的String方法: 15 | 16 | ```Go 17 | func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) } 18 | ``` 19 | 20 | 在本章中,OOP編程的第一方面,我們會向你展示如何有效地定義和使用方法。我們會覆蓋到OOP編程的兩個關鍵點,封裝和組合。 21 | -------------------------------------------------------------------------------- /ch7/ch7-02.md: -------------------------------------------------------------------------------- 1 | ## 7.2. 接口類型 2 | 接口類型具體描述了一繫列方法的集合,一個實現了這些方法的具體類型是這個接口類型的實例。 3 | 4 | io.Writer類型是用的最廣泛的接口之一,因爲它提供了所有的類型寫入bytes的抽象,包括文件類型,內存緩衝區,網絡鏈接,HTTP客戶端,壓縮工具,哈希等等。io包中定義了很多其它有用的接口類型。Reader可以代表任意可以讀取bytes的類型,Closer可以是任意可以關閉的值,例如一個文件或是網絡鏈接。(到現在你可能註意到了很多Go語言中單方法接口的命名習慣) 5 | 6 | ```go 7 | package io 8 | type Reader interface { 9 | Read(p []byte) (n int, err error) 10 | } 11 | type Closer interface { 12 | Close() error 13 | } 14 | ``` 15 | 16 | 在往下看,我們發現有些新的接口類型通過組合已經有的接口來定義。下面是兩個例子: 17 | 18 | ```go 19 | type ReadWriter interface { 20 | Reader 21 | Writer 22 | } 23 | type ReadWriteCloser interface { 24 | Reader 25 | Writer 26 | Closer 27 | } 28 | ``` 29 | 上面用到的語法和結構內嵌相似,我們可以用這種方式以一個簡寫命名另一個接口,而不用聲明它所有的方法。這種方式本稱爲接口內嵌。盡管略失簡潔,我們可以像下面這樣,不使用內嵌來聲明io.Writer接口。 30 | 31 | ```go 32 | type ReadWriter interface { 33 | Read(p []byte) (n int, err error) 34 | Write(p []byte) (n int, err error) 35 | } 36 | ``` 37 | 38 | 或者甚至使用種混合的風格: 39 | 40 | ```go 41 | type ReadWriter interface { 42 | Read(p []byte) (n int, err error) 43 | Writer 44 | } 45 | ``` 46 | 47 | 上面3種定義方式都是一樣的效果。方法的順序變化也沒有影響,唯一重要的就是這個集合里面的方法。 48 | 49 | **練習 7.4:** strings.NewReader函數通過讀取一個string參數返迴一個滿足io.Reader接口類型的值(和其它值)。實現一個簡單版本的NewReader,併用它來構造一個接收字符串輸入的HTML解析器(§5.2) 50 | 51 | **練習 7.5:** io包里面的LimitReader函數接收一個io.Reader接口類型的r和字節數n,併且返迴另一個從r中讀取字節但是當讀完n個字節後就表示讀到文件結束的Reader。實現這個LimitReader函數: 52 | 53 | ```go 54 | func LimitReader(r io.Reader, n int64) io.Reader 55 | ``` 56 | -------------------------------------------------------------------------------- /ch7/ch7-05-1.md: -------------------------------------------------------------------------------- 1 | ### 7.5.1. 警告:一個包含nil指針的接口不是nil接口 2 | 3 | 一個不包含任何值的nil接口值和一個剛好包含nil指針的接口值是不同的。這個細微區别産生了一個容易絆倒每個Go程序員的陷阱。 4 | 5 | 思考下面的程序。當debug變量設置爲true時,main函數會將f函數的輸出收集到一個bytes.Buffer類型中。 6 | 7 | ```go 8 | const debug = true 9 | 10 | func main() { 11 | var buf *bytes.Buffer 12 | if debug { 13 | buf = new(bytes.Buffer) // enable collection of output 14 | } 15 | f(buf) // NOTE: subtly incorrect! 16 | if debug { 17 | // ...use buf... 18 | } 19 | } 20 | 21 | // If out is non-nil, output will be written to it. 22 | func f(out io.Writer) { 23 | // ...do something... 24 | if out != nil { 25 | out.Write([]byte("done!\n")) 26 | } 27 | } 28 | ``` 29 | 30 | 我們可能會預計當把變量debug設置爲false時可以禁止對輸出的收集,但是實際上在out.Write方法調用時程序發生了panic: 31 | 32 | ```go 33 | if out != nil { 34 | out.Write([]byte("done!\n")) // panic: nil pointer dereference 35 | } 36 | ``` 37 | 38 | 當main函數調用函數f時,它給f函數的out參數賦了一個\*bytes.Buffer的空指針,所以out的動態值是nil。然而,它的動態類型是\*bytes.Buffer,意思就是out變量是一個包含空指針值的非空接口(如圖7.5),所以防禦性檢査out!=nil的結果依然是true。 39 | 40 | ![](../images/ch7-05.png) 41 | 42 | 動態分配機製依然決定(\*bytes.Buffer).Write的方法會被調用,但是這次的接收者的值是nil。對於一些如\*os.File的類型,nil是一個有效的接收者(§6.2.1),但是\*bytes.Buffer類型不在這些類型中。這個方法會被調用,但是當它嚐試去獲取緩衝區時會發生panic。 43 | 44 | 問題在於盡管一個nil的\*bytes.Buffer指針有實現這個接口的方法,它也不滿足這個接口具體的行爲上的要求。特别是這個調用違反了(\*bytes.Buffer).Write方法的接收者非空的隱含先覺條件,所以將nil指針賦給這個接口是錯誤的。解決方案就是將main函數中的變量buf的類型改爲io.Writer,因此可以避免一開始就將一個不完全的值賦值給這個接口: 45 | 46 | ```go 47 | var buf io.Writer 48 | if debug { 49 | buf = new(bytes.Buffer) // enable collection of output 50 | } 51 | f(buf) // OK 52 | ``` 53 | 54 | 現在我們已經把接口值的技巧都講完了,讓我們來看更多的一些在Go標準庫中的重要接口類型。在下面的三章中,我們會看到接口類型是怎樣用在排序,web服務,錯誤處理中的。 55 | -------------------------------------------------------------------------------- /ch7/ch7-15.md: -------------------------------------------------------------------------------- 1 | ## 7.15. 一些建議 2 | 當設計一個新的包時,新的Go程序員總是通過創建一個接口的集合開始和後面定義滿足它們的具體類型。這種方式的結果就是有很多的接口,它們中的每一個僅隻有一個實現。不要再這麽做了。這種接口是不必要的抽象;它們也有一個運行時損耗。你可以使用導出機製(§6.6)來限製一個類型的方法或一個結構體的字段是否在包外可見。接口隻有當有兩個或兩個以上的具體類型必須以相同的方式進行處理時才需要。 3 | 4 | 當一個接口隻被一個單一的具體類型實現時有一個例外,就是由於它的依賴,這個具體類型不能和這個接口存在在一個相同的包中。這種情況下,一個接口是解耦這兩個包的一個好好方式。 5 | 6 | 因爲在Go語言中隻有當兩個或更多的類型實現一個接口時才使用接口,它們必定會從任意特定的實現細節中抽象出來。結果就是有更少和更簡單方法(經常和io.Writer或 fmt.Stringer一樣隻有一個)的更小的接口。當新的類型出現時,小的接口更容易滿足。對於接口設計的一個好的標準就是 ask only for what you need(隻考慮你需要的東西) 7 | 8 | 我們完成了對methods和接口的學習過程。Go語言良好的支持面向對象風格的編程,但隻不是説你僅僅隻能使用它。不是任何事物都需要被當做成一個對象;獨立的函數有它們自己的用處,未封裝的數據類型也是這樣。同時觀察到這兩個,在本書的前五章的例子中沒有調用超過兩打方法,像input.Scan,與之相反的是普遍的函數調用如fmt.Printf。 9 | -------------------------------------------------------------------------------- /ch7/ch7.md: -------------------------------------------------------------------------------- 1 | # 第七章 接口 2 | 3 | 接口類型是對其它類型行爲的抽象和概括;因爲接口類型不會和特定的實現細節綁定在一起,通過這種抽象的方式我們可以讓我們的函數更加靈活和更具有適應能力。 4 | 5 | 很多面向對象的語言都有相似的接口概念,但Go語言中接口類型的獨特之處在於它是滿足隱式實現的。也就是説,我們沒有必要對於給定的具體類型定義所有滿足的接口類型;簡單地擁有一些必需的方法就足夠了。這種設計可以讓你創建一個新的接口類型滿足已經存在的具體類型卻不會去改變這些類型的定義;當我們使用的類型來自於不受我們控製的包時這種設計尤其有用。 6 | 7 | 在本章,我們會開始看到接口類型和值的一些基本技巧。順着這種方式我們將學習幾個來自標準庫的重要接口。很多Go程序中都盡可能多的去使用標準庫中的接口。最後,我們會在(§7.10)看到類型斷言的知識,在(§7.13)看到類型開關的使用併且學到他們是怎樣讓不同的類型的概括成爲可能。 8 | -------------------------------------------------------------------------------- /ch8/ch8-01.md: -------------------------------------------------------------------------------- 1 | ## 8.1. Goroutines 2 | 3 | 在Go語言中,每一個併發的執行單元叫作一個goroutine。設想這里有一個程序有兩個函數,一個函數做一些計算,另一個輸出一些結果,假設兩個函數沒有相互之間的調用關繫。一個線性的程序會先調用其中的一個函數,然後再調用來一個,但如果是在有兩個甚至更多個goroutine的程序中,對兩個函數的調用就可以在同一時間。我們馬上就會看到這樣的一個程序。 4 | 5 | 如果你使用過操作繫統或者其它語言提供的線程,那麽你可以簡單地把goroutine類比作一個線程,這樣你就可以寫出一些正確的程序了。goroutine和線程的本質區别會在9.8節中講。 6 | 7 | 當一個程序啟動時,其主函數卽在一個單獨的goroutine中運行,我們叫它main goroutine。新的goroutine會用go語句來創建。在語法上,go語句是一個普通的函數或方法調用前加上關鍵字go。go語句會使其語句中的函數在一個新創建的goroutine中運行。而go語句本身會迅速地完成。 8 | 9 | ```go 10 | f() // call f(); wait for it to return 11 | go f() // create a new goroutine that calls f(); don't wait 12 | ``` 13 | 14 | 在下面的例子中,main goroutine會計算第45個菲波那契數。由於計算函數使用了效率非常低的遞歸,所以會運行相當可觀的一段時間,在這期間我們想要讓用戶看到一個可見的標識來表明程序依然在正常運行,所以顯示一個動畵的小圖標: 15 | 16 | ```go 17 | gopl.io/ch8/spinner 18 | func main() { 19 | go spinner(100 * time.Millisecond) 20 | const n = 45 21 | fibN := fib(n) // slow 22 | fmt.Printf("\rFibonacci(%d) = %d\n", n, fibN) 23 | } 24 | 25 | func spinner(delay time.Duration) { 26 | for { 27 | for _, r := range `-\|/` { 28 | fmt.Printf("\r%c", r) 29 | time.Sleep(delay) 30 | } 31 | } 32 | } 33 | 34 | func fib(x int) int { 35 | if x < 2 { 36 | return x 37 | } 38 | return fib(x-1) + fib(x-2) 39 | } 40 | 41 | ``` 42 | 43 | 動畵顯示了幾秒之後,fib(45)的調用成功地返迴,併且打印結果: 44 | 45 | ``` 46 | Fibonacci(45) = 1134903170 47 | ``` 48 | 49 | 然後主函數返迴。當主函數返迴時,所有的goroutine都會直接打斷,程序退出。除了從主函數退出或者直接退出程序之外,沒有其它的編程方法能夠讓一個goroutine來打斷另一個的執行,但是我們之後可以看到,可以通過goroutine之間的通信來讓一個goroutine請求請求其它的goroutine,併讓其自己結束執行。 50 | 51 | 註意這里的兩個獨立的單元是如何進行組合的,spinning和菲波那契的計算。每一個都是寫在獨立的函數中,但是每一個函數都會併發地執行。 52 | -------------------------------------------------------------------------------- /ch8/ch8-04.md: -------------------------------------------------------------------------------- 1 | ## 8.4. Channels 2 | 3 | 如果説goroutine是Go語音程序的併發體的話,那麽channels它們之間的通信機製。一個channels是一個通信機製,它可以讓一個goroutine通過它給另一個goroutine發送值信息。每個channel都有一個特殊的類型,也就是channels可發送數據的類型。一個可以發送int類型數據的channel一般寫爲chan int。 4 | 5 | 使用內置的make函數,我們可以創建一個channel: 6 | 7 | ```Go 8 | ch := make(chan int) // ch has type 'chan int' 9 | ``` 10 | 11 | 和map類似,channel也一個對應make創建的底層數據結構的引用。當我嗎複製一個channel或用於函數參數傳遞時,我嗎隻是拷貝了一個channel引用,因此調用者何被調用者將引用同一個channel對象。和其它的引用類型一樣,channel的零值也是nil。 12 | 13 | 兩個相同類型的channel可以使用==運算符比較。如果兩個channel引用的是相通的對象,那麽比較的結果爲眞。一個channel也可以和nil進行比較。 14 | 15 | 一個channel有發送和接受兩個主要操作,都是通信行爲。一個發送語句將一個值從一個goroutine通過channel發送到另一個執行接收操作的goroutine。發送和接收兩個操作都是用`<-`運算符。在發送語句中,`<-`運算符分割channel和要發送的值。在接收語句中,`<-`運算符寫在channel對象之前。一個不使用接收結果的接收操作也是合法的。 16 | 17 | ```Go 18 | ch <- x // a send statement 19 | x = <-ch // a receive expression in an assignment statement 20 | <-ch // a receive statement; result is discarded 21 | ``` 22 | 23 | Channel還支持close操作,用於關閉channel,隨後對基於該channel的任何發送操作都將導致panic異常。對一個已經被close過的channel之行接收操作依然可以接受到之前已經成功發送的數據;如果channel中已經沒有數據的話講産生一個零值的數據。 24 | 25 | 使用內置的close函數就可以關閉一個channel: 26 | 27 | ```Go 28 | close(ch) 29 | ``` 30 | 31 | 以最簡單方式調用make函數創建的時一個無緩存的channel,但是我們也可以指定第二個整形參數,對應channel的容量。如果channel的容量大於零,那麽該channel就是帶緩存的channel。 32 | 33 | ```Go 34 | ch = make(chan int) // unbuffered channel 35 | ch = make(chan int, 0) // unbuffered channel 36 | ch = make(chan int, 3) // buffered channel with capacity 3 37 | ``` 38 | 39 | 我們將先討論無緩存的channel,然後在8.4.4節討論帶緩存的channel。 40 | 41 | 42 | {% include "./ch8-04-1.md" %} 43 | 44 | {% include "./ch8-04-2.md" %} 45 | 46 | {% include "./ch8-04-3.md" %} 47 | 48 | {% include "./ch8-04-4.md" %} 49 | -------------------------------------------------------------------------------- /ch8/ch8.md: -------------------------------------------------------------------------------- 1 | # 第八章 Goroutines和Channels 2 | 3 | 併發程序指的是同時做好幾件事情的程序,隨着硬件的發展,併發程序顯得越來越重要。Web服務器會一次處理成韆上萬的請求。平闆電腦和手機app在渲染用戶動畵的同時,還會後台執行各種計算任務和網絡請求。卽使是傳統的批處理問題--讀取數據,計算,寫輸出--現在也會用併發來隱藏掉I/O的操作延遲充分利用現代計算機設備的多核,盡管計算機的性能每年都在增長,但併不是線性。 4 | 5 | Go語言中的併發程序可以用兩種手段來實現。這一章會講解goroutine和channel,其支持“順序進程通信”(communicating sequential processes)或被簡稱爲CSP。CSP是一個現代的併發編程模型,在這種編程模型中值會在不同的運行實例(goroutine)中傳遞,盡管大多數情況下被限製在單一實例中。第9章會覆蓋到更爲傳統的併發模型:多線程共享內存,如果你在其它的主流語言中寫過併發程序的話可能會更熟悉一些。第9章同時會講一些本章不會深入的併發程序帶來的重要風險和陷阱。 6 | 7 | 盡管Go對併發的支持是衆多強力特性之一,但大多數情況下跟蹤併發程序還是很睏難,併且在線性程序中我們的直覺往往還會讓我們誤入歧途。如果這是你第一次接觸併發,那麽我推薦你稍微多花一些時間來思考這兩個章節中的樣例。 8 | -------------------------------------------------------------------------------- /ch9/ch9-03.md: -------------------------------------------------------------------------------- 1 | ## 9.3. sync.RWMutex讀寫鎖 2 | 3 | 在100刀的存款消失時不做記録多少還是會讓我們有一些恐慌,Bob寫了一個程序,每秒運行幾百次來檢査他的銀行餘額。他會在家,在工作中,甚至會在他的手機上來運行這個程序。銀行註意到這些陡增的流量使得存款和取款有了延時,因爲所有的餘額査詢請求是順序執行的,這樣會互斥地獲得鎖,併且會暫時阻止其它的goroutine運行。 4 | 5 | 由於Balance函數隻需要讀取變量的狀態,所以我們同時讓多個Balance調用併發運行事實上是安全的,隻要在運行的時候沒有存款或者取款操作就行。在這種場景下我們需要一種特殊類型的鎖,其允許多個隻讀操作併行執行,但寫操作會完全互斥。這種鎖叫作“多讀單寫”鎖(multiple readers, single writer lock),Go語言提供的這樣的鎖是sync.RWMutex: 6 | 7 | ```go 8 | var mu sync.RWMutex 9 | var balance int 10 | func Balance() int { 11 | mu.RLock() // readers lock 12 | defer mu.RUnlock() 13 | return balance 14 | } 15 | ``` 16 | 17 | Balance函數現在調用了RLock和RUnlock方法來獲取和釋放一個讀取或者共享鎖。Deposit函數沒有變化,會調用mu.Lock和mu.Unlock方法來獲取和釋放一個寫或互斥鎖。 18 | 19 | 在這次脩改後,Bob的餘額査詢請求就可以彼此併行地執行併且會很快地完成了。鎖在更多的時間范圍可用,併且存款請求也能夠及時地被響應了。 20 | 21 | RLock隻能在臨界區共享變量沒有任何寫入操作時可用。一般來説,我們不應該假設邏輯上的隻讀函數/方法也不會去更新某一些變量。比如一個方法功能是訪問一個變量,但它也有可能會同時去給一個內部的計數器+1(譯註:可能是記録這個方法的訪問次數啥的),或者去更新緩存--使卽時的調用能夠更快。如果有疑惑的話,請使用互斥鎖。 22 | 23 | RWMutex隻有當獲得鎖的大部分goroutine都是讀操作,而鎖在競爭條件下,也就是説,goroutine們必須等待才能獲取到鎖的時候,RWMutex才是最能帶來好處的。RWMutex需要更複雜的內部記録,所以會讓它比一般的無競爭鎖的mutex慢一些。 24 | 25 | -------------------------------------------------------------------------------- /ch9/ch9-04.md: -------------------------------------------------------------------------------- 1 | ## 9.4. 內存同步 2 | 3 | 你可能比較糾結爲什麽Balance方法需要用到互斥條件,無論是基於channel還是基於互斥量。畢竟和存款不一樣,它隻由一個簡單的操作組成,所以不會碰到其它goroutine在其執行"中"執行其它的邏輯的風險。這里使用mutex有兩方面考慮。第一Balance不會在其它操作比如Withdraw“中間”執行。第二(更重要)的是"同步"不僅僅是一堆goroutine執行順序的問題;同樣也會涉及到內存的問題。 4 | 5 | 在現代計算機中可能會有一堆處理器,每一個都會有其本地緩存(local cache)。爲了效率,對內存的寫入一般會在每一個處理器中緩衝,併在必要時一起flush到主存。這種情況下這些數據可能會以與當初goroutine寫入順序不同的順序被提交到主存。像channel通信或者互斥量操作這樣的原語會使處理器將其聚集的寫入flush併commit,這樣goroutine在某個時間點上的執行結果才能被其它處理器上運行的goroutine得到。 6 | 7 | 考慮一下下面代碼片段的可能輸出: 8 | 9 | ```go 10 | var x, y int 11 | go func() { 12 | x = 1 // A1 13 | fmt.Print("y:", y, " ") // A2 14 | }() 15 | go func() { 16 | y = 1 // B1 17 | fmt.Print("x:", x, " ") // B2 18 | }() 19 | ``` 20 | 21 | 因爲兩個goroutine是併發執行,併且訪問共享變量時也沒有互斥,會有數據競爭,所以程序的運行結果沒法預測的話也請不要驚訝。我們可能希望它能夠打印出下面這四種結果中的一種,相當於幾種不同的交錯執行時的情況: 22 | 23 | ``` 24 | y:0 x:1 25 | x:0 y:1 26 | x:1 y:1 27 | y:1 x:1 28 | ``` 29 | 30 | 第四行可以被解釋爲執行順序A1,B1,A2,B2或者B1,A1,A2,B2的執行結果。 31 | 然而實際的運行時還是有些情況讓我們有點驚訝: 32 | 33 | ``` 34 | x:0 y:0 35 | y:0 x:0 36 | ``` 37 | 38 | 但是根據所使用的編譯器,CPU,或者其它很多影響因子,這兩種情況也是有可能發生的。那麽這兩種情況要怎麽解釋呢? 39 | 40 | 在一個獨立的goroutine中,每一個語句的執行順序是可以被保證的;也就是説goroutine是順序連貫的。但是在不使用channel且不使用mutex這樣的顯式同步操作時,我們就沒法保證事件在不同的goroutine中看到的執行順序是一致的了。盡管goroutine A中一定需要觀察到x=1執行成功之後才會去讀取y,但它沒法確保自己觀察得到goroutine B中對y的寫入,所以A還可能會打印出y的一個舊版的值。 41 | 42 | 盡管去理解併發的一種嚐試是去將其運行理解爲不同goroutine語句的交錯執行,但看看上面的例子,這已經不是現代的編譯器和cpu的工作方式了。因爲賦值和打印指向不同的變量,編譯器可能會斷定兩條語句的順序不會影響執行結果,併且會交換兩個語句的執行順序。如果兩個goroutine在不同的CPU上執行,每一個核心有自己的緩存,這樣一個goroutine的寫入對於其它goroutine的Print,在主存同步之前就是不可見的了。 43 | 44 | 所有併發的問題都可以用一致的、簡單的旣定的模式來規避。所以可能的話,將變量限定在goroutine內部;如果是多個goroutine都需要訪問的變量,使用互斥條件來訪問。 45 | 46 | -------------------------------------------------------------------------------- /ch9/ch9-06.md: -------------------------------------------------------------------------------- 1 | ## 9.6. 競爭條件檢測 2 | 3 | 卽使我們小心到不能再小心,但在併發程序中犯錯還是太容易了。幸運的是,Go的runtime和工具鏈爲我們裝備了一個複雜但好用的動態分析工具,競爭檢査器(the race detector)。 4 | 5 | 隻要在go build,go run或者go test命令後面加上-race的flag,就會使編譯器創建一個你的應用的“脩改”版或者一個附帶了能夠記録所有運行期對共享變量訪問工具的test,併且會記録下每一個讀或者寫共享變量的goroutine的身份信息。另外,脩改版的程序會記録下所有的同步事件,比如go語句,channel操作,以及對(\*sync.Mutex).Lock,(\*sync.WaitGroup).Wait等等的調用。(完整的同步事件集合是在The Go Memory Model文檔中有説明,該文檔是和語言文檔放在一起的。譯註:https://golang.org/ref/mem) 6 | 7 | 競爭檢査器會檢査這些事件,會尋找在哪一個goroutine中出現了這樣的case,例如其讀或者寫了一個共享變量,這個共享變量是被另一個goroutine在沒有進行榦預同步操作便直接寫入的。這種情況也就表明了是對一個共享變量的併發訪問,卽數據競爭。這個工具會打印一份報告,內容包含變量身份,讀取和寫入的goroutine中活躍的函數的調用棧。這些信息在定位問題時通常很有用。9.7節中會有一個競爭檢査器的實戰樣例。 8 | 9 | 競爭檢査器會報告所有的已經發生的數據競爭。然而,它隻能檢測到運行時的競爭條件;併不能證明之後不會發生數據競爭。所以爲了使結果盡量正確,請保證你的測試併發地覆蓋到了你到包。 10 | 11 | 由於需要額外的記録,因此構建時加了競爭檢測的程序跑起來會慢一些,且需要更大的內存,卽時是這樣,這些代價對於很多生産環境的工作來説還是可以接受的。對於一些偶發的競爭條件來説,讓競爭檢査器來榦活可以節省無數日夜的debugging。(譯註:多少服務端C和C艹程序員爲此盡摺腰) 12 | -------------------------------------------------------------------------------- /ch9/ch9-08-1.md: -------------------------------------------------------------------------------- 1 | ### 9.8.1. 動態棧 2 | 3 | 每一個OS線程都有一個固定大小的內存塊(一般會是2MB)來做棧,這個棧會用來存儲當前正在被調用或掛起(指在調用其它函數時)的函數的內部變量。這個固定大小的棧同時很大又很小。因爲2MB的棧對於一個小小的goroutine來説是很大的內存浪費,比如對於我們用到的,一個隻是用來WaitGroup之後關閉channel的goroutine來説。而對於go程序來説,同時創建成百上韆個gorutine是非常普遍的,如果每一個goroutine都需要這麽大的棧的話,那這麽多的goroutine就不太可能了。除去大小的問題之外,固定大小的棧對於更複雜或者更深層次的遞歸函數調用來説顯然是不夠的。脩改固定的大小可以提陞空間的利用率允許創建更多的線程,併且可以允許更深的遞歸調用,不過這兩者是沒法同時兼備的。 4 | 5 | 相反,一個goroutine會以一個很小的棧開始其生命週期,一般隻需要2KB。一個goroutine的棧,和操作繫統線程一樣,會保存其活躍或掛起的函數調用的本地變量,但是和OS線程不太一樣的是一個goroutine的棧大小併不是固定的;棧的大小會根據需要動態地伸縮。而goroutine的棧的最大值有1GB,比傳統的固定大小的線程棧要大得多,盡管一般情況下,大多goroutine都不需要這麽大的棧。 6 | 7 | 練習 9.4: 創建一個流水線程序,支持用channel連接任意數量的goroutine,在跑爆內存之前,可以創建多少流水線階段?一個變量通過整個流水線需要用多久?(這個練習題翻譯不是很確定。。) 8 | -------------------------------------------------------------------------------- /ch9/ch9-08-2.md: -------------------------------------------------------------------------------- 1 | ### 9.8.2. Goroutine調度 2 | 3 | OS線程會被操作繫統內核調度。每幾毫秒,一個硬件計時器會中斷處理器,這會調用一個叫作scheduler的內核函數。這個函數會掛起當前執行的線程併保存內存中它的寄存器內容,檢査線程列表併決定下一次哪個線程可以被運行,併從內存中恢複該線程的寄存器信息,然後恢複執行該線程的現場併開始執行線程。因爲操作繫統線程是被內核所調度,所以從一個線程向另一個“移動”需要完整的上下文切換,也就是説,保存一個用戶線程的狀態到內存,恢複另一個線程的到寄存器,然後更新調度器的數據結構。這幾步操作很慢,因爲其局部性很差需要幾次內存訪問,併且會增加運行的cpu週期。 4 | 5 | Go的運行時包含了其自己的調度器,這個調度器使用了一些技術手段,比如m:n調度,因爲其會在n個操作繫統線程上多工(調度)m個goroutine。Go調度器的工作和內核的調度是相似的,但是這個調度器隻關註單獨的Go程序中的goroutine(譯註:按程序獨立)。 6 | 7 | 和操作繫統的線程調度不同的是,Go調度器併不是用一個硬件定時器而是被Go語言"建築"本身進行調度的。例如當一個goroutine調用了time.Sleep或者被channel調用或者mutex操作阻塞時,調度器會使其進入休眠併開始執行另一個goroutine直到時機到了再去喚醒第一個goroutine。因爲因爲這種調度方式不需要進入內核的上下文,所以重新調度一個goroutine比調度一個線程代價要低得多。 8 | 9 | 練習 9.5: 寫一個有兩個goroutine的程序,兩個goroutine會向兩個無buffer channel反複地發送ping-pong消息。這樣的程序每秒可以支持多少次通信? 10 | -------------------------------------------------------------------------------- /ch9/ch9-08-3.md: -------------------------------------------------------------------------------- 1 | ### 9.8.3. GOMAXPROCS 2 | 3 | Go的調度器使用了一個叫做GOMAXPROCS的變量來決定會有多少個操作繫統的線程同時執行Go的代碼。其默認的值是運行機器上的CPU的核心數,所以在一個有8個核心的機器上時,調度器一次會在8個OS線程上去調度GO代碼。(GOMAXPROCS是前面説的m:n調度中的n)。在休眠中的或者在通信中被阻塞的goroutine是不需要一個對應的線程來做調度的。在I/O中或繫統調用中或調用非Go語言函數時,是需要一個對應的操作繫統線程的,但是GOMAXPROCS併不需要將這幾種情況計數在內。 4 | 5 | 你可以用GOMAXPROCS的環境變量呂顯式地控製這個參數,或者也可以在運行時用runtime.GOMAXPROCS函數來脩改它。我們在下面的小程序中會看到GOMAXPROCS的效果,這個程序會無限打印0和1。 6 | 7 | 8 | ```go 9 | for { 10 | go fmt.Print(0) 11 | fmt.Print(1) 12 | } 13 | 14 | $ GOMAXPROCS=1 go run hacker-cliché.go 15 | 111111111111111111110000000000000000000011111... 16 | 17 | $ GOMAXPROCS=2 go run hacker-cliché.go 18 | 010101010101010101011001100101011010010100110... 19 | ``` 20 | 21 | 在第一次執行時,最多同時隻能有一個goroutine被執行。初始情況下隻有main goroutine被執行,所以會打印很多1。過了一段時間後,GO調度器會將其置爲休眠,併喚醒另一個goroutine,這時候就開始打印很多0了,在打印的時候,goroutine是被調度到操作繫統線程上的。在第二次執行時,我們使用了兩個操作繫統線程,所以兩個goroutine可以一起被執行,以同樣的頻率交替打印0和1。我們必須強調的是goroutine的調度是受很多因子影響的,而runtime也是在不斷地發展演進的,所以這里的你實際得到的結果可能會因爲版本的不同而與我們運行的結果有所不同。 22 | 23 | 練習9.6: 測試一下計算密集型的併發程序(練習8.5那樣的)會被GOMAXPROCS怎樣影響到。在你的電腦上最佳的值是多少?你的電腦CPU有多少個核心? 24 | -------------------------------------------------------------------------------- /ch9/ch9-08-4.md: -------------------------------------------------------------------------------- 1 | ### 9.8.4. Goroutine沒有ID號 2 | 3 | 在大多數支持多線程的操作繫統和程序語言中,當前的線程都有一個獨特的身份(id),併且這個身份信息可以以一個普通值的形式被被很容易地獲取到,典型的可以是一個integer或者指針值。這種情況下我們做一個抽象化的thread-local storage(線程本地存儲,多線程編程中不希望其它線程訪問的內容)就很容易,隻需要以線程的id作爲key的一個map就可以解決問題,每一個線程以其id就能從中獲取到值,且和其它線程互不衝突。 4 | 5 | goroutine沒有可以被程序員獲取到的身份(id)的概念。這一點是設計上故意而爲之,由於thread-local storage總是會被濫用。比如説,一個web server是用一種支持tls的語言實現的,而非常普遍的是很多函數會去尋找HTTP請求的信息,這代表它們就是去其存儲層(這個存儲層有可能是tls)査找的。這就像是那些過分依賴全局變量的程序一樣,會導致一種非健康的“距離外行爲”,在這種行爲下,一個函數的行爲可能不是由其自己內部的變量所決定,而是由其所運行在的線程所決定。因此,如果線程本身的身份會改變--比如一些worker線程之類的--那麽函數的行爲就會變得神祕莫測。 6 | 7 | Go鼓勵更爲簡單的模式,這種模式下參數對函數的影響都是顯式的。這樣不僅使程序變得更易讀,而且會讓我們自由地向一些給定的函數分配子任務時不用擔心其身份信息影響行爲。 8 | 9 | 你現在應該已經明白了寫一個Go程序所需要的所有語言特性信息。在後面兩章節中,我們會迴顧一些之前的實例和工具,支持我們寫出更大規模的程序:如何將一個工程組織成一繫列的包,如果獲取,構建,測試,性能測試,剖析,寫文檔,併且將這些包分享出去。 10 | 11 | -------------------------------------------------------------------------------- /ch9/ch9-08.md: -------------------------------------------------------------------------------- 1 | ## 9.8. Goroutines和線程 2 | 3 | 在上一章中我們説goroutine和操作繫統的線程區别可以先忽略。盡管兩者的區别實際上隻是一個量的區别,但量變會引起質變的道理同樣適用於goroutine和線程。現在正是我們來區分開兩者的最佳時機。 4 | 5 | {% include "./ch9-08-1.md" %} 6 | 7 | {% include "./ch9-08-2.md" %} 8 | 9 | {% include "./ch9-08-3.md" %} 10 | 11 | {% include "./ch9-08-4.md" %} 12 | -------------------------------------------------------------------------------- /ch9/ch9.md: -------------------------------------------------------------------------------- 1 | # 第九章 基於共享變量的併發 2 | 3 | 前一章我們介紹了一些使用goroutine和channel這樣直接而自然的方式來實現併發的方法。然而這樣做我們實際上屏蔽掉了在寫併發代碼時必須處理的一些重要而且細微的問題。 4 | 5 | 在本章中,我們會細致地了解併發機製。尤其是在多goroutine之間的共享變量,併發問題的分析手段,以及解決這些問題的基本模式。最後我們會解釋goroutine和操作繫統線程之間的技術上的一些區别。 6 | -------------------------------------------------------------------------------- /cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/cover.jpg -------------------------------------------------------------------------------- /cover_bgd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/cover_bgd.png -------------------------------------------------------------------------------- /cover_middle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/cover_middle.jpg -------------------------------------------------------------------------------- /cover_patch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/cover_patch.png -------------------------------------------------------------------------------- /cover_small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/cover_small.jpg -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Golang-China. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // 6 | // GitBook 7 | // 8 | // https://help.gitbook.com 9 | // https://github.com/GitbookIO/gitbook 10 | // https://github.com/wastemobile/gitbook 11 | // 12 | // npm install gitbook-cli -g 13 | // 14 | 15 | //go:generate gitbook build 16 | 17 | // 18 | // Go圣经中文版. 19 | // 20 | // 在线版本: http://golang-china.github.com/gopl-zh 21 | // 22 | // 从源文件构建: 23 | // 24 | // 1. npm install gitbook-cli -g 25 | // 2. go generate github.com/golang-china/gopl-zh 26 | // 3. 打开 _book/index.html 27 | // 28 | package gopl_zh 29 | -------------------------------------------------------------------------------- /docs/Alef/Alef.Language.Reference.Manual.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/docs/Alef/Alef.Language.Reference.Manual.pdf -------------------------------------------------------------------------------- /docs/Alef/Alef.User's.Guide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/docs/Alef/Alef.User's.Guide.pdf -------------------------------------------------------------------------------- /docs/CSP/cspbook.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/docs/CSP/cspbook.pdf -------------------------------------------------------------------------------- /docs/Limbo/descent.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/docs/Limbo/descent.pdf -------------------------------------------------------------------------------- /docs/Limbo/limbo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/docs/Limbo/limbo.pdf -------------------------------------------------------------------------------- /docs/Squeak/Squeak.A.Language.for.Communicating.with.Mice.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/docs/Squeak/Squeak.A.Language.for.Communicating.with.Mice.pdf -------------------------------------------------------------------------------- /docs/Squeak/newsqueak.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/docs/Squeak/newsqueak.pdf -------------------------------------------------------------------------------- /docs/rio_slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/docs/rio_slides.pdf -------------------------------------------------------------------------------- /gopl-zh-qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/gopl-zh-qrcode.png -------------------------------------------------------------------------------- /images/Alan.Donovan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/Alan.Donovan.png -------------------------------------------------------------------------------- /images/Brian.W.Kernighan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/Brian.W.Kernighan.png -------------------------------------------------------------------------------- /images/by-nc-sa-4.0-88x31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/by-nc-sa-4.0-88x31.png -------------------------------------------------------------------------------- /images/ch0-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch0-01.png -------------------------------------------------------------------------------- /images/ch1-01.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch1-01.gif -------------------------------------------------------------------------------- /images/ch1-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch1-01.png -------------------------------------------------------------------------------- /images/ch1-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch1-02.png -------------------------------------------------------------------------------- /images/ch1-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch1-03.png -------------------------------------------------------------------------------- /images/ch10-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch10-01.png -------------------------------------------------------------------------------- /images/ch11-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch11-01.png -------------------------------------------------------------------------------- /images/ch11-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch11-02.png -------------------------------------------------------------------------------- /images/ch11-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch11-03.png -------------------------------------------------------------------------------- /images/ch11-04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch11-04.png -------------------------------------------------------------------------------- /images/ch13-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch13-01.png -------------------------------------------------------------------------------- /images/ch3-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch3-01.png -------------------------------------------------------------------------------- /images/ch3-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch3-02.png -------------------------------------------------------------------------------- /images/ch3-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch3-03.png -------------------------------------------------------------------------------- /images/ch3-04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch3-04.png -------------------------------------------------------------------------------- /images/ch3-05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch3-05.png -------------------------------------------------------------------------------- /images/ch4-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch4-01.png -------------------------------------------------------------------------------- /images/ch4-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch4-02.png -------------------------------------------------------------------------------- /images/ch4-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch4-03.png -------------------------------------------------------------------------------- /images/ch4-04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch4-04.png -------------------------------------------------------------------------------- /images/ch4-05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch4-05.png -------------------------------------------------------------------------------- /images/ch4-06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch4-06.png -------------------------------------------------------------------------------- /images/ch4-xx-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch4-xx-01.png -------------------------------------------------------------------------------- /images/ch6-xx-00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch6-xx-00.png -------------------------------------------------------------------------------- /images/ch7-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch7-01.png -------------------------------------------------------------------------------- /images/ch7-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch7-02.png -------------------------------------------------------------------------------- /images/ch7-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch7-03.png -------------------------------------------------------------------------------- /images/ch7-04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch7-04.png -------------------------------------------------------------------------------- /images/ch7-05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch7-05.png -------------------------------------------------------------------------------- /images/ch7-06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch7-06.png -------------------------------------------------------------------------------- /images/ch7-07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch7-07.png -------------------------------------------------------------------------------- /images/ch8-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch8-01.png -------------------------------------------------------------------------------- /images/ch8-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch8-02.png -------------------------------------------------------------------------------- /images/ch8-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch8-03.png -------------------------------------------------------------------------------- /images/ch8-04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch8-04.png -------------------------------------------------------------------------------- /images/ch8-05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/ch8-05.png -------------------------------------------------------------------------------- /images/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/cover.png -------------------------------------------------------------------------------- /images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/favicon.ico -------------------------------------------------------------------------------- /images/go-log04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/go-log04.png -------------------------------------------------------------------------------- /images/gopher/go_lang_mascot_by_kirael_art-d7kunhu.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/gopher/go_lang_mascot_by_kirael_art-d7kunhu.gif -------------------------------------------------------------------------------- /images/logo/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/logo/github.png -------------------------------------------------------------------------------- /images/logo/gopher-china.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/foreversmart/gopl-zh/688c50f38fcb797e95810acd6f5e0d810eea1eac/images/logo/gopher-china.png -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | :: Copyright 2015 . All rights reserved. 2 | :: Use of this source code is governed by a BSD-style 3 | :: license that can be found in the LICENSE file. 4 | 5 | mingw32-make 6 | 7 | -------------------------------------------------------------------------------- /mkqrcode.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 . All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build ingore 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "io/ioutil" 12 | "log" 13 | 14 | qr "github.com/chai2010/image/qrencoder" 15 | ) 16 | 17 | const ( 18 | gopl_zh_url = "http://golang-china.github.io/gopl-zh/" 19 | output = "gopl-zh-qrcode.png" 20 | ) 21 | 22 | func main() { 23 | c, err := qr.Encode(gopl_zh_url, qr.H) 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | err = ioutil.WriteFile(output, c.PNG(), 0666) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | 32 | fmt.Println("output:", output) 33 | } 34 | -------------------------------------------------------------------------------- /tools/lookpath.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 . All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build ingore 6 | 7 | // Lookpath is a simple which. 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "os" 13 | "os/exec" 14 | "strings" 15 | ) 16 | 17 | func main() { 18 | if len(os.Args) < 2 { 19 | fmt.Printf(`Usage: lookpath COMMAND [...] 20 | Write the full path of COMMAND(s) to standard output. 21 | 22 | Report bugs to . 23 | `) 24 | os.Exit(0) 25 | } 26 | for i := 1; i < len(os.Args); i++ { 27 | path, err := exec.LookPath(os.Args[i]) 28 | if err != nil { 29 | fmt.Printf("lookpath: no %s in (%v)\n", os.Args[i], GetEnv("PATH")) 30 | os.Exit(0) 31 | } 32 | fmt.Println(path) 33 | } 34 | } 35 | 36 | func GetEnv(key string) string { 37 | key = strings.ToUpper(key) + "=" 38 | for _, env := range os.Environ() { 39 | if strings.HasPrefix(strings.ToUpper(env), key) { 40 | return env[len(key):] 41 | } 42 | } 43 | return "" 44 | } 45 | -------------------------------------------------------------------------------- /tools/mktable.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 . All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build ingore 6 | 7 | package main 8 | 9 | import ( 10 | "bufio" 11 | "bytes" 12 | "fmt" 13 | "io/ioutil" 14 | "log" 15 | "os" 16 | "strings" 17 | "unicode/utf8" 18 | ) 19 | 20 | func main() { 21 | f, err := os.Open("./TSCharacters.txt") 22 | if err != nil { 23 | log.Fatal("open failed:", err) 24 | } 25 | defer f.Close() 26 | 27 | br := bufio.NewReader(f) 28 | 29 | var out bytes.Buffer 30 | fmt.Fprintf(&out, ` 31 | // Copyright 2013 . All rights reserved. 32 | // Use of this source code is governed by a BSD-style 33 | // license that can be found in the LICENSE file. 34 | 35 | // Auto generated by go generate, DO NOT EDIT !!! 36 | 37 | package main 38 | 39 | var _TSCharactersMap = map[rune]rune{ 40 | `[1:]) 41 | for i := 0; i < 1<<20; i++ { 42 | data, isPrefix, err := br.ReadLine() 43 | if err != nil || isPrefix { 44 | break 45 | } 46 | if !utf8.ValidString(string(data)) { 47 | continue 48 | } 49 | 50 | line := strings.Replace(string(data), "\t", " ", -1) 51 | ss := strings.Split(string(line), " ") 52 | 53 | if len(ss) >= 2 { 54 | tw := strings.TrimSpace(ss[0]) 55 | zh := strings.TrimSpace(ss[1]) 56 | fmt.Fprintf(&out, "\t'%s': '%s',\n", tw, zh) 57 | } 58 | } 59 | fmt.Fprintf(&out, "}\n") 60 | 61 | ioutil.WriteFile("z_TSCharacters.go", []byte(out.Bytes()), 0666) 62 | } 63 | -------------------------------------------------------------------------------- /vendor/github.com/chai2010/image/qrencoder/hello.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 . All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build ingore 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "io/ioutil" 12 | "log" 13 | 14 | qr "github.com/chai2010/image/qrencoder" 15 | ) 16 | 17 | func main() { 18 | c, err := qr.Encode("hello, world", qr.L) 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | err = ioutil.WriteFile("zz_qrout.png", c.PNG(), 0666) 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | 27 | fmt.Print("output: zz_qrout.png\n") 28 | } 29 | -------------------------------------------------------------------------------- /vendor/github.com/chai2010/image/qrencoder/png_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package qrcode 6 | 7 | import ( 8 | "bytes" 9 | "image" 10 | "image/color" 11 | "image/png" 12 | "testing" 13 | ) 14 | 15 | func TestPNG(t *testing.T) { 16 | c, err := Encode("hello, world", L) 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | pngdat := c.PNG() 21 | m, err := png.Decode(bytes.NewBuffer(pngdat)) 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | gm := m.(*image.Gray) 26 | 27 | scale := c.Scale 28 | siz := c.Size 29 | nbad := 0 30 | for y := 0; y < scale*(8+siz); y++ { 31 | for x := 0; x < scale*(8+siz); x++ { 32 | v := byte(255) 33 | if c.Black(x/scale-4, y/scale-4) { 34 | v = 0 35 | } 36 | if gv := gm.At(x, y).(color.Gray).Y; gv != v { 37 | t.Errorf("%d,%d = %d, want %d", x, y, gv, v) 38 | if nbad++; nbad >= 20 { 39 | t.Fatalf("too many bad pixels") 40 | } 41 | } 42 | } 43 | } 44 | } 45 | 46 | func BenchmarkPNG(b *testing.B) { 47 | c, err := Encode("0123456789012345678901234567890123456789", L) 48 | if err != nil { 49 | panic(err) 50 | } 51 | var bytes []byte 52 | for i := 0; i < b.N; i++ { 53 | bytes = c.PNG() 54 | } 55 | b.SetBytes(int64(len(bytes))) 56 | } 57 | 58 | func BenchmarkImagePNG(b *testing.B) { 59 | c, err := Encode("0123456789012345678901234567890123456789", L) 60 | if err != nil { 61 | panic(err) 62 | } 63 | var buf bytes.Buffer 64 | for i := 0; i < b.N; i++ { 65 | buf.Reset() 66 | png.Encode(&buf, c.Image()) 67 | } 68 | b.SetBytes(int64(buf.Len())) 69 | } 70 | -------------------------------------------------------------------------------- /vendor/gopl.io/README.md: -------------------------------------------------------------------------------- 1 | # The Go Programming Language 2 | 3 | This repository provides the downloadable example programs 4 | for the book, "The Go Programming Language"; see http://www.gopl.io. 5 | 6 | These example programs are licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
7 | Creative Commons License 8 | 9 | You can download, build, and run the programs with the following commands: 10 | 11 | $ export GOPATH=$HOME/gobook # choose workspace directory 12 | $ go get gopl.io/ch1/helloworld # fetch, build, install 13 | $ $GOPATH/bin/helloworld # run 14 | Hello, 世界 15 | 16 | Many of the programs contain comments of the form `//!+` and `//!-`. 17 | These comments bracket the parts of the programs that are excerpted in the 18 | book; you can safely ignore them. In a few cases, programs 19 | have been reformatted in an unnatural way so that they can be presented 20 | in stages in the book. 21 | 22 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch1/dup1/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 8. 5 | //!+ 6 | 7 | // Dup1 prints the text of each line that appears more than 8 | // once in the standard input, preceded by its count. 9 | package main 10 | 11 | import ( 12 | "bufio" 13 | "fmt" 14 | "os" 15 | ) 16 | 17 | func main() { 18 | counts := make(map[string]int) 19 | input := bufio.NewScanner(os.Stdin) 20 | for input.Scan() { 21 | counts[input.Text()]++ 22 | } 23 | // NOTE: ignoring potential errors from input.Err() 24 | for line, n := range counts { 25 | if n > 1 { 26 | fmt.Printf("%d\t%s\n", n, line) 27 | } 28 | } 29 | } 30 | 31 | //!- 32 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch1/dup2/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 10. 5 | //!+ 6 | 7 | // Dup2 prints the count and text of lines that appear more than once 8 | // in the input. It reads from stdin or from a list of named files. 9 | package main 10 | 11 | import ( 12 | "bufio" 13 | "fmt" 14 | "os" 15 | ) 16 | 17 | func main() { 18 | counts := make(map[string]int) 19 | files := os.Args[1:] 20 | if len(files) == 0 { 21 | countLines(os.Stdin, counts) 22 | } else { 23 | for _, arg := range files { 24 | f, err := os.Open(arg) 25 | if err != nil { 26 | fmt.Fprintf(os.Stderr, "dup2: %v\n", err) 27 | continue 28 | } 29 | countLines(f, counts) 30 | f.Close() 31 | } 32 | } 33 | for line, n := range counts { 34 | if n > 1 { 35 | fmt.Printf("%d\t%s\n", n, line) 36 | } 37 | } 38 | } 39 | 40 | func countLines(f *os.File, counts map[string]int) { 41 | input := bufio.NewScanner(f) 42 | for input.Scan() { 43 | counts[input.Text()]++ 44 | } 45 | // NOTE: ignoring potential errors from input.Err() 46 | } 47 | 48 | //!- 49 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch1/dup3/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 12. 5 | 6 | //!+ 7 | 8 | // Dup3 prints the count and text of lines that 9 | // appear more than once in the named input files. 10 | package main 11 | 12 | import ( 13 | "fmt" 14 | "io/ioutil" 15 | "os" 16 | "strings" 17 | ) 18 | 19 | func main() { 20 | counts := make(map[string]int) 21 | for _, filename := range os.Args[1:] { 22 | data, err := ioutil.ReadFile(filename) 23 | if err != nil { 24 | fmt.Fprintf(os.Stderr, "dup3: %v\n", err) 25 | continue 26 | } 27 | for _, line := range strings.Split(string(data), "\n") { 28 | counts[line]++ 29 | } 30 | } 31 | for line, n := range counts { 32 | if n > 1 { 33 | fmt.Printf("%d\t%s\n", n, line) 34 | } 35 | } 36 | } 37 | 38 | //!- 39 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch1/echo1/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 4. 5 | //!+ 6 | 7 | // Echo1 prints its command-line arguments. 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "os" 13 | ) 14 | 15 | func main() { 16 | var s, sep string 17 | for i := 1; i < len(os.Args); i++ { 18 | s += sep + os.Args[i] 19 | sep = " " 20 | } 21 | fmt.Println(s) 22 | } 23 | 24 | //!- 25 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch1/echo2/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 6. 5 | //!+ 6 | 7 | // Echo2 prints its command-line arguments. 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "os" 13 | ) 14 | 15 | func main() { 16 | s, sep := "", "" 17 | for _, arg := range os.Args[1:] { 18 | s += sep + arg 19 | sep = " " 20 | } 21 | fmt.Println(s) 22 | } 23 | 24 | //!- 25 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch1/echo3/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 8. 5 | 6 | // Echo3 prints its command-line arguments. 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "os" 12 | "strings" 13 | ) 14 | 15 | //!+ 16 | func main() { 17 | fmt.Println(strings.Join(os.Args[1:], " ")) 18 | } 19 | 20 | //!- 21 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch1/fetch/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 16. 5 | //!+ 6 | 7 | // Fetch prints the content found at each specified URL. 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "io/ioutil" 13 | "net/http" 14 | "os" 15 | ) 16 | 17 | func main() { 18 | for _, url := range os.Args[1:] { 19 | resp, err := http.Get(url) 20 | if err != nil { 21 | fmt.Fprintf(os.Stderr, "fetch: %v\n", err) 22 | os.Exit(1) 23 | } 24 | b, err := ioutil.ReadAll(resp.Body) 25 | resp.Body.Close() 26 | if err != nil { 27 | fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err) 28 | os.Exit(1) 29 | } 30 | fmt.Printf("%s", b) 31 | } 32 | } 33 | 34 | //!- 35 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch1/fetchall/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 17. 5 | //!+ 6 | 7 | // Fetchall fetches URLs in parallel and reports their times and sizes. 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "io" 13 | "io/ioutil" 14 | "net/http" 15 | "os" 16 | "time" 17 | ) 18 | 19 | func main() { 20 | start := time.Now() 21 | ch := make(chan string) 22 | for _, url := range os.Args[1:] { 23 | go fetch(url, ch) // start a goroutine 24 | } 25 | for range os.Args[1:] { 26 | fmt.Println(<-ch) // receive from channel ch 27 | } 28 | fmt.Printf("%.2fs elapsed\n", time.Since(start).Seconds()) 29 | } 30 | 31 | func fetch(url string, ch chan<- string) { 32 | start := time.Now() 33 | resp, err := http.Get(url) 34 | if err != nil { 35 | ch <- fmt.Sprint(err) // send to channel ch 36 | return 37 | } 38 | 39 | nbytes, err := io.Copy(ioutil.Discard, resp.Body) 40 | resp.Body.Close() // don't leak resources 41 | if err != nil { 42 | ch <- fmt.Sprintf("while reading %s: %v", url, err) 43 | return 44 | } 45 | secs := time.Since(start).Seconds() 46 | ch <- fmt.Sprintf("%.2fs %7d %s", secs, nbytes, url) 47 | } 48 | 49 | //!- 50 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch1/helloworld/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 1. 5 | 6 | // Helloworld is our first Go program. 7 | //!+ 8 | package main 9 | 10 | import "fmt" 11 | 12 | func main() { 13 | fmt.Println("Hello, 世界") 14 | } 15 | 16 | //!- 17 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch1/server1/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 19. 5 | //!+ 6 | 7 | // Server1 is a minimal "echo" server. 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "log" 13 | "net/http" 14 | ) 15 | 16 | func main() { 17 | http.HandleFunc("/", handler) // each request calls handler 18 | log.Fatal(http.ListenAndServe("localhost:8000", nil)) 19 | } 20 | 21 | // handler echoes the Path component of the requested URL. 22 | func handler(w http.ResponseWriter, r *http.Request) { 23 | fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path) 24 | } 25 | 26 | //!- 27 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch1/server2/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 20. 5 | //!+ 6 | 7 | // Server2 is a minimal "echo" and counter server. 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "log" 13 | "net/http" 14 | "sync" 15 | ) 16 | 17 | var mu sync.Mutex 18 | var count int 19 | 20 | func main() { 21 | http.HandleFunc("/", handler) 22 | http.HandleFunc("/count", counter) 23 | log.Fatal(http.ListenAndServe("localhost:8000", nil)) 24 | } 25 | 26 | // handler echoes the Path component of the requested URL. 27 | func handler(w http.ResponseWriter, r *http.Request) { 28 | mu.Lock() 29 | count++ 30 | mu.Unlock() 31 | fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path) 32 | } 33 | 34 | // counter echoes the number of calls so far. 35 | func counter(w http.ResponseWriter, r *http.Request) { 36 | mu.Lock() 37 | fmt.Fprintf(w, "Count %d\n", count) 38 | mu.Unlock() 39 | } 40 | 41 | //!- 42 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch1/server3/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 21. 5 | 6 | // Server3 is an "echo" server that displays request parameters. 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "log" 12 | "net/http" 13 | ) 14 | 15 | func main() { 16 | http.HandleFunc("/", handler) 17 | log.Fatal(http.ListenAndServe("localhost:8000", nil)) 18 | } 19 | 20 | //!+handler 21 | // handler echoes the HTTP request. 22 | func handler(w http.ResponseWriter, r *http.Request) { 23 | fmt.Fprintf(w, "%s %s %s\n", r.Method, r.URL, r.Proto) 24 | for k, v := range r.Header { 25 | fmt.Fprintf(w, "Header[%q] = %q\n", k, v) 26 | } 27 | fmt.Fprintf(w, "Host = %q\n", r.Host) 28 | fmt.Fprintf(w, "RemoteAddr = %q\n", r.RemoteAddr) 29 | if err := r.ParseForm(); err != nil { 30 | log.Print(err) 31 | } 32 | for k, v := range r.Form { 33 | fmt.Fprintf(w, "Form[%q] = %q\n", k, v) 34 | } 35 | } 36 | 37 | //!-handler 38 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch10/cross/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 295. 5 | 6 | // The cross command prints the values of GOOS and GOARCH for this target. 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "runtime" 12 | ) 13 | 14 | //!+ 15 | func main() { 16 | fmt.Println(runtime.GOOS, runtime.GOARCH) 17 | } 18 | 19 | //!- 20 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch10/jpeg/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 287. 5 | 6 | //!+main 7 | 8 | // The jpeg command reads a PNG image from the standard input 9 | // and writes it as a JPEG image to the standard output. 10 | package main 11 | 12 | import ( 13 | "fmt" 14 | "image" 15 | "image/jpeg" 16 | _ "image/png" // register PNG decoder 17 | "io" 18 | "os" 19 | ) 20 | 21 | func main() { 22 | if err := toJPEG(os.Stdin, os.Stdout); err != nil { 23 | fmt.Fprintf(os.Stderr, "jpeg: %v\n", err) 24 | os.Exit(1) 25 | } 26 | } 27 | 28 | func toJPEG(in io.Reader, out io.Writer) error { 29 | img, kind, err := image.Decode(in) 30 | if err != nil { 31 | return err 32 | } 33 | fmt.Fprintln(os.Stderr, "Input format =", kind) 34 | return jpeg.Encode(out, img, &jpeg.Options{Quality: 95}) 35 | } 36 | 37 | //!-main 38 | 39 | /* 40 | //!+with 41 | $ go build gopl.io/ch3/mandelbrot 42 | $ go build gopl.io/ch10/jpeg 43 | $ ./mandelbrot | ./jpeg >mandelbrot.jpg 44 | Input format = png 45 | //!-with 46 | 47 | //!+without 48 | $ go build gopl.io/ch10/jpeg 49 | $ ./mandelbrot | ./jpeg >mandelbrot.jpg 50 | jpeg: image: unknown format 51 | //!-without 52 | */ 53 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch11/echo/echo.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 308. 5 | //!+ 6 | 7 | // Echo prints its command-line arguments. 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "fmt" 13 | "io" 14 | "os" 15 | "strings" 16 | ) 17 | 18 | var ( 19 | n = flag.Bool("n", false, "omit trailing newline") 20 | s = flag.String("s", " ", "separator") 21 | ) 22 | 23 | var out io.Writer = os.Stdout // modified during testing 24 | 25 | func main() { 26 | flag.Parse() 27 | if err := echo(!*n, *s, flag.Args()); err != nil { 28 | fmt.Fprintf(os.Stderr, "echo: %v\n", err) 29 | os.Exit(1) 30 | } 31 | } 32 | 33 | func echo(newline bool, sep string, args []string) error { 34 | fmt.Fprint(out, strings.Join(args, sep)) 35 | if newline { 36 | fmt.Fprintln(out) 37 | } 38 | return nil 39 | } 40 | 41 | //!- 42 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch11/echo/echo_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // Test of echo command. Run with: go test gopl.io/ch11/echo 5 | 6 | //!+ 7 | package main 8 | 9 | import ( 10 | "bytes" 11 | "fmt" 12 | "testing" 13 | ) 14 | 15 | func TestEcho(t *testing.T) { 16 | var tests = []struct { 17 | newline bool 18 | sep string 19 | args []string 20 | want string 21 | }{ 22 | {true, "", []string{}, "\n"}, 23 | {false, "", []string{}, ""}, 24 | {true, "\t", []string{"one", "two", "three"}, "one\ttwo\tthree\n"}, 25 | {true, ",", []string{"a", "b", "c"}, "a,b,c\n"}, 26 | {false, ":", []string{"1", "2", "3"}, "1:2:3"}, 27 | } 28 | 29 | for _, test := range tests { 30 | descr := fmt.Sprintf("echo(%v, %q, %q)", 31 | test.newline, test.sep, test.args) 32 | 33 | out = new(bytes.Buffer) // captured output 34 | if err := echo(test.newline, test.sep, test.args); err != nil { 35 | t.Errorf("%s failed: %v", descr, err) 36 | continue 37 | } 38 | got := out.(*bytes.Buffer).String() 39 | if got != test.want { 40 | t.Errorf("%s = %q, want %q", descr, got, test.want) 41 | } 42 | } 43 | } 44 | 45 | //!- 46 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch11/storage1/storage.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 311. 5 | 6 | // Package storage is part of a hypothetical cloud storage server. 7 | //!+main 8 | package storage 9 | 10 | import ( 11 | "fmt" 12 | "log" 13 | "net/smtp" 14 | ) 15 | 16 | var usage = make(map[string]int64) 17 | 18 | func bytesInUse(username string) int64 { return usage[username] } 19 | 20 | // Email sender configuration. 21 | // NOTE: never put passwords in source code! 22 | const sender = "notifications@example.com" 23 | const password = "correcthorsebatterystaple" 24 | const hostname = "smtp.example.com" 25 | 26 | const template = `Warning: you are using %d bytes of storage, 27 | %d%% of your quota.` 28 | 29 | func CheckQuota(username string) { 30 | used := bytesInUse(username) 31 | const quota = 1000000000 // 1GB 32 | percent := 100 * used / quota 33 | if percent < 90 { 34 | return // OK 35 | } 36 | msg := fmt.Sprintf(template, used, percent) 37 | auth := smtp.PlainAuth("", sender, password, hostname) 38 | err := smtp.SendMail(hostname+":587", auth, sender, 39 | []string{username}, []byte(msg)) 40 | if err != nil { 41 | log.Printf("smtp.SendMail(%s) failed: %s", username, err) 42 | } 43 | } 44 | 45 | //!-main 46 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch11/storage2/quota_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | //!+test 5 | package storage 6 | 7 | import ( 8 | "strings" 9 | "testing" 10 | ) 11 | 12 | func TestCheckQuotaNotifiesUser(t *testing.T) { 13 | var notifiedUser, notifiedMsg string 14 | notifyUser = func(user, msg string) { 15 | notifiedUser, notifiedMsg = user, msg 16 | } 17 | 18 | const user = "joe@example.org" 19 | usage[user] = 980000000 // simulate a 980MB-used condition 20 | 21 | CheckQuota(user) 22 | if notifiedUser == "" && notifiedMsg == "" { 23 | t.Fatalf("notifyUser not called") 24 | } 25 | if notifiedUser != user { 26 | t.Errorf("wrong user (%s) notified, want %s", 27 | notifiedUser, user) 28 | } 29 | const wantSubstring = "98% of your quota" 30 | if !strings.Contains(notifiedMsg, wantSubstring) { 31 | t.Errorf("unexpected notification message <<%s>>, "+ 32 | "want substring %q", notifiedMsg, wantSubstring) 33 | } 34 | } 35 | 36 | //!-test 37 | 38 | /* 39 | //!+defer 40 | func TestCheckQuotaNotifiesUser(t *testing.T) { 41 | // Save and restore original notifyUser. 42 | saved := notifyUser 43 | defer func() { notifyUser = saved }() 44 | 45 | // Install the test's fake notifyUser. 46 | var notifiedUser, notifiedMsg string 47 | notifyUser = func(user, msg string) { 48 | notifiedUser, notifiedMsg = user, msg 49 | } 50 | // ...rest of test... 51 | } 52 | //!-defer 53 | */ 54 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch11/storage2/storage.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 312. 5 | 6 | // Package storage is part of a hypothetical cloud storage server. 7 | package storage 8 | 9 | import ( 10 | "fmt" 11 | "log" 12 | "net/smtp" 13 | ) 14 | 15 | var usage = make(map[string]int64) 16 | 17 | func bytesInUse(username string) int64 { return usage[username] } 18 | 19 | // E-mail sender configuration. 20 | // NOTE: never put passwords in source code! 21 | const sender = "notifications@example.com" 22 | const password = "correcthorsebatterystaple" 23 | const hostname = "smtp.example.com" 24 | 25 | const template = `Warning: you are using %d bytes of storage, 26 | %d%% of your quota.` 27 | 28 | //!+factored 29 | var notifyUser = func(username, msg string) { 30 | auth := smtp.PlainAuth("", sender, password, hostname) 31 | err := smtp.SendMail(hostname+":587", auth, sender, 32 | []string{username}, []byte(msg)) 33 | if err != nil { 34 | log.Printf("smtp.SendEmail(%s) failed: %s", username, err) 35 | } 36 | } 37 | 38 | func CheckQuota(username string) { 39 | used := bytesInUse(username) 40 | const quota = 1000000000 // 1GB 41 | percent := 100 * used / quota 42 | if percent < 90 { 43 | return // OK 44 | } 45 | msg := fmt.Sprintf(template, used, percent) 46 | notifyUser(username, msg) 47 | } 48 | 49 | //!-factored 50 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch11/word1/word.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 303. 5 | //!+ 6 | 7 | // Package word provides utilities for word games. 8 | package word 9 | 10 | // IsPalindrome reports whether s reads the same forward and backward. 11 | // (Our first attempt.) 12 | func IsPalindrome(s string) bool { 13 | for i := range s { 14 | if s[i] != s[len(s)-1-i] { 15 | return false 16 | } 17 | } 18 | return true 19 | } 20 | 21 | //!- 22 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch11/word1/word_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | //!+test 5 | package word 6 | 7 | import "testing" 8 | 9 | func TestPalindrome(t *testing.T) { 10 | if !IsPalindrome("detartrated") { 11 | t.Error(`IsPalindrome("detartrated") = false`) 12 | } 13 | if !IsPalindrome("kayak") { 14 | t.Error(`IsPalindrome("kayak") = false`) 15 | } 16 | } 17 | 18 | func TestNonPalindrome(t *testing.T) { 19 | if IsPalindrome("palindrome") { 20 | t.Error(`IsPalindrome("palindrome") = true`) 21 | } 22 | } 23 | 24 | //!-test 25 | 26 | // The tests below are expected to fail. 27 | // See package gopl.io/ch11/word2 for the fix. 28 | 29 | //!+more 30 | func TestFrenchPalindrome(t *testing.T) { 31 | if !IsPalindrome("été") { 32 | t.Error(`IsPalindrome("été") = false`) 33 | } 34 | } 35 | 36 | func TestCanalPalindrome(t *testing.T) { 37 | input := "A man, a plan, a canal: Panama" 38 | if !IsPalindrome(input) { 39 | t.Errorf(`IsPalindrome(%q) = false`, input) 40 | } 41 | } 42 | 43 | //!-more 44 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch11/word2/word.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 305. 5 | //!+ 6 | 7 | // Package word provides utilities for word games. 8 | package word 9 | 10 | import "unicode" 11 | 12 | // IsPalindrome reports whether s reads the same forward and backward. 13 | // Letter case is ignored, as are non-letters. 14 | func IsPalindrome(s string) bool { 15 | var letters []rune 16 | for _, r := range s { 17 | if unicode.IsLetter(r) { 18 | letters = append(letters, unicode.ToLower(r)) 19 | } 20 | } 21 | for i := range letters { 22 | if letters[i] != letters[len(letters)-1-i] { 23 | return false 24 | } 25 | } 26 | return true 27 | } 28 | 29 | //!- 30 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch12/format/format.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 332. 5 | 6 | // Package format provides an Any function that can format any value. 7 | //!+ 8 | package format 9 | 10 | import ( 11 | "reflect" 12 | "strconv" 13 | ) 14 | 15 | // Any formats any value as a string. 16 | func Any(value interface{}) string { 17 | return formatAtom(reflect.ValueOf(value)) 18 | } 19 | 20 | // formatAtom formats a value without inspecting its internal structure. 21 | func formatAtom(v reflect.Value) string { 22 | switch v.Kind() { 23 | case reflect.Invalid: 24 | return "invalid" 25 | case reflect.Int, reflect.Int8, reflect.Int16, 26 | reflect.Int32, reflect.Int64: 27 | return strconv.FormatInt(v.Int(), 10) 28 | case reflect.Uint, reflect.Uint8, reflect.Uint16, 29 | reflect.Uint32, reflect.Uint64, reflect.Uintptr: 30 | return strconv.FormatUint(v.Uint(), 10) 31 | // ...floating-point and complex cases omitted for brevity... 32 | case reflect.Bool: 33 | return strconv.FormatBool(v.Bool()) 34 | case reflect.String: 35 | return strconv.Quote(v.String()) 36 | case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Slice, reflect.Map: 37 | return v.Type().String() + " 0x" + 38 | strconv.FormatUint(uint64(v.Pointer()), 16) 39 | default: // reflect.Array, reflect.Struct, reflect.Interface 40 | return v.Type().String() + " value" 41 | } 42 | } 43 | 44 | //!- 45 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch12/format/format_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | package format_test 5 | 6 | import ( 7 | "fmt" 8 | "testing" 9 | "time" 10 | 11 | "gopl.io/ch12/format" 12 | ) 13 | 14 | func Test(t *testing.T) { 15 | // The pointer values are just examples, and may vary from run to run. 16 | //!+time 17 | var x int64 = 1 18 | var d time.Duration = 1 * time.Nanosecond 19 | fmt.Println(format.Any(x)) // "1" 20 | fmt.Println(format.Any(d)) // "1" 21 | fmt.Println(format.Any([]int64{x})) // "[]int64 0x8202b87b0" 22 | fmt.Println(format.Any([]time.Duration{d})) // "[]time.Duration 0x8202b87e0" 23 | //!-time 24 | } 25 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch12/methods/methods.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 351. 5 | 6 | // Package methods provides a function to print the methods of any value. 7 | package methods 8 | 9 | import ( 10 | "fmt" 11 | "reflect" 12 | "strings" 13 | ) 14 | 15 | //!+print 16 | // Print prints the method set of the value x. 17 | func Print(x interface{}) { 18 | v := reflect.ValueOf(x) 19 | t := v.Type() 20 | fmt.Printf("type %s\n", t) 21 | 22 | for i := 0; i < v.NumMethod(); i++ { 23 | methType := v.Method(i).Type() 24 | fmt.Printf("func (%s) %s%s\n", t, t.Method(i).Name, 25 | strings.TrimPrefix(methType.String(), "func")) 26 | } 27 | } 28 | 29 | //!-print 30 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch12/methods/methods_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | package methods_test 5 | 6 | import ( 7 | "strings" 8 | "time" 9 | 10 | "gopl.io/ch12/methods" 11 | ) 12 | 13 | func ExamplePrintDuration() { 14 | methods.Print(time.Hour) 15 | // Output: 16 | // type time.Duration 17 | // func (time.Duration) Hours() float64 18 | // func (time.Duration) Minutes() float64 19 | // func (time.Duration) Nanoseconds() int64 20 | // func (time.Duration) Seconds() float64 21 | // func (time.Duration) String() string 22 | } 23 | 24 | func ExamplePrintReplacer() { 25 | methods.Print(new(strings.Replacer)) 26 | // Output: 27 | // type *strings.Replacer 28 | // func (*strings.Replacer) Replace(string) string 29 | // func (*strings.Replacer) WriteString(io.Writer, string) (int, error) 30 | } 31 | 32 | /* 33 | //!+output 34 | methods.Print(time.Hour) 35 | // Output: 36 | // type time.Duration 37 | // func (time.Duration) Hours() float64 38 | // func (time.Duration) Minutes() float64 39 | // func (time.Duration) Nanoseconds() int64 40 | // func (time.Duration) Seconds() float64 41 | // func (time.Duration) String() string 42 | 43 | methods.Print(new(strings.Replacer)) 44 | // Output: 45 | // type *strings.Replacer 46 | // func (*strings.Replacer) Replace(string) string 47 | // func (*strings.Replacer) WriteString(io.Writer, string) (int, error) 48 | //!-output 49 | */ 50 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch13/bzip-print/bzip2.c: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 362. 5 | // This is the version that appears in print, 6 | // but it does not comply with the proposed 7 | // rules for passing pointers between Go and C. 8 | // (https://github.com/golang/proposal/blob/master/design/12416-cgo-pointers.md) 9 | // See gopl.io/ch13/bzip for an updated version. 10 | 11 | //!+ 12 | /* This file is gopl.io/ch13/bzip/bzip2.c, */ 13 | /* a simple wrapper for libbzip2 suitable for cgo. */ 14 | #include 15 | 16 | int bz2compress(bz_stream *s, int action, 17 | char *in, unsigned *inlen, char *out, unsigned *outlen) { 18 | s->next_in = in; 19 | s->avail_in = *inlen; 20 | s->next_out = out; 21 | s->avail_out = *outlen; 22 | int r = BZ2_bzCompress(s, action); 23 | *inlen -= s->avail_in; 24 | *outlen -= s->avail_out; 25 | return r; 26 | } 27 | 28 | //!- 29 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch13/bzip-print/bzip2_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | package bzip_test 5 | 6 | import ( 7 | "bytes" 8 | "compress/bzip2" // reader 9 | "io" 10 | "testing" 11 | 12 | "gopl.io/ch13/bzip" // writer 13 | ) 14 | 15 | func TestBzip2(t *testing.T) { 16 | var compressed, uncompressed bytes.Buffer 17 | w := bzip.NewWriter(&compressed) 18 | 19 | // Write a repetitive message in a million pieces, 20 | // compressing one copy but not the other. 21 | tee := io.MultiWriter(w, &uncompressed) 22 | for i := 0; i < 1000000; i++ { 23 | io.WriteString(tee, "hello") 24 | } 25 | if err := w.Close(); err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | // Check the size of the compressed stream. 30 | if got, want := compressed.Len(), 255; got != want { 31 | t.Errorf("1 million hellos compressed to %d bytes, want %d", got, want) 32 | } 33 | 34 | // Decompress and compare with original. 35 | var decompressed bytes.Buffer 36 | io.Copy(&decompressed, bzip2.NewReader(&compressed)) 37 | if !bytes.Equal(uncompressed.Bytes(), decompressed.Bytes()) { 38 | t.Error("decompression yielded a different message") 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch13/bzip/bzip2.c: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 362. 5 | // 6 | // The version of this program that appeared in the first and second 7 | // printings did not comply with the proposed rules for passing 8 | // pointers between Go and C, described here: 9 | // https://github.com/golang/proposal/blob/master/design/12416-cgo-pointers.md 10 | // 11 | // The version below, which appears in the third printing, 12 | // has been corrected. See bzip2.go for explanation. 13 | 14 | //!+ 15 | /* This file is gopl.io/ch13/bzip/bzip2.c, */ 16 | /* a simple wrapper for libbzip2 suitable for cgo. */ 17 | #include 18 | 19 | int bz2compress(bz_stream *s, int action, 20 | char *in, unsigned *inlen, char *out, unsigned *outlen) { 21 | s->next_in = in; 22 | s->avail_in = *inlen; 23 | s->next_out = out; 24 | s->avail_out = *outlen; 25 | int r = BZ2_bzCompress(s, action); 26 | *inlen -= s->avail_in; 27 | *outlen -= s->avail_out; 28 | s->next_in = s->next_out = NULL; 29 | return r; 30 | } 31 | 32 | //!- 33 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch13/bzip/bzip2_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | package bzip_test 5 | 6 | import ( 7 | "bytes" 8 | "compress/bzip2" // reader 9 | "io" 10 | "testing" 11 | 12 | "gopl.io/ch13/bzip" // writer 13 | ) 14 | 15 | func TestBzip2(t *testing.T) { 16 | var compressed, uncompressed bytes.Buffer 17 | w := bzip.NewWriter(&compressed) 18 | 19 | // Write a repetitive message in a million pieces, 20 | // compressing one copy but not the other. 21 | tee := io.MultiWriter(w, &uncompressed) 22 | for i := 0; i < 1000000; i++ { 23 | io.WriteString(tee, "hello") 24 | } 25 | if err := w.Close(); err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | // Check the size of the compressed stream. 30 | if got, want := compressed.Len(), 255; got != want { 31 | t.Errorf("1 million hellos compressed to %d bytes, want %d", got, want) 32 | } 33 | 34 | // Decompress and compare with original. 35 | var decompressed bytes.Buffer 36 | io.Copy(&decompressed, bzip2.NewReader(&compressed)) 37 | if !bytes.Equal(uncompressed.Bytes(), decompressed.Bytes()) { 38 | t.Error("decompression yielded a different message") 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch13/bzipper/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 365. 5 | 6 | //!+ 7 | 8 | // Bzipper reads input, bzip2-compresses it, and writes it out. 9 | package main 10 | 11 | import ( 12 | "io" 13 | "log" 14 | "os" 15 | 16 | "gopl.io/ch13/bzip" 17 | ) 18 | 19 | func main() { 20 | w := bzip.NewWriter(os.Stdout) 21 | if _, err := io.Copy(w, os.Stdin); err != nil { 22 | log.Fatalf("bzipper: %v\n", err) 23 | } 24 | if err := w.Close(); err != nil { 25 | log.Fatalf("bzipper: close: %v\n", err) 26 | } 27 | } 28 | 29 | //!- 30 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch13/unsafeptr/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 357. 5 | 6 | // Package unsafeptr demonstrates basic use of unsafe.Pointer. 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "unsafe" 12 | ) 13 | 14 | func main() { 15 | //!+main 16 | var x struct { 17 | a bool 18 | b int16 19 | c []int 20 | } 21 | 22 | // equivalent to pb := &x.b 23 | pb := (*int16)(unsafe.Pointer( 24 | uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b))) 25 | *pb = 42 26 | 27 | fmt.Println(x.b) // "42" 28 | //!-main 29 | } 30 | 31 | /* 32 | //!+wrong 33 | // NOTE: subtly incorrect! 34 | tmp := uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b) 35 | pb := (*int16)(unsafe.Pointer(tmp)) 36 | *pb = 42 37 | //!-wrong 38 | */ 39 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch2/boiling/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 29. 5 | //!+ 6 | 7 | // Boiling prints the boiling point of water. 8 | package main 9 | 10 | import "fmt" 11 | 12 | const boilingF = 212.0 13 | 14 | func main() { 15 | var f = boilingF 16 | var c = (f - 32) * 5 / 9 17 | fmt.Printf("boiling point = %g°F or %g°C\n", f, c) 18 | // Output: 19 | // boiling point = 212°F or 100°C 20 | } 21 | 22 | //!- 23 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch2/cf/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 43. 5 | //!+ 6 | 7 | // Cf converts its numeric argument to Celsius and Fahrenheit. 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "os" 13 | "strconv" 14 | 15 | "gopl.io/ch2/tempconv" 16 | ) 17 | 18 | func main() { 19 | for _, arg := range os.Args[1:] { 20 | t, err := strconv.ParseFloat(arg, 64) 21 | if err != nil { 22 | fmt.Fprintf(os.Stderr, "cf: %v\n", err) 23 | os.Exit(1) 24 | } 25 | f := tempconv.Fahrenheit(t) 26 | c := tempconv.Celsius(t) 27 | fmt.Printf("%s = %s, %s = %s\n", 28 | f, tempconv.FToC(f), c, tempconv.CToF(c)) 29 | } 30 | } 31 | 32 | //!- 33 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch2/echo4/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 33. 5 | //!+ 6 | 7 | // Echo4 prints its command-line arguments. 8 | package main 9 | 10 | import ( 11 | "flag" 12 | "fmt" 13 | "strings" 14 | ) 15 | 16 | var n = flag.Bool("n", false, "omit trailing newline") 17 | var sep = flag.String("s", " ", "separator") 18 | 19 | func main() { 20 | flag.Parse() 21 | fmt.Print(strings.Join(flag.Args(), *sep)) 22 | if !*n { 23 | fmt.Println() 24 | } 25 | } 26 | 27 | //!- 28 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch2/ftoc/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 29. 5 | //!+ 6 | 7 | // Ftoc prints two Fahrenheit-to-Celsius conversions. 8 | package main 9 | 10 | import "fmt" 11 | 12 | func main() { 13 | const freezingF, boilingF = 32.0, 212.0 14 | fmt.Printf("%g°F = %g°C\n", freezingF, fToC(freezingF)) // "32°F = 0°C" 15 | fmt.Printf("%g°F = %g°C\n", boilingF, fToC(boilingF)) // "212°F = 100°C" 16 | } 17 | 18 | func fToC(f float64) float64 { 19 | return (f - 32) * 5 / 9 20 | } 21 | 22 | //!- 23 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch2/popcount/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 45. 5 | 6 | // (Package doc comment intentionally malformed to demonstrate golint.) 7 | //!+ 8 | package popcount 9 | 10 | // pc[i] is the population count of i. 11 | var pc [256]byte 12 | 13 | func init() { 14 | for i := range pc { 15 | pc[i] = pc[i/2] + byte(i&1) 16 | } 17 | } 18 | 19 | // PopCount returns the population count (number of set bits) of x. 20 | func PopCount(x uint64) int { 21 | return int(pc[byte(x>>(0*8))] + 22 | pc[byte(x>>(1*8))] + 23 | pc[byte(x>>(2*8))] + 24 | pc[byte(x>>(3*8))] + 25 | pc[byte(x>>(4*8))] + 26 | pc[byte(x>>(5*8))] + 27 | pc[byte(x>>(6*8))] + 28 | pc[byte(x>>(7*8))]) 29 | } 30 | 31 | //!- 32 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch2/tempconv/conv.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 41. 5 | 6 | //!+ 7 | 8 | package tempconv 9 | 10 | // CToF converts a Celsius temperature to Fahrenheit. 11 | func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) } 12 | 13 | // FToC converts a Fahrenheit temperature to Celsius. 14 | func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) } 15 | 16 | //!- 17 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch2/tempconv/tempconv.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | //!+ 5 | 6 | // Package tempconv performs Celsius and Fahrenheit conversions. 7 | package tempconv 8 | 9 | import "fmt" 10 | 11 | type Celsius float64 12 | type Fahrenheit float64 13 | 14 | const ( 15 | AbsoluteZeroC Celsius = -273.15 16 | FreezingC Celsius = 0 17 | BoilingC Celsius = 100 18 | ) 19 | 20 | func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) } 21 | func (f Fahrenheit) String() string { return fmt.Sprintf("%g°F", f) } 22 | 23 | //!- 24 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch2/tempconv0/celsius.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 39. 5 | //!+ 6 | 7 | // Package tempconv performs Celsius and Fahrenheit temperature computations. 8 | package tempconv 9 | 10 | import "fmt" 11 | 12 | type Celsius float64 13 | type Fahrenheit float64 14 | 15 | const ( 16 | AbsoluteZeroC Celsius = -273.15 17 | FreezingC Celsius = 0 18 | BoilingC Celsius = 100 19 | ) 20 | 21 | func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) } 22 | 23 | func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) } 24 | 25 | //!- 26 | 27 | func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) } 28 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch2/tempconv0/tempconv_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | package tempconv 5 | 6 | import "fmt" 7 | 8 | func Example_one() { 9 | { 10 | //!+arith 11 | fmt.Printf("%g\n", BoilingC-FreezingC) // "100" °C 12 | boilingF := CToF(BoilingC) 13 | fmt.Printf("%g\n", boilingF-CToF(FreezingC)) // "180" °F 14 | //!-arith 15 | } 16 | /* 17 | //!+arith 18 | fmt.Printf("%g\n", boilingF-FreezingC) // compile error: type mismatch 19 | //!-arith 20 | */ 21 | 22 | // Output: 23 | // 100 24 | // 180 25 | } 26 | 27 | func Example_two() { 28 | //!+printf 29 | c := FToC(212.0) 30 | fmt.Println(c.String()) // "100°C" 31 | fmt.Printf("%v\n", c) // "100°C"; no need to call String explicitly 32 | fmt.Printf("%s\n", c) // "100°C" 33 | fmt.Println(c) // "100°C" 34 | fmt.Printf("%g\n", c) // "100"; does not call String 35 | fmt.Println(float64(c)) // "100"; does not call String 36 | //!-printf 37 | 38 | // Output: 39 | // 100°C 40 | // 100°C 41 | // 100°C 42 | // 100°C 43 | // 100 44 | // 100 45 | } 46 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch3/basename1/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 72. 5 | 6 | // Basename1 reads file names from stdin and prints the base name of each one. 7 | package main 8 | 9 | import ( 10 | "bufio" 11 | "fmt" 12 | "os" 13 | ) 14 | 15 | func main() { 16 | input := bufio.NewScanner(os.Stdin) 17 | for input.Scan() { 18 | fmt.Println(basename(input.Text())) 19 | } 20 | // NOTE: ignoring potential errors from input.Err() 21 | } 22 | 23 | //!+ 24 | // basename removes directory components and a .suffix. 25 | // e.g., a => a, a.go => a, a/b/c.go => c, a/b.c.go => b.c 26 | func basename(s string) string { 27 | // Discard last '/' and everything before. 28 | for i := len(s) - 1; i >= 0; i-- { 29 | if s[i] == '/' { 30 | s = s[i+1:] 31 | break 32 | } 33 | } 34 | // Preserve everything before last '.'. 35 | for i := len(s) - 1; i >= 0; i-- { 36 | if s[i] == '.' { 37 | s = s[:i] 38 | break 39 | } 40 | } 41 | return s 42 | } 43 | 44 | //!- 45 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch3/basename2/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 72. 5 | 6 | // Basename2 reads file names from stdin and prints the base name of each one. 7 | package main 8 | 9 | import ( 10 | "bufio" 11 | "fmt" 12 | "os" 13 | "strings" 14 | ) 15 | 16 | func main() { 17 | input := bufio.NewScanner(os.Stdin) 18 | for input.Scan() { 19 | fmt.Println(basename(input.Text())) 20 | } 21 | // NOTE: ignoring potential errors from input.Err() 22 | } 23 | 24 | // basename removes directory components and a trailing .suffix. 25 | // e.g., a => a, a.go => a, a/b/c.go => c, a/b.c.go => b.c 26 | //!+ 27 | func basename(s string) string { 28 | slash := strings.LastIndex(s, "/") // -1 if "/" not found 29 | s = s[slash+1:] 30 | if dot := strings.LastIndex(s, "."); dot >= 0 { 31 | s = s[:dot] 32 | } 33 | return s 34 | } 35 | 36 | //!- 37 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch3/comma/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 73. 5 | 6 | // Comma prints its argument numbers with a comma at each power of 1000. 7 | // 8 | // Example: 9 | // $ go build gopl.io/ch3/comma 10 | // $ ./comma 1 12 123 1234 1234567890 11 | // 1 12 | // 12 13 | // 123 14 | // 1,234 15 | // 1,234,567,890 16 | // 17 | package main 18 | 19 | import ( 20 | "fmt" 21 | "os" 22 | ) 23 | 24 | func main() { 25 | for i := 1; i < len(os.Args); i++ { 26 | fmt.Printf(" %s\n", comma(os.Args[i])) 27 | } 28 | } 29 | 30 | //!+ 31 | // comma inserts commas in a non-negative decimal integer string. 32 | func comma(s string) string { 33 | n := len(s) 34 | if n <= 3 { 35 | return s 36 | } 37 | return comma(s[:n-3]) + "," + s[n-3:] 38 | } 39 | 40 | //!- 41 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch3/netflag/netflag.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 77. 5 | 6 | // Netflag demonstrates an integer type used as a bit field. 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | . "net" 12 | ) 13 | 14 | //!+ 15 | func IsUp(v Flags) bool { return v&FlagUp == FlagUp } 16 | func TurnDown(v *Flags) { *v &^= FlagUp } 17 | func SetBroadcast(v *Flags) { *v |= FlagBroadcast } 18 | func IsCast(v Flags) bool { return v&(FlagBroadcast|FlagMulticast) != 0 } 19 | 20 | func main() { 21 | var v Flags = FlagMulticast | FlagUp 22 | fmt.Printf("%b %t\n", v, IsUp(v)) // "10001 true" 23 | TurnDown(&v) 24 | fmt.Printf("%b %t\n", v, IsUp(v)) // "10000 false" 25 | SetBroadcast(&v) 26 | fmt.Printf("%b %t\n", v, IsUp(v)) // "10010 false" 27 | fmt.Printf("%b %t\n", v, IsCast(v)) // "10010 true" 28 | } 29 | 30 | //!- 31 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch3/printints/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 74. 5 | 6 | // Printints demonstrates the use of bytes.Buffer to format a string. 7 | package main 8 | 9 | import ( 10 | "bytes" 11 | "fmt" 12 | ) 13 | 14 | //!+ 15 | // intsToString is like fmt.Sprint(values) but adds commas. 16 | func intsToString(values []int) string { 17 | var buf bytes.Buffer 18 | buf.WriteByte('[') 19 | for i, v := range values { 20 | if i > 0 { 21 | buf.WriteString(", ") 22 | } 23 | fmt.Fprintf(&buf, "%d", v) 24 | } 25 | buf.WriteByte(']') 26 | return buf.String() 27 | } 28 | 29 | func main() { 30 | fmt.Println(intsToString([]int{1, 2, 3})) // "[1, 2, 3]" 31 | } 32 | 33 | //!- 34 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch4/autoescape/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 117. 5 | 6 | // Autoescape demonstrates automatic HTML escaping in html/template. 7 | package main 8 | 9 | import ( 10 | "html/template" 11 | "log" 12 | "os" 13 | ) 14 | 15 | //!+ 16 | func main() { 17 | const templ = `

A: {{.A}}

B: {{.B}}

` 18 | t := template.Must(template.New("escape").Parse(templ)) 19 | var data struct { 20 | A string // untrusted plain text 21 | B template.HTML // trusted HTML 22 | } 23 | data.A = "Hello!" 24 | data.B = "Hello!" 25 | if err := t.Execute(os.Stdout, data); err != nil { 26 | log.Fatal(err) 27 | } 28 | } 29 | 30 | //!- 31 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch4/charcount/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 97. 5 | //!+ 6 | 7 | // Charcount computes counts of Unicode characters. 8 | package main 9 | 10 | import ( 11 | "bufio" 12 | "fmt" 13 | "io" 14 | "os" 15 | "unicode" 16 | "unicode/utf8" 17 | ) 18 | 19 | func main() { 20 | counts := make(map[rune]int) // counts of Unicode characters 21 | var utflen [utf8.UTFMax + 1]int // count of lengths of UTF-8 encodings 22 | invalid := 0 // count of invalid UTF-8 characters 23 | 24 | in := bufio.NewReader(os.Stdin) 25 | for { 26 | r, n, err := in.ReadRune() // returns rune, nbytes, error 27 | if err == io.EOF { 28 | break 29 | } 30 | if err != nil { 31 | fmt.Fprintf(os.Stderr, "charcount: %v\n", err) 32 | os.Exit(1) 33 | } 34 | if r == unicode.ReplacementChar && n == 1 { 35 | invalid++ 36 | continue 37 | } 38 | counts[r]++ 39 | utflen[n]++ 40 | } 41 | fmt.Printf("rune\tcount\n") 42 | for c, n := range counts { 43 | fmt.Printf("%q\t%d\n", c, n) 44 | } 45 | fmt.Print("\nlen\tcount\n") 46 | for i, n := range utflen { 47 | if i > 0 { 48 | fmt.Printf("%d\t%d\n", i, n) 49 | } 50 | } 51 | if invalid > 0 { 52 | fmt.Printf("\n%d invalid UTF-8 characters\n", invalid) 53 | } 54 | } 55 | 56 | //!- 57 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch4/dedup/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 96. 5 | 6 | // Dedup prints only one instance of each line; duplicates are removed. 7 | package main 8 | 9 | import ( 10 | "bufio" 11 | "fmt" 12 | "os" 13 | ) 14 | 15 | //!+ 16 | func main() { 17 | seen := make(map[string]bool) // a set of strings 18 | input := bufio.NewScanner(os.Stdin) 19 | for input.Scan() { 20 | line := input.Text() 21 | if !seen[line] { 22 | seen[line] = true 23 | fmt.Println(line) 24 | } 25 | } 26 | 27 | if err := input.Err(); err != nil { 28 | fmt.Fprintf(os.Stderr, "dedup: %v\n", err) 29 | os.Exit(1) 30 | } 31 | } 32 | 33 | //!- 34 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch4/embed/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 106. 5 | 6 | // Embed demonstrates basic struct embedding. 7 | package main 8 | 9 | import "fmt" 10 | 11 | type Point struct{ X, Y int } 12 | 13 | type Circle struct { 14 | Point 15 | Radius int 16 | } 17 | 18 | type Wheel struct { 19 | Circle 20 | Spokes int 21 | } 22 | 23 | func main() { 24 | var w Wheel 25 | //!+ 26 | w = Wheel{Circle{Point{8, 8}, 5}, 20} 27 | 28 | w = Wheel{ 29 | Circle: Circle{ 30 | Point: Point{X: 8, Y: 8}, 31 | Radius: 5, 32 | }, 33 | Spokes: 20, // NOTE: trailing comma necessary here (and at Radius) 34 | } 35 | 36 | fmt.Printf("%#v\n", w) 37 | // Output: 38 | // Wheel{Circle:Circle{Point:Point{X:8, Y:8}, Radius:5}, Spokes:20} 39 | 40 | w.X = 42 41 | 42 | fmt.Printf("%#v\n", w) 43 | // Output: 44 | // Wheel{Circle:Circle{Point:Point{X:42, Y:8}, Radius:5}, Spokes:20} 45 | //!- 46 | } 47 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch4/github/github.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 110. 5 | //!+ 6 | 7 | // Package github provides a Go API for the GitHub issue tracker. 8 | // See https://developer.github.com/v3/search/#search-issues. 9 | package github 10 | 11 | import "time" 12 | 13 | const IssuesURL = "https://api.github.com/search/issues" 14 | 15 | type IssuesSearchResult struct { 16 | TotalCount int `json:"total_count"` 17 | Items []*Issue 18 | } 19 | 20 | type Issue struct { 21 | Number int 22 | HTMLURL string `json:"html_url"` 23 | Title string 24 | State string 25 | User *User 26 | CreatedAt time.Time `json:"created_at"` 27 | Body string // in Markdown format 28 | } 29 | 30 | type User struct { 31 | Login string 32 | HTMLURL string `json:"html_url"` 33 | } 34 | 35 | //!- 36 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch4/github/search.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | //!+ 5 | 6 | package github 7 | 8 | import ( 9 | "encoding/json" 10 | "fmt" 11 | "net/http" 12 | "net/url" 13 | "strings" 14 | ) 15 | 16 | // SearchIssues queries the GitHub issue tracker. 17 | func SearchIssues(terms []string) (*IssuesSearchResult, error) { 18 | q := url.QueryEscape(strings.Join(terms, " ")) 19 | resp, err := http.Get(IssuesURL + "?q=" + q) 20 | if err != nil { 21 | return nil, err 22 | } 23 | //!- 24 | // For long-term stability, instead of http.Get, use the 25 | // variant below which adds an HTTP request header indicating 26 | // that only version 3 of the GitHub API is acceptable. 27 | // 28 | // req, err := http.NewRequest("GET", IssuesURL+"?q="+q, nil) 29 | // if err != nil { 30 | // return nil, err 31 | // } 32 | // req.Header.Set( 33 | // "Accept", "application/vnd.github.v3.text-match+json") 34 | // resp, err := http.DefaultClient.Do(req) 35 | //!+ 36 | 37 | // We must close resp.Body on all execution paths. 38 | // (Chapter 5 presents 'defer', which makes this simpler.) 39 | if resp.StatusCode != http.StatusOK { 40 | resp.Body.Close() 41 | return nil, fmt.Errorf("search query failed: %s", resp.Status) 42 | } 43 | 44 | var result IssuesSearchResult 45 | if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { 46 | resp.Body.Close() 47 | return nil, err 48 | } 49 | resp.Body.Close() 50 | return &result, nil 51 | } 52 | 53 | //!- 54 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch4/graph/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 99. 5 | 6 | // Graph shows how to use a map of maps to represent a directed graph. 7 | package main 8 | 9 | import "fmt" 10 | 11 | //!+ 12 | var graph = make(map[string]map[string]bool) 13 | 14 | func addEdge(from, to string) { 15 | edges := graph[from] 16 | if edges == nil { 17 | edges = make(map[string]bool) 18 | graph[from] = edges 19 | } 20 | edges[to] = true 21 | } 22 | 23 | func hasEdge(from, to string) bool { 24 | return graph[from][to] 25 | } 26 | 27 | //!- 28 | 29 | func main() { 30 | addEdge("a", "b") 31 | addEdge("c", "d") 32 | addEdge("a", "d") 33 | addEdge("d", "a") 34 | fmt.Println(hasEdge("a", "b")) 35 | fmt.Println(hasEdge("c", "d")) 36 | fmt.Println(hasEdge("a", "d")) 37 | fmt.Println(hasEdge("d", "a")) 38 | fmt.Println(hasEdge("x", "b")) 39 | fmt.Println(hasEdge("c", "d")) 40 | fmt.Println(hasEdge("x", "d")) 41 | fmt.Println(hasEdge("d", "x")) 42 | 43 | } 44 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch4/issueshtml/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 115. 5 | 6 | // Issueshtml prints an HTML table of issues matching the search terms. 7 | package main 8 | 9 | import ( 10 | "log" 11 | "os" 12 | 13 | "gopl.io/ch4/github" 14 | ) 15 | 16 | //!+template 17 | import "html/template" 18 | 19 | var issueList = template.Must(template.New("issuelist").Parse(` 20 |

{{.TotalCount}} issues

21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | {{range .Items}} 29 | 30 | 31 | 32 | 33 | 34 | 35 | {{end}} 36 |
#StateUserTitle
{{.Number}}{{.State}}{{.User.Login}}{{.Title}}
37 | `)) 38 | 39 | //!-template 40 | 41 | //!+ 42 | func main() { 43 | result, err := github.SearchIssues(os.Args[1:]) 44 | if err != nil { 45 | log.Fatal(err) 46 | } 47 | if err := issueList.Execute(os.Stdout, result); err != nil { 48 | log.Fatal(err) 49 | } 50 | } 51 | 52 | //!- 53 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch4/nonempty/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 91. 5 | 6 | //!+nonempty 7 | 8 | // Nonempty is an example of an in-place slice algorithm. 9 | package main 10 | 11 | import "fmt" 12 | 13 | // nonempty returns a slice holding only the non-empty strings. 14 | // The underlying array is modified during the call. 15 | func nonempty(strings []string) []string { 16 | i := 0 17 | for _, s := range strings { 18 | if s != "" { 19 | strings[i] = s 20 | i++ 21 | } 22 | } 23 | return strings[:i] 24 | } 25 | 26 | //!-nonempty 27 | 28 | func main() { 29 | //!+main 30 | data := []string{"one", "", "three"} 31 | fmt.Printf("%q\n", nonempty(data)) // `["one" "three"]` 32 | fmt.Printf("%q\n", data) // `["one" "three" "three"]` 33 | //!-main 34 | } 35 | 36 | //!+alt 37 | func nonempty2(strings []string) []string { 38 | out := strings[:0] // zero-length slice of original 39 | for _, s := range strings { 40 | if s != "" { 41 | out = append(out, s) 42 | } 43 | } 44 | return out 45 | } 46 | 47 | //!-alt 48 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch4/rev/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 86. 5 | 6 | // Rev reverses a slice. 7 | package main 8 | 9 | import ( 10 | "bufio" 11 | "fmt" 12 | "os" 13 | "strconv" 14 | "strings" 15 | ) 16 | 17 | func main() { 18 | //!+array 19 | a := [...]int{0, 1, 2, 3, 4, 5} 20 | reverse(a[:]) 21 | fmt.Println(a) // "[5 4 3 2 1 0]" 22 | //!-array 23 | 24 | //!+slice 25 | s := []int{0, 1, 2, 3, 4, 5} 26 | // Rotate s left by two positions. 27 | reverse(s[:2]) 28 | reverse(s[2:]) 29 | reverse(s) 30 | fmt.Println(s) // "[2 3 4 5 0 1]" 31 | //!-slice 32 | 33 | // Interactive test of reverse. 34 | input := bufio.NewScanner(os.Stdin) 35 | outer: 36 | for input.Scan() { 37 | var ints []int 38 | for _, s := range strings.Fields(input.Text()) { 39 | x, err := strconv.ParseInt(s, 10, 64) 40 | if err != nil { 41 | fmt.Fprintln(os.Stderr, err) 42 | continue outer 43 | } 44 | ints = append(ints, int(x)) 45 | } 46 | reverse(ints) 47 | fmt.Printf("%v\n", ints) 48 | } 49 | // NOTE: ignoring potential errors from input.Err() 50 | } 51 | 52 | //!+rev 53 | // reverse reverses a slice of ints in place. 54 | func reverse(s []int) { 55 | for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { 56 | s[i], s[j] = s[j], s[i] 57 | } 58 | } 59 | 60 | //!-rev 61 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch4/sha256/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 83. 5 | 6 | // The sha256 command computes the SHA256 hash (an array) of a string. 7 | package main 8 | 9 | import "fmt" 10 | 11 | //!+ 12 | import "crypto/sha256" 13 | 14 | func main() { 15 | c1 := sha256.Sum256([]byte("x")) 16 | c2 := sha256.Sum256([]byte("X")) 17 | fmt.Printf("%x\n%x\n%t\n%T\n", c1, c2, c1 == c2, c1) 18 | // Output: 19 | // 2d711642b726b04401627ca9fbac32f5c8530fb1903cc4db02258717921a4881 20 | // 4b68ab3847feda7d6c62c1fbcbeebfa35eab7351ed5e78f4ddadea5df64b8015 21 | // false 22 | // [32]uint8 23 | } 24 | 25 | //!- 26 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch4/treesort/sort.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 101. 5 | 6 | // Package treesort provides insertion sort using an unbalanced binary tree. 7 | package treesort 8 | 9 | //!+ 10 | type tree struct { 11 | value int 12 | left, right *tree 13 | } 14 | 15 | // Sort sorts values in place. 16 | func Sort(values []int) { 17 | var root *tree 18 | for _, v := range values { 19 | root = add(root, v) 20 | } 21 | appendValues(values[:0], root) 22 | } 23 | 24 | // appendValues appends the elements of t to values in order 25 | // and returns the resulting slice. 26 | func appendValues(values []int, t *tree) []int { 27 | if t != nil { 28 | values = appendValues(values, t.left) 29 | values = append(values, t.value) 30 | values = appendValues(values, t.right) 31 | } 32 | return values 33 | } 34 | 35 | func add(t *tree, value int) *tree { 36 | if t == nil { 37 | // Equivalent to return &tree{value: value}. 38 | t = new(tree) 39 | t.value = value 40 | return t 41 | } 42 | if value < t.value { 43 | t.left = add(t.left, value) 44 | } else { 45 | t.right = add(t.right, value) 46 | } 47 | return t 48 | } 49 | 50 | //!- 51 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch4/treesort/sort_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | package treesort_test 5 | 6 | import ( 7 | "math/rand" 8 | "sort" 9 | "testing" 10 | 11 | "gopl.io/ch4/treesort" 12 | ) 13 | 14 | func TestSort(t *testing.T) { 15 | data := make([]int, 50) 16 | for i := range data { 17 | data[i] = rand.Int() % 50 18 | } 19 | treesort.Sort(data) 20 | if !sort.IntsAreSorted(data) { 21 | t.Errorf("not sorted: %v", data) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch5/defer1/defer.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 150. 5 | 6 | // Defer1 demonstrates a deferred call being invoked during a panic. 7 | package main 8 | 9 | import "fmt" 10 | 11 | //!+f 12 | func main() { 13 | f(3) 14 | } 15 | 16 | func f(x int) { 17 | fmt.Printf("f(%d)\n", x+0/x) // panics if x == 0 18 | defer fmt.Printf("defer %d\n", x) 19 | f(x - 1) 20 | } 21 | 22 | //!-f 23 | 24 | /* 25 | //!+stdout 26 | f(3) 27 | f(2) 28 | f(1) 29 | defer 1 30 | defer 2 31 | defer 3 32 | //!-stdout 33 | 34 | //!+stderr 35 | panic: runtime error: integer divide by zero 36 | main.f(0) 37 | src/gopl.io/ch5/defer1/defer.go:14 38 | main.f(1) 39 | src/gopl.io/ch5/defer1/defer.go:16 40 | main.f(2) 41 | src/gopl.io/ch5/defer1/defer.go:16 42 | 43 | main.f(3) 44 | src/gopl.io/ch5/defer1/defer.go:16 45 | main.main() 46 | src/gopl.io/ch5/defer1/defer.go:10 47 | //!-stderr 48 | */ 49 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch5/defer2/defer.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 151. 5 | 6 | // Defer2 demonstrates a deferred call to runtime.Stack during a panic. 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "os" 12 | "runtime" 13 | ) 14 | 15 | //!+ 16 | func main() { 17 | defer printStack() 18 | f(3) 19 | } 20 | 21 | func printStack() { 22 | var buf [4096]byte 23 | n := runtime.Stack(buf[:], false) 24 | os.Stdout.Write(buf[:n]) 25 | } 26 | 27 | //!- 28 | 29 | func f(x int) { 30 | fmt.Printf("f(%d)\n", x+0/x) // panics if x == 0 31 | defer fmt.Printf("defer %d\n", x) 32 | f(x - 1) 33 | } 34 | 35 | /* 36 | //!+printstack 37 | goroutine 1 [running]: 38 | main.printStack() 39 | src/gopl.io/ch5/defer2/defer.go:20 40 | main.f(0) 41 | src/gopl.io/ch5/defer2/defer.go:27 42 | main.f(1) 43 | src/gopl.io/ch5/defer2/defer.go:29 44 | main.f(2) 45 | src/gopl.io/ch5/defer2/defer.go:29 46 | main.f(3) 47 | src/gopl.io/ch5/defer2/defer.go:29 48 | main.main() 49 | src/gopl.io/ch5/defer2/defer.go:15 50 | //!-printstack 51 | */ 52 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch5/fetch/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 148. 5 | 6 | // Fetch saves the contents of a URL into a local file. 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "io" 12 | "net/http" 13 | "os" 14 | "path" 15 | ) 16 | 17 | //!+ 18 | // Fetch downloads the URL and returns the 19 | // name and length of the local file. 20 | func fetch(url string) (filename string, n int64, err error) { 21 | resp, err := http.Get(url) 22 | if err != nil { 23 | return "", 0, err 24 | } 25 | defer resp.Body.Close() 26 | 27 | local := path.Base(resp.Request.URL.Path) 28 | if local == "/" { 29 | local = "index.html" 30 | } 31 | f, err := os.Create(local) 32 | if err != nil { 33 | return "", 0, err 34 | } 35 | n, err = io.Copy(f, resp.Body) 36 | // Close file, but prefer error from Copy, if any. 37 | if closeErr := f.Close(); err == nil { 38 | err = closeErr 39 | } 40 | return local, n, err 41 | } 42 | 43 | //!- 44 | 45 | func main() { 46 | for _, url := range os.Args[1:] { 47 | local, n, err := fetch(url) 48 | if err != nil { 49 | fmt.Fprintf(os.Stderr, "fetch %s: %v\n", url, err) 50 | continue 51 | } 52 | fmt.Fprintf(os.Stderr, "%s => %s (%d bytes).\n", url, local, n) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch5/findlinks1/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 122. 5 | //!+main 6 | 7 | // Findlinks1 prints the links in an HTML document read from standard input. 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "os" 13 | 14 | "golang.org/x/net/html" 15 | ) 16 | 17 | func main() { 18 | doc, err := html.Parse(os.Stdin) 19 | if err != nil { 20 | fmt.Fprintf(os.Stderr, "findlinks1: %v\n", err) 21 | os.Exit(1) 22 | } 23 | for _, link := range visit(nil, doc) { 24 | fmt.Println(link) 25 | } 26 | } 27 | 28 | //!-main 29 | 30 | //!+visit 31 | // visit appends to links each link found in n and returns the result. 32 | func visit(links []string, n *html.Node) []string { 33 | if n.Type == html.ElementNode && n.Data == "a" { 34 | for _, a := range n.Attr { 35 | if a.Key == "href" { 36 | links = append(links, a.Val) 37 | } 38 | } 39 | } 40 | for c := n.FirstChild; c != nil; c = c.NextSibling { 41 | links = visit(links, c) 42 | } 43 | return links 44 | } 45 | 46 | //!-visit 47 | 48 | /* 49 | //!+html 50 | package html 51 | 52 | type Node struct { 53 | Type NodeType 54 | Data string 55 | Attr []Attribute 56 | FirstChild, NextSibling *Node 57 | } 58 | 59 | type NodeType int32 60 | 61 | const ( 62 | ErrorNode NodeType = iota 63 | TextNode 64 | DocumentNode 65 | ElementNode 66 | CommentNode 67 | DoctypeNode 68 | ) 69 | 70 | type Attribute struct { 71 | Key, Val string 72 | } 73 | 74 | func Parse(r io.Reader) (*Node, error) 75 | //!-html 76 | */ 77 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch5/findlinks3/findlinks.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 139. 5 | 6 | // Findlinks3 crawls the web, starting with the URLs on the command line. 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "log" 12 | "os" 13 | 14 | "gopl.io/ch5/links" 15 | ) 16 | 17 | //!+breadthFirst 18 | // breadthFirst calls f for each item in the worklist. 19 | // Any items returned by f are added to the worklist. 20 | // f is called at most once for each item. 21 | func breadthFirst(f func(item string) []string, worklist []string) { 22 | seen := make(map[string]bool) 23 | for len(worklist) > 0 { 24 | items := worklist 25 | worklist = nil 26 | for _, item := range items { 27 | if !seen[item] { 28 | seen[item] = true 29 | worklist = append(worklist, f(item)...) 30 | } 31 | } 32 | } 33 | } 34 | 35 | //!-breadthFirst 36 | 37 | //!+crawl 38 | func crawl(url string) []string { 39 | fmt.Println(url) 40 | list, err := links.Extract(url) 41 | if err != nil { 42 | log.Print(err) 43 | } 44 | return list 45 | } 46 | 47 | //!-crawl 48 | 49 | //!+main 50 | func main() { 51 | // Crawl the web breadth-first, 52 | // starting from the command-line arguments. 53 | breadthFirst(crawl, os.Args[1:]) 54 | } 55 | 56 | //!-main 57 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch5/outline/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 123. 5 | 6 | // Outline prints the outline of an HTML document tree. 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "os" 12 | 13 | "golang.org/x/net/html" 14 | ) 15 | 16 | //!+ 17 | func main() { 18 | doc, err := html.Parse(os.Stdin) 19 | if err != nil { 20 | fmt.Fprintf(os.Stderr, "outline: %v\n", err) 21 | os.Exit(1) 22 | } 23 | outline(nil, doc) 24 | } 25 | 26 | func outline(stack []string, n *html.Node) { 27 | if n.Type == html.ElementNode { 28 | stack = append(stack, n.Data) // push tag 29 | fmt.Println(stack) 30 | } 31 | for c := n.FirstChild; c != nil; c = c.NextSibling { 32 | outline(stack, c) 33 | } 34 | } 35 | 36 | //!- 37 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch5/squares/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 135. 5 | 6 | // The squares program demonstrates a function value with state. 7 | package main 8 | 9 | import "fmt" 10 | 11 | //!+ 12 | // squares returns a function that returns 13 | // the next square number each time it is called. 14 | func squares() func() int { 15 | var x int 16 | return func() int { 17 | x++ 18 | return x * x 19 | } 20 | } 21 | 22 | func main() { 23 | f := squares() 24 | fmt.Println(f()) // "1" 25 | fmt.Println(f()) // "4" 26 | fmt.Println(f()) // "9" 27 | fmt.Println(f()) // "16" 28 | } 29 | 30 | //!- 31 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch5/sum/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 142. 5 | 6 | // The sum program demonstrates a variadic function. 7 | package main 8 | 9 | import "fmt" 10 | 11 | //!+ 12 | func sum(vals ...int) int { 13 | total := 0 14 | for _, val := range vals { 15 | total += val 16 | } 17 | return total 18 | } 19 | 20 | //!- 21 | 22 | func main() { 23 | //!+main 24 | fmt.Println(sum()) // "0" 25 | fmt.Println(sum(3)) // "3" 26 | fmt.Println(sum(1, 2, 3, 4)) // "10" 27 | //!-main 28 | 29 | //!+slice 30 | values := []int{1, 2, 3, 4} 31 | fmt.Println(sum(values...)) // "10" 32 | //!-slice 33 | } 34 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch5/trace/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 146. 5 | 6 | // The trace program uses defer to add entry/exit diagnostics to a function. 7 | package main 8 | 9 | import ( 10 | "log" 11 | "time" 12 | ) 13 | 14 | //!+main 15 | func bigSlowOperation() { 16 | defer trace("bigSlowOperation")() // don't forget the extra parentheses 17 | // ...lots of work... 18 | time.Sleep(10 * time.Second) // simulate slow operation by sleeping 19 | } 20 | 21 | func trace(msg string) func() { 22 | start := time.Now() 23 | log.Printf("enter %s", msg) 24 | return func() { log.Printf("exit %s (%s)", msg, time.Since(start)) } 25 | } 26 | 27 | //!-main 28 | 29 | func main() { 30 | bigSlowOperation() 31 | } 32 | 33 | /* 34 | !+output 35 | $ go build gopl.io/ch5/trace 36 | $ ./trace 37 | 2015/11/18 09:53:26 enter bigSlowOperation 38 | 2015/11/18 09:53:36 exit bigSlowOperation (10.000589217s) 39 | !-output 40 | */ 41 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch5/wait/wait.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 130. 5 | 6 | // The wait program waits for an HTTP server to start responding. 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "log" 12 | "net/http" 13 | "os" 14 | "time" 15 | ) 16 | 17 | //!+ 18 | // WaitForServer attempts to contact the server of a URL. 19 | // It tries for one minute using exponential back-off. 20 | // It reports an error if all attempts fail. 21 | func WaitForServer(url string) error { 22 | const timeout = 1 * time.Minute 23 | deadline := time.Now().Add(timeout) 24 | for tries := 0; time.Now().Before(deadline); tries++ { 25 | _, err := http.Head(url) 26 | if err == nil { 27 | return nil // success 28 | } 29 | log.Printf("server not responding (%s); retrying...", err) 30 | time.Sleep(time.Second << uint(tries)) // exponential back-off 31 | } 32 | return fmt.Errorf("server %s failed to respond after %s", url, timeout) 33 | } 34 | 35 | //!- 36 | 37 | func main() { 38 | if len(os.Args) != 2 { 39 | fmt.Fprintf(os.Stderr, "usage: wait url\n") 40 | os.Exit(1) 41 | } 42 | url := os.Args[1] 43 | //!+main 44 | // (In function main.) 45 | if err := WaitForServer(url); err != nil { 46 | fmt.Fprintf(os.Stderr, "Site is down: %v\n", err) 47 | os.Exit(1) 48 | } 49 | //!-main 50 | } 51 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch6/geometry/geometry.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 156. 5 | 6 | // Package geometry defines simple types for plane geometry. 7 | //!+point 8 | package geometry 9 | 10 | import "math" 11 | 12 | type Point struct{ X, Y float64 } 13 | 14 | // traditional function 15 | func Distance(p, q Point) float64 { 16 | return math.Hypot(q.X-p.X, q.Y-p.Y) 17 | } 18 | 19 | // same thing, but as a method of the Point type 20 | func (p Point) Distance(q Point) float64 { 21 | return math.Hypot(q.X-p.X, q.Y-p.Y) 22 | } 23 | 24 | //!-point 25 | 26 | //!+path 27 | 28 | // A Path is a journey connecting the points with straight lines. 29 | type Path []Point 30 | 31 | // Distance returns the distance traveled along the path. 32 | func (path Path) Distance() float64 { 33 | sum := 0.0 34 | for i := range path { 35 | if i > 0 { 36 | sum += path[i-1].Distance(path[i]) 37 | } 38 | } 39 | return sum 40 | } 41 | 42 | //!-path 43 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch6/intset/intset_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | package intset 5 | 6 | import "fmt" 7 | 8 | func Example_one() { 9 | //!+main 10 | var x, y IntSet 11 | x.Add(1) 12 | x.Add(144) 13 | x.Add(9) 14 | fmt.Println(x.String()) // "{1 9 144}" 15 | 16 | y.Add(9) 17 | y.Add(42) 18 | fmt.Println(y.String()) // "{9 42}" 19 | 20 | x.UnionWith(&y) 21 | fmt.Println(x.String()) // "{1 9 42 144}" 22 | 23 | fmt.Println(x.Has(9), x.Has(123)) // "true false" 24 | //!-main 25 | 26 | // Output: 27 | // {1 9 144} 28 | // {9 42} 29 | // {1 9 42 144} 30 | // true false 31 | } 32 | 33 | func Example_two() { 34 | var x IntSet 35 | x.Add(1) 36 | x.Add(144) 37 | x.Add(9) 38 | x.Add(42) 39 | 40 | //!+note 41 | fmt.Println(&x) // "{1 9 42 144}" 42 | fmt.Println(x.String()) // "{1 9 42 144}" 43 | fmt.Println(x) // "{[4398046511618 0 65536]}" 44 | //!-note 45 | 46 | // Output: 47 | // {1 9 42 144} 48 | // {1 9 42 144} 49 | // {[4398046511618 0 65536]} 50 | } 51 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch6/urlvalues/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 160. 5 | 6 | // The urlvalues command demonstrates a map type with methods. 7 | package main 8 | 9 | /* 10 | //!+values 11 | package url 12 | 13 | // Values maps a string key to a list of values. 14 | type Values map[string][]string 15 | 16 | // Get returns the first value associated with the given key, 17 | // or "" if there are none. 18 | func (v Values) Get(key string) string { 19 | if vs := v[key]; len(vs) > 0 { 20 | return vs[0] 21 | } 22 | return "" 23 | } 24 | 25 | // Add adds the value to key. 26 | // It appends to any existing values associated with key. 27 | func (v Values) Add(key, value string) { 28 | v[key] = append(v[key], value) 29 | } 30 | //!-values 31 | */ 32 | 33 | import ( 34 | "fmt" 35 | "net/url" 36 | ) 37 | 38 | func main() { 39 | //!+main 40 | m := url.Values{"lang": {"en"}} // direct construction 41 | m.Add("item", "1") 42 | m.Add("item", "2") 43 | 44 | fmt.Println(m.Get("lang")) // "en" 45 | fmt.Println(m.Get("q")) // "" 46 | fmt.Println(m.Get("item")) // "1" (first value) 47 | fmt.Println(m["item"]) // "[1 2]" (direct map access) 48 | 49 | m = nil 50 | fmt.Println(m.Get("item")) // "" 51 | m.Add("item", "3") // panic: assignment to entry in nil map 52 | //!-main 53 | } 54 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch7/bytecounter/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 173. 5 | 6 | // Bytecounter demonstrates an implementation of io.Writer that counts bytes. 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | ) 12 | 13 | //!+bytecounter 14 | 15 | type ByteCounter int 16 | 17 | func (c *ByteCounter) Write(p []byte) (int, error) { 18 | *c += ByteCounter(len(p)) // convert int to ByteCounter 19 | return len(p), nil 20 | } 21 | 22 | //!-bytecounter 23 | 24 | func main() { 25 | //!+main 26 | var c ByteCounter 27 | c.Write([]byte("hello")) 28 | fmt.Println(c) // "5", = len("hello") 29 | 30 | c = 0 // reset the counter 31 | var name = "Dolly" 32 | fmt.Fprintf(&c, "hello, %s", name) 33 | fmt.Println(c) // "12", = len("hello, Dolly") 34 | //!-main 35 | } 36 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch7/eval/ast.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | package eval 5 | 6 | // An Expr is an arithmetic expression. 7 | type Expr interface { 8 | // Eval returns the value of this Expr in the environment env. 9 | Eval(env Env) float64 10 | // Check reports errors in this Expr and adds its Vars to the set. 11 | Check(vars map[Var]bool) error 12 | } 13 | 14 | //!+ast 15 | 16 | // A Var identifies a variable, e.g., x. 17 | type Var string 18 | 19 | // A literal is a numeric constant, e.g., 3.141. 20 | type literal float64 21 | 22 | // A unary represents a unary operator expression, e.g., -x. 23 | type unary struct { 24 | op rune // one of '+', '-' 25 | x Expr 26 | } 27 | 28 | // A binary represents a binary operator expression, e.g., x+y. 29 | type binary struct { 30 | op rune // one of '+', '-', '*', '/' 31 | x, y Expr 32 | } 33 | 34 | // A call represents a function call expression, e.g., sin(x). 35 | type call struct { 36 | fn string // one of "pow", "sin", "sqrt" 37 | args []Expr 38 | } 39 | 40 | //!-ast 41 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch7/eval/check.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | package eval 5 | 6 | import ( 7 | "fmt" 8 | "strings" 9 | ) 10 | 11 | //!+Check 12 | 13 | func (v Var) Check(vars map[Var]bool) error { 14 | vars[v] = true 15 | return nil 16 | } 17 | 18 | func (literal) Check(vars map[Var]bool) error { 19 | return nil 20 | } 21 | 22 | func (u unary) Check(vars map[Var]bool) error { 23 | if !strings.ContainsRune("+-", u.op) { 24 | return fmt.Errorf("unexpected unary op %q", u.op) 25 | } 26 | return u.x.Check(vars) 27 | } 28 | 29 | func (b binary) Check(vars map[Var]bool) error { 30 | if !strings.ContainsRune("+-*/", b.op) { 31 | return fmt.Errorf("unexpected binary op %q", b.op) 32 | } 33 | if err := b.x.Check(vars); err != nil { 34 | return err 35 | } 36 | return b.y.Check(vars) 37 | } 38 | 39 | func (c call) Check(vars map[Var]bool) error { 40 | arity, ok := numParams[c.fn] 41 | if !ok { 42 | return fmt.Errorf("unknown function %q", c.fn) 43 | } 44 | if len(c.args) != arity { 45 | return fmt.Errorf("call to %s has %d args, want %d", 46 | c.fn, len(c.args), arity) 47 | } 48 | for _, arg := range c.args { 49 | if err := arg.Check(vars); err != nil { 50 | return err 51 | } 52 | } 53 | return nil 54 | } 55 | 56 | var numParams = map[string]int{"pow": 2, "sin": 1, "sqrt": 1} 57 | 58 | //!-Check 59 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch7/eval/coverage_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | package eval 5 | 6 | import ( 7 | "fmt" 8 | "math" 9 | "testing" 10 | ) 11 | 12 | //!+TestCoverage 13 | func TestCoverage(t *testing.T) { 14 | var tests = []struct { 15 | input string 16 | env Env 17 | want string // expected error from Parse/Check or result from Eval 18 | }{ 19 | {"x % 2", nil, "unexpected '%'"}, 20 | {"!true", nil, "unexpected '!'"}, 21 | {"log(10)", nil, `unknown function "log"`}, 22 | {"sqrt(1, 2)", nil, "call to sqrt has 2 args, want 1"}, 23 | {"sqrt(A / pi)", Env{"A": 87616, "pi": math.Pi}, "167"}, 24 | {"pow(x, 3) + pow(y, 3)", Env{"x": 9, "y": 10}, "1729"}, 25 | {"5 / 9 * (F - 32)", Env{"F": -40}, "-40"}, 26 | } 27 | 28 | for _, test := range tests { 29 | expr, err := Parse(test.input) 30 | if err == nil { 31 | err = expr.Check(map[Var]bool{}) 32 | } 33 | if err != nil { 34 | if err.Error() != test.want { 35 | t.Errorf("%s: got %q, want %q", test.input, err, test.want) 36 | } 37 | continue 38 | } 39 | 40 | got := fmt.Sprintf("%.6g", expr.Eval(test.env)) 41 | if got != test.want { 42 | t.Errorf("%s: %v => %s, want %s", 43 | test.input, test.env, got, test.want) 44 | } 45 | } 46 | } 47 | 48 | //!-TestCoverage 49 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch7/eval/eval.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 198. 5 | 6 | // Package eval provides an expression evaluator. 7 | package eval 8 | 9 | import ( 10 | "fmt" 11 | "math" 12 | ) 13 | 14 | //!+env 15 | 16 | type Env map[Var]float64 17 | 18 | //!-env 19 | 20 | //!+Eval1 21 | 22 | func (v Var) Eval(env Env) float64 { 23 | return env[v] 24 | } 25 | 26 | func (l literal) Eval(_ Env) float64 { 27 | return float64(l) 28 | } 29 | 30 | //!-Eval1 31 | 32 | //!+Eval2 33 | 34 | func (u unary) Eval(env Env) float64 { 35 | switch u.op { 36 | case '+': 37 | return +u.x.Eval(env) 38 | case '-': 39 | return -u.x.Eval(env) 40 | } 41 | panic(fmt.Sprintf("unsupported unary operator: %q", u.op)) 42 | } 43 | 44 | func (b binary) Eval(env Env) float64 { 45 | switch b.op { 46 | case '+': 47 | return b.x.Eval(env) + b.y.Eval(env) 48 | case '-': 49 | return b.x.Eval(env) - b.y.Eval(env) 50 | case '*': 51 | return b.x.Eval(env) * b.y.Eval(env) 52 | case '/': 53 | return b.x.Eval(env) / b.y.Eval(env) 54 | } 55 | panic(fmt.Sprintf("unsupported binary operator: %q", b.op)) 56 | } 57 | 58 | func (c call) Eval(env Env) float64 { 59 | switch c.fn { 60 | case "pow": 61 | return math.Pow(c.args[0].Eval(env), c.args[1].Eval(env)) 62 | case "sin": 63 | return math.Sin(c.args[0].Eval(env)) 64 | case "sqrt": 65 | return math.Sqrt(c.args[0].Eval(env)) 66 | } 67 | panic(fmt.Sprintf("unsupported function call: %s", c.fn)) 68 | } 69 | 70 | //!-Eval2 71 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch7/eval/print.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | package eval 5 | 6 | import ( 7 | "bytes" 8 | "fmt" 9 | ) 10 | 11 | // Format formats an expression as a string. 12 | // It does not attempt to remove unnecessary parens. 13 | func Format(e Expr) string { 14 | var buf bytes.Buffer 15 | write(&buf, e) 16 | return buf.String() 17 | } 18 | 19 | func write(buf *bytes.Buffer, e Expr) { 20 | switch e := e.(type) { 21 | case literal: 22 | fmt.Fprintf(buf, "%g", e) 23 | 24 | case Var: 25 | fmt.Fprintf(buf, "%s", e) 26 | 27 | case unary: 28 | fmt.Fprintf(buf, "(%c", e.op) 29 | write(buf, e.x) 30 | buf.WriteByte(')') 31 | 32 | case binary: 33 | buf.WriteByte('(') 34 | write(buf, e.x) 35 | fmt.Fprintf(buf, " %c ", e.op) 36 | write(buf, e.y) 37 | buf.WriteByte(')') 38 | 39 | case call: 40 | fmt.Fprintf(buf, "%s(", e.fn) 41 | for i, arg := range e.args { 42 | if i > 0 { 43 | buf.WriteString(", ") 44 | } 45 | write(buf, arg) 46 | } 47 | buf.WriteByte(')') 48 | 49 | default: 50 | panic(fmt.Sprintf("unknown Expr: %T", e)) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch7/http1/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 191. 5 | 6 | // Http1 is a rudimentary e-commerce server. 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "log" 12 | "net/http" 13 | ) 14 | 15 | //!+main 16 | 17 | func main() { 18 | db := database{"shoes": 50, "socks": 5} 19 | log.Fatal(http.ListenAndServe("localhost:8000", db)) 20 | } 21 | 22 | type dollars float32 23 | 24 | func (d dollars) String() string { return fmt.Sprintf("$%.2f", d) } 25 | 26 | type database map[string]dollars 27 | 28 | func (db database) ServeHTTP(w http.ResponseWriter, req *http.Request) { 29 | for item, price := range db { 30 | fmt.Fprintf(w, "%s: %s\n", item, price) 31 | } 32 | } 33 | 34 | //!-main 35 | 36 | /* 37 | //!+handler 38 | package http 39 | 40 | type Handler interface { 41 | ServeHTTP(w ResponseWriter, r *Request) 42 | } 43 | 44 | func ListenAndServe(address string, h Handler) error 45 | //!-handler 46 | */ 47 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch7/http2/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 192. 5 | 6 | // Http2 is an e-commerce server with /list and /price endpoints. 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "log" 12 | "net/http" 13 | ) 14 | 15 | func main() { 16 | db := database{"shoes": 50, "socks": 5} 17 | log.Fatal(http.ListenAndServe("localhost:8000", db)) 18 | } 19 | 20 | type dollars float32 21 | 22 | func (d dollars) String() string { return fmt.Sprintf("$%.2f", d) } 23 | 24 | type database map[string]dollars 25 | 26 | //!+handler 27 | func (db database) ServeHTTP(w http.ResponseWriter, req *http.Request) { 28 | switch req.URL.Path { 29 | case "/list": 30 | for item, price := range db { 31 | fmt.Fprintf(w, "%s: %s\n", item, price) 32 | } 33 | case "/price": 34 | item := req.URL.Query().Get("item") 35 | price, ok := db[item] 36 | if !ok { 37 | w.WriteHeader(http.StatusNotFound) // 404 38 | fmt.Fprintf(w, "no such item: %q\n", item) 39 | return 40 | } 41 | fmt.Fprintf(w, "%s\n", price) 42 | default: 43 | w.WriteHeader(http.StatusNotFound) // 404 44 | fmt.Fprintf(w, "no such page: %s\n", req.URL) 45 | } 46 | } 47 | 48 | //!-handler 49 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch7/http3/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 194. 5 | 6 | // Http3 is an e-commerce server that registers the /list and /price 7 | // endpoints by calling (*http.ServeMux).Handle. 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "log" 13 | "net/http" 14 | ) 15 | 16 | type dollars float32 17 | 18 | func (d dollars) String() string { return fmt.Sprintf("$%.2f", d) } 19 | 20 | //!+main 21 | 22 | func main() { 23 | db := database{"shoes": 50, "socks": 5} 24 | mux := http.NewServeMux() 25 | mux.Handle("/list", http.HandlerFunc(db.list)) 26 | mux.Handle("/price", http.HandlerFunc(db.price)) 27 | log.Fatal(http.ListenAndServe("localhost:8000", mux)) 28 | } 29 | 30 | type database map[string]dollars 31 | 32 | func (db database) list(w http.ResponseWriter, req *http.Request) { 33 | for item, price := range db { 34 | fmt.Fprintf(w, "%s: %s\n", item, price) 35 | } 36 | } 37 | 38 | func (db database) price(w http.ResponseWriter, req *http.Request) { 39 | item := req.URL.Query().Get("item") 40 | price, ok := db[item] 41 | if !ok { 42 | w.WriteHeader(http.StatusNotFound) // 404 43 | fmt.Fprintf(w, "no such item: %q\n", item) 44 | return 45 | } 46 | fmt.Fprintf(w, "%s\n", price) 47 | } 48 | 49 | //!-main 50 | 51 | /* 52 | //!+handlerfunc 53 | package http 54 | 55 | type HandlerFunc func(w ResponseWriter, r *Request) 56 | 57 | func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { 58 | f(w, r) 59 | } 60 | //!-handlerfunc 61 | */ 62 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch7/http3a/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 195. 5 | 6 | // Http3a is an e-commerce server that registers the /list and /price 7 | // endpoints by calling (*http.ServeMux).HandleFunc. 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "log" 13 | "net/http" 14 | ) 15 | 16 | func main() { 17 | db := database{"shoes": 50, "socks": 5} 18 | mux := http.NewServeMux() 19 | //!+main 20 | mux.HandleFunc("/list", db.list) 21 | mux.HandleFunc("/price", db.price) 22 | //!-main 23 | log.Fatal(http.ListenAndServe("localhost:8000", mux)) 24 | } 25 | 26 | type database map[string]int 27 | 28 | func (db database) list(w http.ResponseWriter, req *http.Request) { 29 | for item, price := range db { 30 | fmt.Fprintf(w, "%s: $%d\n", item, price) 31 | } 32 | } 33 | 34 | func (db database) price(w http.ResponseWriter, req *http.Request) { 35 | item := req.URL.Query().Get("item") 36 | if price, ok := db[item]; ok { 37 | fmt.Fprintf(w, "$%d\n", price) 38 | } else { 39 | w.WriteHeader(http.StatusNotFound) // 404 40 | fmt.Fprintf(w, "no such item: %q\n", item) 41 | } 42 | } 43 | 44 | /* 45 | //!+handlerfunc 46 | package http 47 | 48 | type HandlerFunc func(w ResponseWriter, r *Request) 49 | 50 | func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { 51 | f(w, r) 52 | } 53 | //!-handlerfunc 54 | */ 55 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch7/http4/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 195. 5 | 6 | // Http4 is an e-commerce server that registers the /list and /price 7 | // endpoint by calling http.HandleFunc. 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "log" 13 | "net/http" 14 | ) 15 | 16 | //!+main 17 | 18 | func main() { 19 | db := database{"shoes": 50, "socks": 5} 20 | http.HandleFunc("/list", db.list) 21 | http.HandleFunc("/price", db.price) 22 | log.Fatal(http.ListenAndServe("localhost:8000", nil)) 23 | } 24 | 25 | //!-main 26 | 27 | type dollars float32 28 | 29 | func (d dollars) String() string { return fmt.Sprintf("$%.2f", d) } 30 | 31 | type database map[string]dollars 32 | 33 | func (db database) list(w http.ResponseWriter, req *http.Request) { 34 | for item, price := range db { 35 | fmt.Fprintf(w, "%s: %s\n", item, price) 36 | } 37 | } 38 | 39 | func (db database) price(w http.ResponseWriter, req *http.Request) { 40 | item := req.URL.Query().Get("item") 41 | if price, ok := db[item]; ok { 42 | fmt.Fprintf(w, "%s\n", price) 43 | } else { 44 | w.WriteHeader(http.StatusNotFound) // 404 45 | fmt.Fprintf(w, "no such item: %q\n", item) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch7/sleep/sleep.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 179. 5 | 6 | // The sleep program sleeps for a specified period of time. 7 | package main 8 | 9 | import ( 10 | "flag" 11 | "fmt" 12 | "time" 13 | ) 14 | 15 | //!+sleep 16 | var period = flag.Duration("period", 1*time.Second, "sleep period") 17 | 18 | func main() { 19 | flag.Parse() 20 | fmt.Printf("Sleeping for %v...", *period) 21 | time.Sleep(*period) 22 | fmt.Println() 23 | } 24 | 25 | //!-sleep 26 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch7/tempflag/tempflag.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 181. 5 | 6 | // Tempflag prints the value of its -temp (temperature) flag. 7 | package main 8 | 9 | import ( 10 | "flag" 11 | "fmt" 12 | 13 | "gopl.io/ch7/tempconv" 14 | ) 15 | 16 | //!+ 17 | var temp = tempconv.CelsiusFlag("temp", 20.0, "the temperature") 18 | 19 | func main() { 20 | flag.Parse() 21 | fmt.Println(*temp) 22 | } 23 | 24 | //!- 25 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch7/xmlselect/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 214. 5 | //!+ 6 | 7 | // Xmlselect prints the text of selected elements of an XML document. 8 | package main 9 | 10 | import ( 11 | "encoding/xml" 12 | "fmt" 13 | "io" 14 | "os" 15 | "strings" 16 | ) 17 | 18 | func main() { 19 | dec := xml.NewDecoder(os.Stdin) 20 | var stack []string // stack of element names 21 | for { 22 | tok, err := dec.Token() 23 | if err == io.EOF { 24 | break 25 | } else if err != nil { 26 | fmt.Fprintf(os.Stderr, "xmlselect: %v\n", err) 27 | os.Exit(1) 28 | } 29 | switch tok := tok.(type) { 30 | case xml.StartElement: 31 | stack = append(stack, tok.Name.Local) // push 32 | case xml.EndElement: 33 | stack = stack[:len(stack)-1] // pop 34 | case xml.CharData: 35 | if containsAll(stack, os.Args[1:]) { 36 | fmt.Printf("%s: %s\n", strings.Join(stack, " "), tok) 37 | } 38 | } 39 | } 40 | } 41 | 42 | // containsAll reports whether x contains the elements of y, in order. 43 | func containsAll(x, y []string) bool { 44 | for len(y) <= len(x) { 45 | if len(y) == 0 { 46 | return true 47 | } 48 | if x[0] == y[0] { 49 | y = y[1:] 50 | } 51 | x = x[1:] 52 | } 53 | return false 54 | } 55 | 56 | //!- 57 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch8/clock1/clock.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 219. 5 | //!+ 6 | 7 | // Clock1 is a TCP server that periodically writes the time. 8 | package main 9 | 10 | import ( 11 | "io" 12 | "log" 13 | "net" 14 | "time" 15 | ) 16 | 17 | func main() { 18 | listener, err := net.Listen("tcp", "localhost:8000") 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | for { 23 | conn, err := listener.Accept() 24 | if err != nil { 25 | log.Print(err) // e.g., connection aborted 26 | continue 27 | } 28 | handleConn(conn) // handle one connection at a time 29 | } 30 | } 31 | 32 | func handleConn(c net.Conn) { 33 | defer c.Close() 34 | for { 35 | _, err := io.WriteString(c, time.Now().Format("15:04:05\n")) 36 | if err != nil { 37 | return // e.g., client disconnected 38 | } 39 | time.Sleep(1 * time.Second) 40 | } 41 | } 42 | 43 | //!- 44 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch8/clock2/clock.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 222. 5 | 6 | // Clock is a TCP server that periodically writes the time. 7 | package main 8 | 9 | import ( 10 | "io" 11 | "log" 12 | "net" 13 | "time" 14 | ) 15 | 16 | func handleConn(c net.Conn) { 17 | defer c.Close() 18 | for { 19 | _, err := io.WriteString(c, time.Now().Format("15:04:05\n")) 20 | if err != nil { 21 | return // e.g., client disconnected 22 | } 23 | time.Sleep(1 * time.Second) 24 | } 25 | } 26 | 27 | func main() { 28 | listener, err := net.Listen("tcp", "localhost:8000") 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | //!+ 33 | for { 34 | conn, err := listener.Accept() 35 | if err != nil { 36 | log.Print(err) // e.g., connection aborted 37 | continue 38 | } 39 | go handleConn(conn) // handle connections concurrently 40 | } 41 | //!- 42 | } 43 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch8/countdown1/countdown.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 244. 5 | 6 | // Countdown implements the countdown for a rocket launch. 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "time" 12 | ) 13 | 14 | //!+ 15 | func main() { 16 | fmt.Println("Commencing countdown.") 17 | tick := time.Tick(1 * time.Second) 18 | for countdown := 10; countdown > 0; countdown-- { 19 | fmt.Println(countdown) 20 | <-tick 21 | } 22 | launch() 23 | } 24 | 25 | //!- 26 | 27 | func launch() { 28 | fmt.Println("Lift off!") 29 | } 30 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch8/countdown2/countdown.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 244. 5 | 6 | // Countdown implements the countdown for a rocket launch. 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "os" 12 | "time" 13 | ) 14 | 15 | //!+ 16 | 17 | func main() { 18 | // ...create abort channel... 19 | 20 | //!- 21 | 22 | //!+abort 23 | abort := make(chan struct{}) 24 | go func() { 25 | os.Stdin.Read(make([]byte, 1)) // read a single byte 26 | abort <- struct{}{} 27 | }() 28 | //!-abort 29 | 30 | //!+ 31 | fmt.Println("Commencing countdown. Press return to abort.") 32 | select { 33 | case <-time.After(10 * time.Second): 34 | // Do nothing. 35 | case <-abort: 36 | fmt.Println("Launch aborted!") 37 | return 38 | } 39 | launch() 40 | } 41 | 42 | //!- 43 | 44 | func launch() { 45 | fmt.Println("Lift off!") 46 | } 47 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch8/countdown3/countdown.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 246. 5 | 6 | // Countdown implements the countdown for a rocket launch. 7 | package main 8 | 9 | // NOTE: the ticker goroutine never terminates if the launch is aborted. 10 | // This is a "goroutine leak". 11 | 12 | import ( 13 | "fmt" 14 | "os" 15 | "time" 16 | ) 17 | 18 | //!+ 19 | 20 | func main() { 21 | // ...create abort channel... 22 | 23 | //!- 24 | 25 | abort := make(chan struct{}) 26 | go func() { 27 | os.Stdin.Read(make([]byte, 1)) // read a single byte 28 | abort <- struct{}{} 29 | }() 30 | 31 | //!+ 32 | fmt.Println("Commencing countdown. Press return to abort.") 33 | tick := time.Tick(1 * time.Second) 34 | for countdown := 10; countdown > 0; countdown-- { 35 | fmt.Println(countdown) 36 | select { 37 | case <-tick: 38 | // Do nothing. 39 | case <-abort: 40 | fmt.Println("Launch aborted!") 41 | return 42 | } 43 | } 44 | launch() 45 | } 46 | 47 | //!- 48 | 49 | func launch() { 50 | fmt.Println("Lift off!") 51 | } 52 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch8/crawl2/findlinks.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 241. 5 | 6 | // Crawl2 crawls web links starting with the command-line arguments. 7 | // 8 | // This version uses a buffered channel as a counting semaphore 9 | // to limit the number of concurrent calls to links.Extract. 10 | package main 11 | 12 | import ( 13 | "fmt" 14 | "log" 15 | "os" 16 | 17 | "gopl.io/ch5/links" 18 | ) 19 | 20 | //!+sema 21 | // tokens is a counting semaphore used to 22 | // enforce a limit of 20 concurrent requests. 23 | var tokens = make(chan struct{}, 20) 24 | 25 | func crawl(url string) []string { 26 | fmt.Println(url) 27 | tokens <- struct{}{} // acquire a token 28 | list, err := links.Extract(url) 29 | <-tokens // release the token 30 | 31 | if err != nil { 32 | log.Print(err) 33 | } 34 | return list 35 | } 36 | 37 | //!-sema 38 | 39 | //!+ 40 | func main() { 41 | worklist := make(chan []string) 42 | var n int // number of pending sends to worklist 43 | 44 | // Start with the command-line arguments. 45 | n++ 46 | go func() { worklist <- os.Args[1:] }() 47 | 48 | // Crawl the web concurrently. 49 | seen := make(map[string]bool) 50 | for ; n > 0; n-- { 51 | list := <-worklist 52 | for _, link := range list { 53 | if !seen[link] { 54 | seen[link] = true 55 | n++ 56 | go func(link string) { 57 | worklist <- crawl(link) 58 | }(link) 59 | } 60 | } 61 | } 62 | } 63 | 64 | //!- 65 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch8/crawl3/findlinks.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 243. 5 | 6 | // Crawl3 crawls web links starting with the command-line arguments. 7 | // 8 | // This version uses bounded parallelism. 9 | // For simplicity, it does not address the termination problem. 10 | // 11 | package main 12 | 13 | import ( 14 | "fmt" 15 | "log" 16 | "os" 17 | 18 | "gopl.io/ch5/links" 19 | ) 20 | 21 | func crawl(url string) []string { 22 | fmt.Println(url) 23 | list, err := links.Extract(url) 24 | if err != nil { 25 | log.Print(err) 26 | } 27 | return list 28 | } 29 | 30 | //!+ 31 | func main() { 32 | worklist := make(chan []string) // lists of URLs, may have duplicates 33 | unseenLinks := make(chan string) // de-duplicated URLs 34 | 35 | // Add command-line arguments to worklist. 36 | go func() { worklist <- os.Args[1:] }() 37 | 38 | // Create 20 crawler goroutines to fetch each unseen link. 39 | for i := 0; i < 20; i++ { 40 | go func() { 41 | for link := range unseenLinks { 42 | foundLinks := crawl(link) 43 | go func() { worklist <- foundLinks }() 44 | } 45 | }() 46 | } 47 | 48 | // The main goroutine de-duplicates worklist items 49 | // and sends the unseen ones to the crawlers. 50 | seen := make(map[string]bool) 51 | for list := range worklist { 52 | for _, link := range list { 53 | if !seen[link] { 54 | seen[link] = true 55 | unseenLinks <- link 56 | } 57 | } 58 | } 59 | } 60 | 61 | //!- 62 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch8/netcat1/netcat.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 221. 5 | //!+ 6 | 7 | // Netcat1 is a read-only TCP client. 8 | package main 9 | 10 | import ( 11 | "io" 12 | "log" 13 | "net" 14 | "os" 15 | ) 16 | 17 | func main() { 18 | conn, err := net.Dial("tcp", "localhost:8000") 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | defer conn.Close() 23 | mustCopy(os.Stdout, conn) 24 | } 25 | 26 | func mustCopy(dst io.Writer, src io.Reader) { 27 | if _, err := io.Copy(dst, src); err != nil { 28 | log.Fatal(err) 29 | } 30 | } 31 | 32 | //!- 33 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch8/netcat2/netcat.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 223. 5 | 6 | // Netcat is a simple read/write client for TCP servers. 7 | package main 8 | 9 | import ( 10 | "io" 11 | "log" 12 | "net" 13 | "os" 14 | ) 15 | 16 | //!+ 17 | func main() { 18 | conn, err := net.Dial("tcp", "localhost:8000") 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | defer conn.Close() 23 | go mustCopy(os.Stdout, conn) 24 | mustCopy(conn, os.Stdin) 25 | } 26 | 27 | //!- 28 | 29 | func mustCopy(dst io.Writer, src io.Reader) { 30 | if _, err := io.Copy(dst, src); err != nil { 31 | log.Fatal(err) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch8/netcat3/netcat.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 227. 5 | 6 | // Netcat is a simple read/write client for TCP servers. 7 | package main 8 | 9 | import ( 10 | "io" 11 | "log" 12 | "net" 13 | "os" 14 | ) 15 | 16 | //!+ 17 | func main() { 18 | conn, err := net.Dial("tcp", "localhost:8000") 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | done := make(chan struct{}) 23 | go func() { 24 | io.Copy(os.Stdout, conn) // NOTE: ignoring errors 25 | log.Println("done") 26 | done <- struct{}{} // signal the main goroutine 27 | }() 28 | mustCopy(conn, os.Stdin) 29 | conn.Close() 30 | <-done // wait for background goroutine to finish 31 | } 32 | 33 | //!- 34 | 35 | func mustCopy(dst io.Writer, src io.Reader) { 36 | if _, err := io.Copy(dst, src); err != nil { 37 | log.Fatal(err) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch8/pipeline1/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 228. 5 | 6 | // Pipeline1 demonstrates an infinite 3-stage pipeline. 7 | package main 8 | 9 | import "fmt" 10 | 11 | //!+ 12 | func main() { 13 | naturals := make(chan int) 14 | squares := make(chan int) 15 | 16 | // Counter 17 | go func() { 18 | for x := 0; ; x++ { 19 | naturals <- x 20 | } 21 | }() 22 | 23 | // Squarer 24 | go func() { 25 | for { 26 | x := <-naturals 27 | squares <- x * x 28 | } 29 | }() 30 | 31 | // Printer (in main goroutine) 32 | for { 33 | fmt.Println(<-squares) 34 | } 35 | } 36 | 37 | //!- 38 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch8/pipeline2/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 229. 5 | 6 | // Pipeline2 demonstrates a finite 3-stage pipeline. 7 | package main 8 | 9 | import "fmt" 10 | 11 | //!+ 12 | func main() { 13 | naturals := make(chan int) 14 | squares := make(chan int) 15 | 16 | // Counter 17 | go func() { 18 | for x := 0; x < 100; x++ { 19 | naturals <- x 20 | } 21 | close(naturals) 22 | }() 23 | 24 | // Squarer 25 | go func() { 26 | for x := range naturals { 27 | squares <- x * x 28 | } 29 | close(squares) 30 | }() 31 | 32 | // Printer (in main goroutine) 33 | for x := range squares { 34 | fmt.Println(x) 35 | } 36 | } 37 | 38 | //!- 39 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch8/pipeline3/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 231. 5 | 6 | // Pipeline3 demonstrates a finite 3-stage pipeline 7 | // with range, close, and unidirectional channel types. 8 | package main 9 | 10 | import "fmt" 11 | 12 | //!+ 13 | func counter(out chan<- int) { 14 | for x := 0; x < 100; x++ { 15 | out <- x 16 | } 17 | close(out) 18 | } 19 | 20 | func squarer(out chan<- int, in <-chan int) { 21 | for v := range in { 22 | out <- v * v 23 | } 24 | close(out) 25 | } 26 | 27 | func printer(in <-chan int) { 28 | for v := range in { 29 | fmt.Println(v) 30 | } 31 | } 32 | 33 | func main() { 34 | naturals := make(chan int) 35 | squares := make(chan int) 36 | 37 | go counter(naturals) 38 | go squarer(squares, naturals) 39 | printer(squares) 40 | } 41 | 42 | //!- 43 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch8/reverb1/reverb.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 223. 5 | 6 | // Reverb1 is a TCP server that simulates an echo. 7 | package main 8 | 9 | import ( 10 | "bufio" 11 | "fmt" 12 | "log" 13 | "net" 14 | "strings" 15 | "time" 16 | ) 17 | 18 | //!+ 19 | func echo(c net.Conn, shout string, delay time.Duration) { 20 | fmt.Fprintln(c, "\t", strings.ToUpper(shout)) 21 | time.Sleep(delay) 22 | fmt.Fprintln(c, "\t", shout) 23 | time.Sleep(delay) 24 | fmt.Fprintln(c, "\t", strings.ToLower(shout)) 25 | } 26 | 27 | func handleConn(c net.Conn) { 28 | input := bufio.NewScanner(c) 29 | for input.Scan() { 30 | echo(c, input.Text(), 1*time.Second) 31 | } 32 | // NOTE: ignoring potential errors from input.Err() 33 | c.Close() 34 | } 35 | 36 | //!- 37 | 38 | func main() { 39 | l, err := net.Listen("tcp", "localhost:8000") 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | for { 44 | conn, err := l.Accept() 45 | if err != nil { 46 | log.Print(err) // e.g., connection aborted 47 | continue 48 | } 49 | go handleConn(conn) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch8/reverb2/reverb.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 224. 5 | 6 | // Reverb2 is a TCP server that simulates an echo. 7 | package main 8 | 9 | import ( 10 | "bufio" 11 | "fmt" 12 | "log" 13 | "net" 14 | "strings" 15 | "time" 16 | ) 17 | 18 | func echo(c net.Conn, shout string, delay time.Duration) { 19 | fmt.Fprintln(c, "\t", strings.ToUpper(shout)) 20 | time.Sleep(delay) 21 | fmt.Fprintln(c, "\t", shout) 22 | time.Sleep(delay) 23 | fmt.Fprintln(c, "\t", strings.ToLower(shout)) 24 | } 25 | 26 | //!+ 27 | func handleConn(c net.Conn) { 28 | input := bufio.NewScanner(c) 29 | for input.Scan() { 30 | go echo(c, input.Text(), 1*time.Second) 31 | } 32 | // NOTE: ignoring potential errors from input.Err() 33 | c.Close() 34 | } 35 | 36 | //!- 37 | 38 | func main() { 39 | l, err := net.Listen("tcp", "localhost:8000") 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | for { 44 | conn, err := l.Accept() 45 | if err != nil { 46 | log.Print(err) // e.g., connection aborted 47 | continue 48 | } 49 | go handleConn(conn) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch8/spinner/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 218. 5 | 6 | // Spinner displays an animation while computing the 45th Fibonacci number. 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "time" 12 | ) 13 | 14 | //!+ 15 | func main() { 16 | go spinner(100 * time.Millisecond) 17 | const n = 45 18 | fibN := fib(n) // slow 19 | fmt.Printf("\rFibonacci(%d) = %d\n", n, fibN) 20 | } 21 | 22 | func spinner(delay time.Duration) { 23 | for { 24 | for _, r := range `-\|/` { 25 | fmt.Printf("\r%c", r) 26 | time.Sleep(delay) 27 | } 28 | } 29 | } 30 | 31 | func fib(x int) int { 32 | if x < 2 { 33 | return x 34 | } 35 | return fib(x-1) + fib(x-2) 36 | } 37 | 38 | //!- 39 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch8/thumbnail/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // +build ignore 5 | 6 | // The thumbnail command produces thumbnails of JPEG files 7 | // whose names are provided on each line of the standard input. 8 | // 9 | // The "+build ignore" tag (see p.295) excludes this file from the 10 | // thumbnail package, but it can be compiled as a command and run like 11 | // this: 12 | // 13 | // Run with: 14 | // $ go run $GOPATH/src/gopl.io/ch8/thumbnail/main.go 15 | // foo.jpeg 16 | // ^D 17 | // 18 | package main 19 | 20 | import ( 21 | "bufio" 22 | "fmt" 23 | "log" 24 | "os" 25 | 26 | "gopl.io/ch8/thumbnail" 27 | ) 28 | 29 | func main() { 30 | input := bufio.NewScanner(os.Stdin) 31 | for input.Scan() { 32 | thumb, err := thumbnail.ImageFile(input.Text()) 33 | if err != nil { 34 | log.Print(err) 35 | continue 36 | } 37 | fmt.Println(thumb) 38 | } 39 | if err := input.Err(); err != nil { 40 | log.Fatal(err) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch9/bank1/bank.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 261. 5 | //!+ 6 | 7 | // Package bank provides a concurrency-safe bank with one account. 8 | package bank 9 | 10 | var deposits = make(chan int) // send amount to deposit 11 | var balances = make(chan int) // receive balance 12 | 13 | func Deposit(amount int) { deposits <- amount } 14 | func Balance() int { return <-balances } 15 | 16 | func teller() { 17 | var balance int // balance is confined to teller goroutine 18 | for { 19 | select { 20 | case amount := <-deposits: 21 | balance += amount 22 | case balances <- balance: 23 | } 24 | } 25 | } 26 | 27 | func init() { 28 | go teller() // start the monitor goroutine 29 | } 30 | 31 | //!- 32 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch9/bank1/bank_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | package bank_test 5 | 6 | import ( 7 | "fmt" 8 | "testing" 9 | 10 | "gopl.io/ch9/bank1" 11 | ) 12 | 13 | func TestBank(t *testing.T) { 14 | done := make(chan struct{}) 15 | 16 | // Alice 17 | go func() { 18 | bank.Deposit(200) 19 | fmt.Println("=", bank.Balance()) 20 | done <- struct{}{} 21 | }() 22 | 23 | // Bob 24 | go func() { 25 | bank.Deposit(100) 26 | done <- struct{}{} 27 | }() 28 | 29 | // Wait for both transactions. 30 | <-done 31 | <-done 32 | 33 | if got, want := bank.Balance(), 300; got != want { 34 | t.Errorf("Balance = %d, want %d", got, want) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch9/bank2/bank.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 262. 5 | 6 | // Package bank provides a concurrency-safe bank with one account. 7 | package bank 8 | 9 | //!+ 10 | var ( 11 | sema = make(chan struct{}, 1) // a binary semaphore guarding balance 12 | balance int 13 | ) 14 | 15 | func Deposit(amount int) { 16 | sema <- struct{}{} // acquire token 17 | balance = balance + amount 18 | <-sema // release token 19 | } 20 | 21 | func Balance() int { 22 | sema <- struct{}{} // acquire token 23 | b := balance 24 | <-sema // release token 25 | return b 26 | } 27 | 28 | //!- 29 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch9/bank2/bank_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | package bank_test 5 | 6 | import ( 7 | "sync" 8 | "testing" 9 | 10 | "gopl.io/ch9/bank2" 11 | ) 12 | 13 | func TestBank(t *testing.T) { 14 | // Deposit [1..1000] concurrently. 15 | var n sync.WaitGroup 16 | for i := 1; i <= 1000; i++ { 17 | n.Add(1) 18 | go func(amount int) { 19 | bank.Deposit(amount) 20 | n.Done() 21 | }(i) 22 | } 23 | n.Wait() 24 | 25 | if got, want := bank.Balance(), (1000+1)*1000/2; got != want { 26 | t.Errorf("Balance = %d, want %d", got, want) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch9/bank3/bank.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 263. 5 | 6 | // Package bank provides a concurrency-safe single-account bank. 7 | package bank 8 | 9 | //!+ 10 | import "sync" 11 | 12 | var ( 13 | mu sync.Mutex // guards balance 14 | balance int 15 | ) 16 | 17 | func Deposit(amount int) { 18 | mu.Lock() 19 | balance = balance + amount 20 | mu.Unlock() 21 | } 22 | 23 | func Balance() int { 24 | mu.Lock() 25 | b := balance 26 | mu.Unlock() 27 | return b 28 | } 29 | 30 | //!- 31 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch9/bank3/bank_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | package bank_test 5 | 6 | import ( 7 | "sync" 8 | "testing" 9 | 10 | "gopl.io/ch9/bank2" 11 | ) 12 | 13 | func TestBank(t *testing.T) { 14 | // Deposit [1..1000] concurrently. 15 | var n sync.WaitGroup 16 | for i := 1; i <= 1000; i++ { 17 | n.Add(1) 18 | go func(amount int) { 19 | bank.Deposit(amount) 20 | n.Done() 21 | }(i) 22 | } 23 | n.Wait() 24 | 25 | if got, want := bank.Balance(), (1000+1)*1000/2; got != want { 26 | t.Errorf("Balance = %d, want %d", got, want) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch9/memo1/memo.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 272. 5 | 6 | //!+ 7 | 8 | // Package memo provides a concurrency-unsafe 9 | // memoization of a function of type Func. 10 | package memo 11 | 12 | // A Memo caches the results of calling a Func. 13 | type Memo struct { 14 | f Func 15 | cache map[string]result 16 | } 17 | 18 | // Func is the type of the function to memoize. 19 | type Func func(key string) (interface{}, error) 20 | 21 | type result struct { 22 | value interface{} 23 | err error 24 | } 25 | 26 | func New(f Func) *Memo { 27 | return &Memo{f: f, cache: make(map[string]result)} 28 | } 29 | 30 | // NOTE: not concurrency-safe! 31 | func (memo *Memo) Get(key string) (interface{}, error) { 32 | res, ok := memo.cache[key] 33 | if !ok { 34 | res.value, res.err = memo.f(key) 35 | memo.cache[key] = res 36 | } 37 | return res.value, res.err 38 | } 39 | 40 | //!- 41 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch9/memo2/memo.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 275. 5 | 6 | // Package memo provides a concurrency-safe memoization a function of 7 | // type Func. Concurrent requests are serialized by a Mutex. 8 | package memo 9 | 10 | import "sync" 11 | 12 | // Func is the type of the function to memoize. 13 | type Func func(string) (interface{}, error) 14 | 15 | type result struct { 16 | value interface{} 17 | err error 18 | } 19 | 20 | func New(f Func) *Memo { 21 | return &Memo{f: f, cache: make(map[string]result)} 22 | } 23 | 24 | //!+ 25 | 26 | type Memo struct { 27 | f Func 28 | mu sync.Mutex // guards cache 29 | cache map[string]result 30 | } 31 | 32 | // Get is concurrency-safe. 33 | func (memo *Memo) Get(key string) (value interface{}, err error) { 34 | memo.mu.Lock() 35 | res, ok := memo.cache[key] 36 | if !ok { 37 | res.value, res.err = memo.f(key) 38 | memo.cache[key] = res 39 | } 40 | memo.mu.Unlock() 41 | return res.value, res.err 42 | } 43 | 44 | //!- 45 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch9/memo2/memo_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | package memo_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "gopl.io/ch9/memo2" 10 | "gopl.io/ch9/memotest" 11 | ) 12 | 13 | var httpGetBody = memotest.HTTPGetBody 14 | 15 | func Test(t *testing.T) { 16 | m := memo.New(httpGetBody) 17 | memotest.Sequential(t, m) 18 | } 19 | 20 | func TestConcurrent(t *testing.T) { 21 | m := memo.New(httpGetBody) 22 | memotest.Concurrent(t, m) 23 | } 24 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch9/memo3/memo.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 276. 5 | 6 | // Package memo provides a concurrency-safe memoization a function of 7 | // type Func. Requests for different keys run concurrently. 8 | // Concurrent requests for the same key result in duplicate work. 9 | package memo 10 | 11 | import "sync" 12 | 13 | type Memo struct { 14 | f Func 15 | mu sync.Mutex // guards cache 16 | cache map[string]result 17 | } 18 | 19 | type Func func(string) (interface{}, error) 20 | 21 | type result struct { 22 | value interface{} 23 | err error 24 | } 25 | 26 | func New(f Func) *Memo { 27 | return &Memo{f: f, cache: make(map[string]result)} 28 | } 29 | 30 | //!+ 31 | 32 | func (memo *Memo) Get(key string) (value interface{}, err error) { 33 | memo.mu.Lock() 34 | res, ok := memo.cache[key] 35 | memo.mu.Unlock() 36 | if !ok { 37 | res.value, res.err = memo.f(key) 38 | 39 | // Between the two critical sections, several goroutines 40 | // may race to compute f(key) and update the map. 41 | memo.mu.Lock() 42 | memo.cache[key] = res 43 | memo.mu.Unlock() 44 | } 45 | return res.value, res.err 46 | } 47 | 48 | //!- 49 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch9/memo3/memo_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | package memo_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "gopl.io/ch9/memo3" 10 | "gopl.io/ch9/memotest" 11 | ) 12 | 13 | var httpGetBody = memotest.HTTPGetBody 14 | 15 | func Test(t *testing.T) { 16 | m := memo.New(httpGetBody) 17 | memotest.Sequential(t, m) 18 | } 19 | 20 | func TestConcurrent(t *testing.T) { 21 | m := memo.New(httpGetBody) 22 | memotest.Concurrent(t, m) 23 | } 24 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch9/memo4/memo.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | // See page 276. 5 | 6 | // Package memo provides a concurrency-safe memoization a function of 7 | // a function. Requests for different keys proceed in parallel. 8 | // Concurrent requests for the same key block until the first completes. 9 | // This implementation uses a Mutex. 10 | package memo 11 | 12 | import "sync" 13 | 14 | // Func is the type of the function to memoize. 15 | type Func func(string) (interface{}, error) 16 | 17 | type result struct { 18 | value interface{} 19 | err error 20 | } 21 | 22 | //!+ 23 | type entry struct { 24 | res result 25 | ready chan struct{} // closed when res is ready 26 | } 27 | 28 | func New(f Func) *Memo { 29 | return &Memo{f: f, cache: make(map[string]*entry)} 30 | } 31 | 32 | type Memo struct { 33 | f Func 34 | mu sync.Mutex // guards cache 35 | cache map[string]*entry 36 | } 37 | 38 | func (memo *Memo) Get(key string) (value interface{}, err error) { 39 | memo.mu.Lock() 40 | e := memo.cache[key] 41 | if e == nil { 42 | // This is the first request for this key. 43 | // This goroutine becomes responsible for computing 44 | // the value and broadcasting the ready condition. 45 | e = &entry{ready: make(chan struct{})} 46 | memo.cache[key] = e 47 | memo.mu.Unlock() 48 | 49 | e.res.value, e.res.err = memo.f(key) 50 | 51 | close(e.ready) // broadcast ready condition 52 | } else { 53 | // This is a repeat request for this key. 54 | memo.mu.Unlock() 55 | 56 | <-e.ready // wait for ready condition 57 | } 58 | return e.res.value, e.res.err 59 | } 60 | 61 | //!- 62 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch9/memo4/memo_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | package memo_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "gopl.io/ch9/memo4" 10 | "gopl.io/ch9/memotest" 11 | ) 12 | 13 | var httpGetBody = memotest.HTTPGetBody 14 | 15 | func Test(t *testing.T) { 16 | m := memo.New(httpGetBody) 17 | memotest.Sequential(t, m) 18 | } 19 | 20 | func TestConcurrent(t *testing.T) { 21 | m := memo.New(httpGetBody) 22 | memotest.Concurrent(t, m) 23 | } 24 | -------------------------------------------------------------------------------- /vendor/gopl.io/ch9/memo5/memo_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. 2 | // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ 3 | 4 | package memo_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "gopl.io/ch9/memo5" 10 | "gopl.io/ch9/memotest" 11 | ) 12 | 13 | var httpGetBody = memotest.HTTPGetBody 14 | 15 | func Test(t *testing.T) { 16 | m := memo.New(httpGetBody) 17 | defer m.Close() 18 | memotest.Sequential(t, m) 19 | } 20 | 21 | func TestConcurrent(t *testing.T) { 22 | m := memo.New(httpGetBody) 23 | defer m.Close() 24 | memotest.Concurrent(t, m) 25 | } 26 | --------------------------------------------------------------------------------