diff --git a/TRANSLATE b/TRANSLATE new file mode 100644 index 0000000..d0b5586 --- /dev/null +++ b/TRANSLATE @@ -0,0 +1,35 @@ +Translating the Tour + +A Tour of Go is a Go program that runs as a stand-alone web server or +an App Engine app. The version available at tour.golang.org is run from +App Engine. There are several localized versions of the tour, such as +this Chinese translation, also running on App Engine: + + http://go-tour-zh.appspot.com + +The Tour contains a slide named "Go local", which lists several of +these translations. If you are a native speaker of a language not on +that list and have some experience with Go, please consider providing +a translation of the Tour in your own language. + +To translate the tour: + +1. Translate the contents of tour.article +2. Provide localized verison fo the UI strings in js/lang.js +3. Sign up to App Engine and create an app named go-tour-LL, + where LL is the two-letter country code that applies best + to your chosen language. (This shouldn't cost you anything; + the Tour App usually runs inside App Engine's free quota.) +4. Deploy your version of the Tour to that app. +5. Announce to golang-nuts@googlegroups.com + +The Tour content changes occasionally, and you should keep your +translation up to date. To follow the development of the tour, +subscribe to the go-tour-commits mailing list: + + https://groups.google.com/group/go-tour-commits + +All new commits to the go-tour repository are mailed there. + +Finally, if you have any questions about the Tour or Go, +please mail golang-nuts@googlegroups.com. diff --git a/appengine/app.yaml b/app.yaml similarity index 72% rename from appengine/app.yaml rename to app.yaml index 1311ef0..b06acf9 100644 --- a/appengine/app.yaml +++ b/app.yaml @@ -4,9 +4,6 @@ runtime: go api_version: go1 handlers: -- url: / - static_files: static/index.html - upload: static/index.html - url: /favicon.ico static_files: static/favicon.ico upload: static/favicon.ico @@ -14,5 +11,7 @@ handlers: static_dir: static - url: /talks static_dir: talks -- url: /(compile|fmt) +- url: /(|compile|fmt|script\.js) script: _go_app + +nobuild_files: (solutions|prog)/.* diff --git a/appengine/README b/appengine/README deleted file mode 100644 index d3616a0..0000000 --- a/appengine/README +++ /dev/null @@ -1,16 +0,0 @@ -This is the App Engine version of the Go Playground. - -To deploy: (instructions relative to the appengine directory) - -1. Make a copy of the static directory. - - cp -r ../static ../talks . - -2. Edit static/mode.js to set the tourMode variable to "appengine". - -3. Edit app.yaml to set the application name to something you have access to. - -4. Use appcfg.py to deploy it. - - /path/to/sdk/appcfg.py update . - diff --git a/appengine/goplay/compile.go b/appengine/goplay/compile.go deleted file mode 100644 index be1e3e7..0000000 --- a/appengine/goplay/compile.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package goplay - -import ( - "fmt" - "io" - "net/http" - - "appengine" - "appengine/urlfetch" -) - -const runUrl = "http://golang.org/compile?output=json" - -func init() { - http.HandleFunc("/compile", compile) -} - -func compile(w http.ResponseWriter, r *http.Request) { - if err := passThru(w, r); err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintln(w, "Compile server error.") - } -} - -func passThru(w io.Writer, req *http.Request) error { - c := appengine.NewContext(req) - client := urlfetch.Client(c) - defer req.Body.Close() - r, err := client.Post(runUrl, req.Header.Get("Content-type"), req.Body) - if err != nil { - c.Errorf("making POST request:", err) - return err - } - defer r.Body.Close() - if _, err := io.Copy(w, r.Body); err != nil { - c.Errorf("copying response Body:", err) - return err - } - return nil -} diff --git a/appengine/goplay/fmt.go b/appengine/goplay/fmt.go deleted file mode 100644 index e49bc72..0000000 --- a/appengine/goplay/fmt.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package goplay - -import ( - "bytes" - "encoding/json" - "go/ast" - "go/parser" - "go/printer" - "go/token" - "net/http" -) - -func init() { - http.HandleFunc("/fmt", fmtHandler) -} - -type fmtResponse struct { - Body string - Error string -} - -func fmtHandler(w http.ResponseWriter, r *http.Request) { - resp := new(fmtResponse) - body, err := gofmt(r.FormValue("body")) - if err != nil { - resp.Error = err.Error() - } else { - resp.Body = body - } - json.NewEncoder(w).Encode(resp) -} - -func gofmt(body string) (string, error) { - fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "prog.go", body, parser.ParseComments) - if err != nil { - return "", err - } - ast.SortImports(fset, f) - var buf bytes.Buffer - err = printer.Fprint(&buf, fset, f) - if err != nil { - return "", err - } - return buf.String(), nil -} diff --git a/gotour/appengine.go b/gotour/appengine.go new file mode 100644 index 0000000..a410989 --- /dev/null +++ b/gotour/appengine.go @@ -0,0 +1,103 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build appengine + +package main + +import ( + "bufio" + "bytes" + "fmt" + "io" + "net/http" + + "appengine" + "appengine/urlfetch" +) + +const runUrl = "http://golang.org/compile" + +func init() { + http.HandleFunc("/", rootHandler) + http.HandleFunc("/compile", compileHandler) + err := serveScripts("js", "playground.js") + if err != nil { + panic(err) + } + if err := initTour("."); err != nil { + panic(err) + } +} + +func rootHandler(w http.ResponseWriter, r *http.Request) { + c := appengine.NewContext(r) + if err := renderTour(w); err != nil { + c.Criticalf("template render: %v", err) + } +} + +func compileHandler(w http.ResponseWriter, r *http.Request) { + if err := passThru(w, r); err != nil { + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprintln(w, "Compile server error.") + } +} + +func passThru(w io.Writer, req *http.Request) error { + c := appengine.NewContext(req) + client := urlfetch.Client(c) + defer req.Body.Close() + r, err := client.Post(runUrl, req.Header.Get("Content-type"), req.Body) + if err != nil { + c.Errorf("making POST request:", err) + return err + } + defer r.Body.Close() + if _, err := io.Copy(w, r.Body); err != nil { + c.Errorf("copying response Body:", err) + return err + } + return nil +} + +// prepContent returns a Reader that produces the content from the given +// Reader, but strips the prefix "#appengine: " from each line. It also drops +// any non-blank like that follows a series of 1 or more lines with the prefix. +func prepContent(in io.Reader) io.Reader { + var prefix = []byte("#appengine: ") + out, w := io.Pipe() + go func() { + r := bufio.NewReader(in) + drop := false + for { + b, err := r.ReadBytes('\n') + if err != nil && err != io.EOF { + w.CloseWithError(err) + return + } + if bytes.HasPrefix(b, prefix) { + b = b[len(prefix):] + drop = true + } else if drop { + if len(b) > 1 { + b = nil + } + drop = false + } + if len(b) > 0 { + w.Write(b) + } + if err == io.EOF { + w.Close() + return + } + } + }() + return out +} + +// socketAddr returns the WebSocket handler address. +// The App Engine version does not provide a WebSocket handler. +func socketAddr() string { return "" } diff --git a/gotour/goplay.go b/gotour/goplay.go deleted file mode 100644 index 8814def..0000000 --- a/gotour/goplay.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "encoding/json" - "log" - "net/http" -) - -func init() { - http.HandleFunc("/compile", Compile) -} - -type Response struct { - Output string `json:"output"` - Errors string `json:"compile_errors"` -} - -func Compile(w http.ResponseWriter, req *http.Request) { - resp := new(Response) - out, err := compile(req) - if err != nil { - if len(out) > 0 { - resp.Errors = string(out) + "\n" + err.Error() - } else { - resp.Errors = err.Error() - } - } else { - resp.Output = string(out) - } - if err := json.NewEncoder(w).Encode(resp); err != nil { - log.Println(err) - } -} diff --git a/gotour/local.go b/gotour/local.go index f42469c..d9a8be6 100644 --- a/gotour/local.go +++ b/gotour/local.go @@ -2,33 +2,37 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build !appengine + package main import ( - "bytes" "flag" "fmt" "go/build" - "io/ioutil" + "io" "log" "net" "net/http" "os" "os/exec" "path/filepath" - "regexp" "runtime" - "strconv" - "sync" + "strings" "time" + "code.google.com/p/go.talks/pkg/socket" + // Imports so that go build/install automatically installs them. _ "bitbucket.org/mikespook/go-tour-zh/pic" _ "bitbucket.org/mikespook/go-tour-zh/tree" _ "bitbucket.org/mikespook/go-tour-zh/wc" ) -const basePkg = "bitbucket.org/mikespook/go-tour-zh/" +const ( + basePkg = "bitbucket.org/mikespook/go-tour-zh/" + socketPath = "/socket" +) var ( httpListen = flag.String("http", "127.0.0.1:3999", "host:port to listen on") @@ -39,8 +43,34 @@ var ( var ( // a source of numbers, for naming temporary files uniq = make(chan int) + + // GOPATH containing the tour packages + gopath = os.Getenv("GOPATH") + + httpAddr string ) +func isRoot(path string) bool { + _, err := os.Stat(filepath.Join(path, "tour.article")) + return err == nil +} + +func findRoot() (string, error) { + ctx := build.Default + p, err := ctx.Import(basePkg, "", build.FindOnly) + if err == nil && isRoot(p.Dir) { + return p.Dir, nil + } + tourRoot := filepath.Join(runtime.GOROOT(), "misc", "tour") + ctx.GOPATH = tourRoot + p, err = ctx.Import(basePkg, "", build.FindOnly) + if err == nil && isRoot(tourRoot) { + gopath = tourRoot + return tourRoot, nil + } + return "", fmt.Errorf("could not find go-tour content; check $GOROOT and $GOPATH") +} + func main() { flag.Parse() @@ -52,23 +82,11 @@ func main() { }() // find and serve the go tour files - p, err := build.Default.Import(basePkg, "", build.FindOnly) + root, err := findRoot() if err != nil { log.Fatalf("Couldn't find tour files: %v", err) } - root := p.Dir log.Println("Serving content from", root) - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/favicon.ico" || r.URL.Path == "/" { - fn := filepath.Join(root, "static", r.URL.Path[1:]) - http.ServeFile(w, r, fn) - return - } - http.Error(w, "not found", 404) - }) - http.Handle("/static/", http.FileServer(http.Dir(root))) - http.Handle("/talks/", http.FileServer(http.Dir(root))) - http.HandleFunc("/kill", kill) host, port, err := net.SplitHostPort(*httpListen) if err != nil { @@ -80,8 +98,34 @@ func main() { if host != "127.0.0.1" && host != "localhost" { log.Print(localhostWarning) } + httpAddr = host + ":" + port + + if err := initTour(root); err != nil { + log.Fatal(err) + } + + fs := http.FileServer(http.Dir(root)) + http.Handle("/favicon.ico", fs) + http.Handle("/static/", fs) + http.Handle("/talks/", fs) + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/" { + if err := renderTour(w); err != nil { + log.Println(err) + } + return + } + http.Error(w, "not found", 404) + }) + + http.Handle(socketPath, socket.Handler) + + err = serveScripts(filepath.Join(root, "js"), "socket.js") + if err != nil { + log.Fatal(err) + } - httpAddr := host + ":" + port go func() { url := "http://" + httpAddr if waitServer(url) && *openBrowser && startBrowser(url) { @@ -105,106 +149,26 @@ If you don't understand this message, hit Control-C to terminate this process. WARNING! WARNING! WARNING! ` -var running struct { - sync.Mutex - cmd *exec.Cmd +type response struct { + Output string `json:"output"` + Errors string `json:"compile_errors"` } -func stopRun() { - running.Lock() - if running.cmd != nil { - running.cmd.Process.Kill() - running.cmd = nil +// environ returns an execution environment containing only GO* variables +// and replacing GOPATH with the value of the global var gopath. +func environ() (env []string) { + for _, v := range os.Environ() { + if !strings.HasPrefix(v, "GO") { + continue } - running.Unlock() -} - -func kill(w http.ResponseWriter, r *http.Request) { - stopRun() + if strings.HasPrefix(v, "GOPATH=") { + v = "GOPATH=" + gopath } -var ( - commentRe = regexp.MustCompile(`(?m)^#.*\n`) - tmpdir string -) - -func init() { - // find real temporary directory (for rewriting filename in output) - var err error - tmpdir, err = filepath.EvalSymlinks(os.TempDir()) - if err != nil { - log.Fatal(err) - } + env = append(env, v) } -func compile(req *http.Request) (out []byte, err error) { - stopRun() - - // x is the base name for .go, .6, executable files - x := filepath.Join(tmpdir, "compile"+strconv.Itoa(<-uniq)) - src := x + ".go" - bin := x - if runtime.GOOS == "windows" { - bin += ".exe" - } - - // rewrite filename in error output - defer func() { - if err != nil { - // drop messages from the go tool like '# _/compile0' - out = commentRe.ReplaceAll(out, nil) - } - out = bytes.Replace(out, []byte(src+":"), []byte("main.go:"), -1) - }() - - // write body to x.go - body := []byte(req.FormValue("body")) - defer os.Remove(src) - if err = ioutil.WriteFile(src, body, 0666); err != nil { return - } - - // build x.go, creating x - dir, file := filepath.Split(src) - out, err = run(dir, "go", "build", "-o", bin, file) - defer os.Remove(bin) - if err != nil { - return - } - - // run x - return run("", bin) -} - -// run executes the specified command and returns its output and an error. -func run(dir string, args ...string) ([]byte, error) { - var buf bytes.Buffer - cmd := exec.Command(args[0], args[1:]...) - cmd.Dir = dir - cmd.Stdout = &buf - cmd.Stderr = cmd.Stdout - - // Start command and leave in 'running'. - running.Lock() - if running.cmd != nil { - defer running.Unlock() - return nil, fmt.Errorf("already running %s", running.cmd.Path) - } - if err := cmd.Start(); err != nil { - running.Unlock() - return nil, err - } - running.cmd = cmd - running.Unlock() - - // Wait for the command. Clean up, - err := cmd.Wait() - running.Lock() - if running.cmd == cmd { - running.cmd = nil - } - running.Unlock() - return buf.Bytes(), err } // waitServer waits some time for the http Server to start @@ -239,3 +203,9 @@ func startBrowser(url string) bool { cmd := exec.Command(args[0], append(args[1:], url)...) return cmd.Start() == nil } + +// prepContent for the local tour simply returns the content as-is. +func prepContent(r io.Reader) io.Reader { return r } + +// socketAddr returns the WebSocket handler address. +func socketAddr() string { return "ws://" + httpAddr + socketPath } diff --git a/gotour/tour.go b/gotour/tour.go new file mode 100644 index 0000000..180c193 --- /dev/null +++ b/gotour/tour.go @@ -0,0 +1,109 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "fmt" + "html/template" + "io" + "io/ioutil" + "net/http" + "os" + "path/filepath" + "time" + + "code.google.com/p/go.talks/pkg/present" +) + +func init() { + present.PlayEnabled = true +} + +var tourContent []byte + +// initTour loads tour.article and the relevant HTML templates from the given +// tour root, and renders the template to the tourContent global variable. +func initTour(root string) error { + // Open and parse source file. + source := filepath.Join(root, "tour.article") + f, err := os.Open(source) + if err != nil { + return err + } + defer f.Close() + doc, err := present.Parse(prepContent(f), source, 0) + if err != nil { + return err + } + + // Set up templates. + action := filepath.Join(root, "template", "action.tmpl") + tour := filepath.Join(root, "template", "tour.tmpl") + t := present.Template().Funcs(template.FuncMap{"nocode": nocode, "socketAddr": socketAddr}) + _, err = t.ParseFiles(action, tour) + if err != nil { + return err + } + + // Render. + buf := new(bytes.Buffer) + if err := doc.Render(buf, t); err != nil { + return err + } + tourContent = buf.Bytes() + return nil +} + +// renderTour writes the tour content to the provided Writer. +func renderTour(w io.Writer) error { + if tourContent == nil { + panic("renderTour called before successful initTour") + } + _, err := w.Write(tourContent) + return err +} + +// nocode returns true if the provided Section contains +// no Code elements with Play enabled. +func nocode(s present.Section) bool { + for _, e := range s.Elem { + if c, ok := e.(present.Code); ok && c.Play { + return false + } + } + return true +} + +var commonScripts = []string{ + "jquery.js", + "codemirror/lib/codemirror.js", + "codemirror/lib/go.js", + "lang.js", +} + +// serveScripts registers an HTTP handler at /script.js that serves a +// concatenated set of all the scripts specified by path relative to root. +func serveScripts(root string, path ...string) error { + modTime := time.Now() + var buf bytes.Buffer + scripts := append(commonScripts, path...) + scripts = append(scripts, "tour.js") + for _, p := range scripts { + fn := filepath.Join(root, p) + b, err := ioutil.ReadFile(fn) + if err != nil { + return err + } + fmt.Fprintf(&buf, "\n\n// **** %s ****\n\n", filepath.Base(fn)) + buf.Write(b) + } + b := buf.Bytes() + http.HandleFunc("/script.js", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-type", "application/javascript") + http.ServeContent(w, r, "", modTime, bytes.NewReader(b)) + }) + return nil +} diff --git a/static/codemirror/lib/codemirror.js b/js/codemirror/lib/codemirror.js similarity index 100% rename from static/codemirror/lib/codemirror.js rename to js/codemirror/lib/codemirror.js diff --git a/static/codemirror/lib/go.js b/js/codemirror/lib/go.js similarity index 100% rename from static/codemirror/lib/go.js rename to js/codemirror/lib/go.js diff --git a/static/jquery.js b/js/jquery.js similarity index 100% rename from static/jquery.js rename to js/jquery.js diff --git a/js/lang.js b/js/lang.js new file mode 100644 index 0000000..67a4067 --- /dev/null +++ b/js/lang.js @@ -0,0 +1,16 @@ +// Localized user interface. +var tr = { + "off": "关闭", + "on": "开启", + "syntax": "语法高亮", + "lineno": "行号", + "reset": "重置", + "format": "格式化代码", + "kill": "杀死进程", + "run": "运行", + "toc": "目录", + "prev": "向前", + "next": "向后", + "waiting": "等待远端服务器...", + "errcomm": "与远端服务器通讯异常。", +} diff --git a/js/playground.js b/js/playground.js new file mode 100644 index 0000000..a3d9539 --- /dev/null +++ b/js/playground.js @@ -0,0 +1,122 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// HACK: This is file is identical to go.talks/present's playground.js except +// the lines containing "HACK". Also, the window.playground function is removed. + +(function() { + + function lineHighlight(error) { + // HACK: hook back into tour.js. + if (window.highlightErrors) window.highlightErrors(error); // HACK + } + + function connectPlayground() { + var playbackTimeout; + + function playback(pre, events) { + if (!pre.data("cleared")) pre.empty().data("cleared", true); // HACK + function show(msg) { + // ^L clears the screen. + var msgs = msg.split("\x0c"); + if (msgs.length == 1) { + pre.text(pre.text() + msg); + return; + } + pre.text(msgs.pop()); + } + function next() { + if (events.length === 0) { + var exit = $(''); + exit.text("\nProgram exited."); + exit.appendTo(pre); + return; + } + var e = events.shift(); + if (e.Delay === 0) { + show(e.Message); + next(); + } else { + playbackTimeout = setTimeout(function() { + show(e.Message); + next(); + }, e.Delay / 1000000); + } + } + next(); + } + + function stopPlayback() { + clearTimeout(playbackTimeout); + } + + function setOutput(output, events, error) { + stopPlayback(); + output.empty(); + + // Display errors. + if (error) { + lineHighlight(error); + output.addClass("error").text(error); + return; + } + + // Display image output. + if (events.length > 0 && events[0].Message.indexOf("IMAGE:") === 0) { + var out = ""; + for (var i = 0; i < events.length; i++) { + out += events[i].Message; + } + var url = "data:image/png;base64," + out.substr(6); + $("").attr("src", url).appendTo(output); + return; + } + + // Play back events. + if (events !== null) { + playback(output, events); + } + } + + var seq = 0; + function runFunc(body, output) { + output = $(output); + seq++; + var cur = seq; + var data = { + "version": 2, + "body": body + }; + $.ajax("/compile", { + data: data, + type: "POST", + dataType: "json", + success: function(data) { + if (seq != cur) { + return; + } + if (!data) { + return; + } + if (data.Errors) { + setOutput(output, null, data.Errors); + return; + } + setOutput(output, data.Events, false); + }, + error: function() { + output.addClass("error").text( + "Error communicating with remote server." + ); + } + }); + return stopPlayback; + } + + return runFunc; + } + + window.connectPlayground = connectPlayground; + +})(); diff --git a/js/socket.js b/js/socket.js new file mode 100644 index 0000000..24b209c --- /dev/null +++ b/js/socket.js @@ -0,0 +1,74 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// HACK: This is file is identical to go.talks/present's socket.js except the +// lines containing "HACK". Please keep this file in sync. + +(function() { + "use strict"; + + var websocket, outputs = {}; + + function onClose() { + window.alert('websocket connection closed'); + } + + function sendMessage(m) { + websocket.send(JSON.stringify(m)); + } + + function onMessage(e) { + var m = JSON.parse(e.data); + var o = outputs[m.Id]; + if (o === null) { + return; + } + if (!$(o).data("cleared")) $(o).empty().data("cleared", true); // HACK + if (m.Kind === "stdout" || m.Kind === "stderr") { + showMessage(o, m.Body, m.Kind); + } + if (m.Kind === "end") { + var s = "Program exited"; + if (m.Body !== "") { + s += ": " + m.Body; + } else { + s += "."; + } + s += "\n"; + showMessage(o, s, "system"); + } + } + + function showMessage(o, m, className) { + // HACK: hook back into tour.js. + if (className == "stderr" && window.highlightErrors) { // HACK + window.highlightErrors(m); // HACK + } // HACK + var span = document.createElement("span"); + var needScroll = (o.scrollTop + o.offsetHeight) == o.scrollHeight; + m = m.replace(/&/g, "&"); + m = m.replace(/').insertBefore('#slides').hide(); $tocdiv.append($('

'+L('toc')+'

')); var $toc = $('
    ').appendTo($tocdiv); @@ -69,11 +63,11 @@ function init() { // set up playground editor editor = CodeMirror.fromTextArea(document.getElementById('editor'), { - theme: "plain", + theme: "default", matchBrackets: true, - indentUnit: 8, - tabSize: 8, - indentWithTabs: true, + indentUnit: 4, + tabSize: 4, + indentWithTabs: false, mode: "text/x-go", lineNumbers: true, extraKeys: { @@ -200,7 +194,7 @@ function show(i) { } else { $('#workspace').show(); $output.empty(); - editor.setValue(load(i) || $s.find('pre.source').text()); + editor.setValue(load(i) || $s.find('div.source').text()); editor.focus(); } @@ -215,7 +209,7 @@ function show(i) { } function reset() { - editor.setValue($(slide).find('pre.source').text()); + editor.setValue($(slide).find('div.source').text()); save(slidenum); } @@ -279,47 +273,31 @@ $(window).unload(function() { save(slidenum); }); +var runFunc, stopFunc; -var seq = 0; - +function body() { + return editor.getValue(); +} +function loading() { + $output.html('
    '+L('waiting')+'
    '); +} function run() { - seq++; - var cur = seq; - $output.html('
    '+L('waiting')+'
    '); - $.ajax("/compile", { - data: {"body": editor.getValue()}, - type: "POST", - dataType: "json", - success: function(data) { - if (seq !== cur) { - return; - } - $output.empty(); - if (data.compile_errors) { - $('
    ').text(data.compile_errors).appendTo($output);
    -				highlightErrors(data.compile_errors);
    -			}
    -			if (/^IMAGE:/.exec(data.output)) {
    -				var img = $('').attr('src',
    -					'data:image/png;base64,' + data.output.substr(6));
    -				$output.empty().append(img);
    -				return;
    -			}
    -			$('
    ').text(data.output).appendTo($output);
    -		},
    -		error: function() {
    -			$output.empty();
    -			$('
    ').text(L('errcomm')).appendTo($output);
    -		}
    -	});
    +	loading();
    +	stopFunc = runFunc(body(), $output.find("pre")[0]);
    +}
    +
    +function kill() {
    +	if (stopFunc) stopFunc();
     }
     
    +var seq = 0;
    +
     function format() {
     	seq++;
     	var cur = seq;
    -	$output.html('
    '+L('waiting')+'
    '); + loading(); $.ajax("/fmt", { - data: {"body": editor.getValue()}, + data: {"body": body()}, type: "POST", dataType: "json", success: function(data) { @@ -340,10 +318,6 @@ function format() { }); } -function kill() { - $.ajax("/kill"); -} - function highlightErrors(text) { if (!editor || !text) { return; @@ -362,6 +336,9 @@ function highlightErrors(text) { }); } +// Nasty hack to make this function available to playground.js and socket.js. +window.highlightErrors = highlightErrors; + function getcookie(name) { if (document.cookie.length > 0) { var start = document.cookie.indexOf(name + '='); @@ -384,4 +361,12 @@ function setcookie(name, value, expire) { ((expire === undefined) ? '' : ';expires=' + expdate.toGMTString()); } +if (window.connectPlayground) { + runFunc = window.connectPlayground(window.socketAddr); +} else { + // If this message is logged, + // we have neglected to include socket.js or playground.js. + console.log("No playground transport available."); +} + }()); diff --git a/prog/advanced-exercise-complex-cube-roots.go b/prog/advanced-exercise-complex-cube-roots.go new file mode 100644 index 0000000..d79e11b --- /dev/null +++ b/prog/advanced-exercise-complex-cube-roots.go @@ -0,0 +1,10 @@ +package main + +import "fmt" + +func Cbrt(x complex128) complex128 { +} + +func main() { + fmt.Println(Cbrt(2)) +} diff --git a/prog/basic-types.go b/prog/basic-types.go new file mode 100644 index 0000000..1dbdf14 --- /dev/null +++ b/prog/basic-types.go @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "math/cmplx" +) + +var ( + ToBe bool = false + MaxInt uint64 = 1<<64 - 1 + z complex128 = cmplx.Sqrt(-5 + 12i) +) + +func main() { + const f = "%T(%v)\n" + fmt.Printf(f, ToBe, ToBe) + fmt.Printf(f, MaxInt, MaxInt) + fmt.Printf(f, z, z) +} diff --git a/prog/buffered-channels.go b/prog/buffered-channels.go new file mode 100644 index 0000000..ef53bf2 --- /dev/null +++ b/prog/buffered-channels.go @@ -0,0 +1,11 @@ +package main + +import "fmt" + +func main() { + c := make(chan int, 2) + c <- 1 + c <- 2 + fmt.Println(<-c) + fmt.Println(<-c) +} diff --git a/prog/channels.go b/prog/channels.go new file mode 100644 index 0000000..863097f --- /dev/null +++ b/prog/channels.go @@ -0,0 +1,22 @@ +package main + +import "fmt" + +func sum(a []int, c chan int) { + sum := 0 + for _, v := range a { + sum += v + } + c <- sum // 将和送入 c +} + +func main() { + a := []int{7, 2, 8, -9, 4, 0} + + c := make(chan int) + go sum(a[:len(a)/2], c) + go sum(a[len(a)/2:], c) + x, y := <-c, <-c // 从 c 中获取 + + fmt.Println(x, y, x+y) +} diff --git a/prog/constants.go b/prog/constants.go new file mode 100644 index 0000000..94d0652 --- /dev/null +++ b/prog/constants.go @@ -0,0 +1,14 @@ +package main + +import "fmt" + +const Pi = 3.14 + +func main() { + const World = "世界" + fmt.Println("Hello", World) + fmt.Println("Happy", Pi, "Day") + + const Truth = true + fmt.Println("Go rules?", Truth) +} diff --git a/prog/default-selection.go b/prog/default-selection.go new file mode 100644 index 0000000..ca5873c --- /dev/null +++ b/prog/default-selection.go @@ -0,0 +1,23 @@ +package main + +import ( + "fmt" + "time" +) + +func main() { + tick := time.Tick(1e8) + boom := time.After(5e8) + for { + select { + case <-tick: + fmt.Println("tick.") + case <-boom: + fmt.Println("BOOM!") + return + default: + fmt.Println(" .") + time.Sleep(5e7) + } + } +} diff --git a/prog/errors.go b/prog/errors.go new file mode 100644 index 0000000..b98b4a4 --- /dev/null +++ b/prog/errors.go @@ -0,0 +1,29 @@ +package main + +import ( + "fmt" + "time" +) + +type MyError struct { + When time.Time + What string +} + +func (e *MyError) Error() string { + return fmt.Sprintf("at %v, %s", + e.When, e.What) +} + +func run() error { + return &MyError{ + time.Now(), + "it didn't work", + } +} + +func main() { + if err := run(); err != nil { + fmt.Println(err) + } +} diff --git a/prog/exercise-equivalent-binary-trees.go b/prog/exercise-equivalent-binary-trees.go new file mode 100644 index 0000000..b711444 --- /dev/null +++ b/prog/exercise-equivalent-binary-trees.go @@ -0,0 +1,12 @@ +package main + +import "code.google.com/p/go-tour/tree" + +// Walk 步进 tree t 将所有的值从 tree 发送到 channel ch。 +func Walk(t *tree.Tree, ch chan int) + +// Same 检测树 t1 和 t2 是否含有相同的值。 +func Same(t1, t2 *tree.Tree) bool + +func main() { +} diff --git a/prog/exercise-errors.go b/prog/exercise-errors.go new file mode 100644 index 0000000..d77afea --- /dev/null +++ b/prog/exercise-errors.go @@ -0,0 +1,14 @@ +package main + +import ( + "fmt" +) + +func Sqrt(f float64) (float64, error) { + return 0, nil +} + +func main() { + fmt.Println(Sqrt(2)) + fmt.Println(Sqrt(-2)) +} diff --git a/prog/exercise-fibonacci-closure.go b/prog/exercise-fibonacci-closure.go new file mode 100644 index 0000000..2d5950b --- /dev/null +++ b/prog/exercise-fibonacci-closure.go @@ -0,0 +1,14 @@ +package main + +import "fmt" + +// fibonacci 函数会返回一个返回 int 的函数。 +func fibonacci() func() int { +} + +func main() { + f := fibonacci() + for i := 0; i < 10; i++ { + fmt.Println(f()) + } +} diff --git a/prog/exercise-http-handlers.go b/prog/exercise-http-handlers.go new file mode 100644 index 0000000..74d82cd --- /dev/null +++ b/prog/exercise-http-handlers.go @@ -0,0 +1,10 @@ +package main + +import ( + "net/http" +) + +func main() { + // your http.Handle calls here + http.ListenAndServe("localhost:4000", nil) +} diff --git a/prog/exercise-images.go b/prog/exercise-images.go new file mode 100644 index 0000000..d09d892 --- /dev/null +++ b/prog/exercise-images.go @@ -0,0 +1,13 @@ +package main + +import ( + "code.google.com/p/go-tour/pic" + "image" +) + +type Image struct{} + +func main() { + m := Image{} + pic.ShowImage(m) +} diff --git a/prog/exercise-loops-and-functions.go b/prog/exercise-loops-and-functions.go new file mode 100644 index 0000000..5f2bf65 --- /dev/null +++ b/prog/exercise-loops-and-functions.go @@ -0,0 +1,12 @@ +package main + +import ( + "fmt" +) + +func Sqrt(x float64) float64 { +} + +func main() { + fmt.Println(Sqrt(2)) +} diff --git a/prog/exercise-maps.go b/prog/exercise-maps.go new file mode 100644 index 0000000..4234fe4 --- /dev/null +++ b/prog/exercise-maps.go @@ -0,0 +1,13 @@ +package main + +import ( + "code.google.com/p/go-tour/wc" +) + +func WordCount(s string) map[string]int { + return map[string]int{"x": 1} +} + +func main() { + wc.Test(WordCount) +} diff --git a/prog/exercise-rot-reader.go b/prog/exercise-rot-reader.go new file mode 100644 index 0000000..210cc99 --- /dev/null +++ b/prog/exercise-rot-reader.go @@ -0,0 +1,18 @@ +package main + +import ( + "io" + "os" + "strings" +) + +type rot13Reader struct { + r io.Reader +} + +func main() { + s := strings.NewReader( + "Lbh penpxrq gur pbqr!") + r := rot13Reader{s} + io.Copy(os.Stdout, &r) +} diff --git a/prog/exercise-slices.go b/prog/exercise-slices.go new file mode 100644 index 0000000..b153353 --- /dev/null +++ b/prog/exercise-slices.go @@ -0,0 +1,10 @@ +package main + +import "code.google.com/p/go-tour/pic" + +func Pic(dx, dy int) [][]uint8 { +} + +func main() { + pic.Show(Pic) +} diff --git a/prog/exercise-web-crawler.go b/prog/exercise-web-crawler.go new file mode 100644 index 0000000..f1ca9ec --- /dev/null +++ b/prog/exercise-web-crawler.go @@ -0,0 +1,83 @@ +package main + +import ( + "fmt" +) + +type Fetcher interface { + // Fetch 返回 URL 的 body 内容,并且将在这个页面上找到的 URL 放到一个 slice 中。 + Fetch(url string) (body string, urls []string, err error) +} + +// Crawl 使用 fetcher 从某个 URL 开始递归的爬取页面,直到达到最大深度。 +func Crawl(url string, depth int, fetcher Fetcher) { + // TODO: 并行的抓取 URL。 + // TODO: 不重复抓取页面。 + // 下面并没有实现上面两种情况: + if depth <= 0 { + return + } + body, urls, err := fetcher.Fetch(url) + if err != nil { + fmt.Println(err) + return + } + fmt.Printf("found: %s %q\n", url, body) + for _, u := range urls { + Crawl(u, depth-1, fetcher) + } + return +} + +func main() { + Crawl("http://golang.org/", 4, fetcher) +} + +// fakeFetcher 是返回若干结果的 Fetcher。 +type fakeFetcher map[string]*fakeResult + +type fakeResult struct { + body string + urls []string +} + +func (f *fakeFetcher) Fetch(url string) (string, []string, error) { + if res, ok := (*f)[url]; ok { + return res.body, res.urls, nil + } + return "", nil, fmt.Errorf("not found: %s", url) +} + +// fetcher 是填充后的 fakeFetcher。 +var fetcher = &fakeFetcher{ + "http://golang.org/": &fakeResult{ + "The Go Programming Language", + []string{ + "http://golang.org/pkg/", + "http://golang.org/cmd/", + }, + }, + "http://golang.org/pkg/": &fakeResult{ + "Packages", + []string{ + "http://golang.org/", + "http://golang.org/cmd/", + "http://golang.org/pkg/fmt/", + "http://golang.org/pkg/os/", + }, + }, + "http://golang.org/pkg/fmt/": &fakeResult{ + "Package fmt", + []string{ + "http://golang.org/", + "http://golang.org/pkg/", + }, + }, + "http://golang.org/pkg/os/": &fakeResult{ + "Package os", + []string{ + "http://golang.org/", + "http://golang.org/pkg/", + }, + }, +} diff --git a/prog/exported-names.go b/prog/exported-names.go new file mode 100644 index 0000000..4d1c7dd --- /dev/null +++ b/prog/exported-names.go @@ -0,0 +1,10 @@ +package main + +import ( + "fmt" + "math" +) + +func main() { + fmt.Println(math.pi) +} diff --git a/prog/for-continued.go b/prog/for-continued.go new file mode 100644 index 0000000..6f1c33b --- /dev/null +++ b/prog/for-continued.go @@ -0,0 +1,11 @@ +package main + +import "fmt" + +func main() { + sum := 1 + for sum < 1000 { + sum += sum + } + fmt.Println(sum) +} diff --git a/prog/for-is-gos-while.go b/prog/for-is-gos-while.go new file mode 100644 index 0000000..6f1c33b --- /dev/null +++ b/prog/for-is-gos-while.go @@ -0,0 +1,11 @@ +package main + +import "fmt" + +func main() { + sum := 1 + for sum < 1000 { + sum += sum + } + fmt.Println(sum) +} diff --git a/prog/for.go b/prog/for.go new file mode 100644 index 0000000..e495cd3 --- /dev/null +++ b/prog/for.go @@ -0,0 +1,11 @@ +package main + +import "fmt" + +func main() { + sum := 0 + for i := 0; i < 10; i++ { + sum += i + } + fmt.Println(sum) +} diff --git a/prog/forever.go b/prog/forever.go new file mode 100644 index 0000000..06c48fd --- /dev/null +++ b/prog/forever.go @@ -0,0 +1,6 @@ +package main + +func main() { + for { + } +} diff --git a/prog/function-closures.go b/prog/function-closures.go new file mode 100644 index 0000000..abd3b1e --- /dev/null +++ b/prog/function-closures.go @@ -0,0 +1,21 @@ +package main + +import "fmt" + +func adder() func(int) int { + sum := 0 + return func(x int) int { + sum += x + return sum + } +} + +func main() { + pos, neg := adder(), adder() + for i := 0; i < 10; i++ { + fmt.Println( + pos(i), + neg(-2*i), + ) + } +} diff --git a/prog/function-values.go b/prog/function-values.go new file mode 100644 index 0000000..65e0c4a --- /dev/null +++ b/prog/function-values.go @@ -0,0 +1,14 @@ +package main + +import ( + "fmt" + "math" +) + +func main() { + hypot := func(x, y float64) float64 { + return math.Sqrt(x*x + y*y) + } + + fmt.Println(hypot(3, 4)) +} diff --git a/prog/functions-continued.go b/prog/functions-continued.go new file mode 100644 index 0000000..5184203 --- /dev/null +++ b/prog/functions-continued.go @@ -0,0 +1,11 @@ +package main + +import "fmt" + +func add(x, y int) int { + return x + y +} + +func main() { + fmt.Println(add(42, 13)) +} diff --git a/prog/functions.go b/prog/functions.go new file mode 100644 index 0000000..ff36d50 --- /dev/null +++ b/prog/functions.go @@ -0,0 +1,11 @@ +package main + +import "fmt" + +func add(x int, y int) int { + return x + y +} + +func main() { + fmt.Println(add(42, 13)) +} diff --git a/prog/goroutines.go b/prog/goroutines.go new file mode 100644 index 0000000..0e7be4c --- /dev/null +++ b/prog/goroutines.go @@ -0,0 +1,18 @@ +package main + +import ( + "fmt" + "time" +) + +func say(s string) { + for i := 0; i < 5; i++ { + time.Sleep(100 * time.Millisecond) + fmt.Println(s) + } +} + +func main() { + go say("world") + say("hello") +} diff --git a/prog/hello.go b/prog/hello.go new file mode 100644 index 0000000..078ddff --- /dev/null +++ b/prog/hello.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("Hello, 世界") +} diff --git a/prog/if-and-else.go b/prog/if-and-else.go new file mode 100644 index 0000000..68dbf74 --- /dev/null +++ b/prog/if-and-else.go @@ -0,0 +1,23 @@ +package main + +import ( + "fmt" + "math" +) + +func pow(x, n, lim float64) float64 { + if v := math.Pow(x, n); v < lim { + return v + } else { + fmt.Printf("%g >= %g\n", v, lim) + } + // 这里开始就不能使用 v 了 + return lim +} + +func main() { + fmt.Println( + pow(3, 2, 10), + pow(3, 3, 20), + ) +} diff --git a/prog/if-with-a-short-statement.go b/prog/if-with-a-short-statement.go new file mode 100644 index 0000000..480a7a8 --- /dev/null +++ b/prog/if-with-a-short-statement.go @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + "math" +) + +func pow(x, n, lim float64) float64 { + if v := math.Pow(x, n); v < lim { + return v + } + return lim +} + +func main() { + fmt.Println( + pow(3, 2, 10), + pow(3, 3, 20), + ) +} diff --git a/prog/if.go b/prog/if.go new file mode 100644 index 0000000..6fcb221 --- /dev/null +++ b/prog/if.go @@ -0,0 +1,17 @@ +package main + +import ( + "fmt" + "math" +) + +func sqrt(x float64) string { + if x < 0 { + return sqrt(-x) + "i" + } + return fmt.Sprint(math.Sqrt(x)) +} + +func main() { + fmt.Println(sqrt(2), sqrt(-4)) +} diff --git a/prog/images.go b/prog/images.go new file mode 100644 index 0000000..42de838 --- /dev/null +++ b/prog/images.go @@ -0,0 +1,12 @@ +package main + +import ( + "fmt" + "image" +) + +func main() { + m := image.NewRGBA(image.Rect(0, 0, 100, 100)) + fmt.Println(m.Bounds()) + fmt.Println(m.At(0, 0).RGBA()) +} diff --git a/prog/imports.go b/prog/imports.go new file mode 100644 index 0000000..81e7144 --- /dev/null +++ b/prog/imports.go @@ -0,0 +1,11 @@ +package main + +import ( + "fmt" + "math" +) + +func main() { + fmt.Printf("Now you have %g problems.", + math.Nextafter(2, 3)) +} diff --git a/prog/interfaces-are-satisfied-implicitly.go b/prog/interfaces-are-satisfied-implicitly.go new file mode 100644 index 0000000..05e8176 --- /dev/null +++ b/prog/interfaces-are-satisfied-implicitly.go @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" + "os" +) + +type Reader interface { + Read(b []byte) (n int, err error) +} + +type Writer interface { + Write(b []byte) (n int, err error) +} + +type ReadWriter interface { + Reader + Writer +} + +func main() { + var w Writer + + // os.Stdout 实现了 Writer + w = os.Stdout + + fmt.Fprintf(w, "hello, writer\n") +} diff --git a/prog/interfaces.go b/prog/interfaces.go new file mode 100644 index 0000000..05bb254 --- /dev/null +++ b/prog/interfaces.go @@ -0,0 +1,39 @@ +package main + +import ( + "fmt" + "math" +) + +type Abser interface { + Abs() float64 +} + +func main() { + var a Abser + f := MyFloat(-math.Sqrt2) + v := Vertex{3, 4} + + a = f // a MyFloat 实现了 Abser + a = &v // a *Vertex 实现了 Abser + a = v // a Vertex, 没有实现 Abser + + fmt.Println(a.Abs()) +} + +type MyFloat float64 + +func (f MyFloat) Abs() float64 { + if f < 0 { + return float64(-f) + } + return float64(f) +} + +type Vertex struct { + X, Y float64 +} + +func (v *Vertex) Abs() float64 { + return math.Sqrt(v.X*v.X + v.Y*v.Y) +} diff --git a/prog/making-slices.go b/prog/making-slices.go new file mode 100644 index 0000000..3c8e20f --- /dev/null +++ b/prog/making-slices.go @@ -0,0 +1,19 @@ +package main + +import "fmt" + +func main() { + a := make([]int, 5) + printSlice("a", a) + b := make([]int, 0, 5) + printSlice("b", b) + c := b[:2] + printSlice("c", c) + d := c[2:5] + printSlice("d", d) +} + +func printSlice(s string, x []int) { + fmt.Printf("%s len=%d cap=%d %v\n", + s, len(x), cap(x), x) +} diff --git a/prog/map-literals-continued.go b/prog/map-literals-continued.go new file mode 100644 index 0000000..f656d3c --- /dev/null +++ b/prog/map-literals-continued.go @@ -0,0 +1,16 @@ +package main + +import "fmt" + +type Vertex struct { + Lat, Long float64 +} + +var m = map[string]Vertex{ + "Bell Labs": {40.68433, -74.39967}, + "Google": {37.42202, -122.08408}, +} + +func main() { + fmt.Println(m) +} diff --git a/prog/map-literals.go b/prog/map-literals.go new file mode 100644 index 0000000..f805d62 --- /dev/null +++ b/prog/map-literals.go @@ -0,0 +1,20 @@ +package main + +import "fmt" + +type Vertex struct { + Lat, Long float64 +} + +var m = map[string]Vertex{ + "Bell Labs": Vertex{ + 40.68433, -74.39967, + }, + "Google": Vertex{ + 37.42202, -122.08408, + }, +} + +func main() { + fmt.Println(m) +} diff --git a/prog/maps.go b/prog/maps.go new file mode 100644 index 0000000..4aa1a0c --- /dev/null +++ b/prog/maps.go @@ -0,0 +1,17 @@ +package main + +import "fmt" + +type Vertex struct { + Lat, Long float64 +} + +var m map[string]Vertex + +func main() { + m = make(map[string]Vertex) + m["Bell Labs"] = Vertex{ + 40.68433, -74.39967, + } + fmt.Println(m["Bell Labs"]) +} diff --git a/prog/methods-continued.go b/prog/methods-continued.go new file mode 100644 index 0000000..397be47 --- /dev/null +++ b/prog/methods-continued.go @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + "math" +) + +type MyFloat float64 + +func (f MyFloat) Abs() float64 { + if f < 0 { + return float64(-f) + } + return float64(f) +} + +func main() { + f := MyFloat(-math.Sqrt2) + fmt.Println(f.Abs()) +} diff --git a/prog/methods-with-pointer-receivers.go b/prog/methods-with-pointer-receivers.go new file mode 100644 index 0000000..f44133d --- /dev/null +++ b/prog/methods-with-pointer-receivers.go @@ -0,0 +1,25 @@ +package main + +import ( + "fmt" + "math" +) + +type Vertex struct { + X, Y float64 +} + +func (v *Vertex) Scale(f float64) { + v.X = v.X * f + v.Y = v.Y * f +} + +func (v *Vertex) Abs() float64 { + return math.Sqrt(v.X*v.X + v.Y*v.Y) +} + +func main() { + v := &Vertex{3, 4} + v.Scale(5) + fmt.Println(v, v.Abs()) +} diff --git a/prog/methods.go b/prog/methods.go new file mode 100644 index 0000000..821d584 --- /dev/null +++ b/prog/methods.go @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "math" +) + +type Vertex struct { + X, Y float64 +} + +func (v *Vertex) Abs() float64 { + return math.Sqrt(v.X*v.X + v.Y*v.Y) +} + +func main() { + v := &Vertex{3, 4} + fmt.Println(v.Abs()) +} diff --git a/prog/multiple-results.go b/prog/multiple-results.go new file mode 100644 index 0000000..eb9e705 --- /dev/null +++ b/prog/multiple-results.go @@ -0,0 +1,12 @@ +package main + +import "fmt" + +func swap(x, y string) (string, string) { + return y, x +} + +func main() { + a, b := swap("hello", "world") + fmt.Println(a, b) +} diff --git a/prog/mutating-maps.go b/prog/mutating-maps.go new file mode 100644 index 0000000..8147ab4 --- /dev/null +++ b/prog/mutating-maps.go @@ -0,0 +1,19 @@ +package main + +import "fmt" + +func main() { + m := make(map[string]int) + + m["Answer"] = 42 + fmt.Println("The value:", m["Answer"]) + + m["Answer"] = 48 + fmt.Println("The value:", m["Answer"]) + + delete(m, "Answer") + fmt.Println("The value:", m["Answer"]) + + v, ok := m["Answer"] + fmt.Println("The value:", v, "Present?", ok) +} diff --git a/prog/named-results.go b/prog/named-results.go new file mode 100644 index 0000000..e1a8207 --- /dev/null +++ b/prog/named-results.go @@ -0,0 +1,13 @@ +package main + +import "fmt" + +func split(sum int) (x, y int) { + x = sum * 4 / 9 + y = sum - x + return +} + +func main() { + fmt.Println(split(17)) +} diff --git a/prog/nil-slices.go b/prog/nil-slices.go new file mode 100644 index 0000000..5fed762 --- /dev/null +++ b/prog/nil-slices.go @@ -0,0 +1,11 @@ +package main + +import "fmt" + +func main() { + var z []int + fmt.Println(z, len(z), cap(z)) + if z == nil { + fmt.Println("nil!") + } +} diff --git a/prog/numeric-constants.go b/prog/numeric-constants.go new file mode 100644 index 0000000..1177ab7 --- /dev/null +++ b/prog/numeric-constants.go @@ -0,0 +1,19 @@ +package main + +import "fmt" + +const ( + Big = 1 << 100 + Small = Big >> 99 +) + +func needInt(x int) int { return x*10 + 1 } +func needFloat(x float64) float64 { + return x * 0.1 +} + +func main() { + fmt.Println(needInt(Small)) + fmt.Println(needFloat(Small)) + fmt.Println(needFloat(Big)) +} diff --git a/prog/packages.go b/prog/packages.go new file mode 100644 index 0000000..4f66c9e --- /dev/null +++ b/prog/packages.go @@ -0,0 +1,10 @@ +package main + +import ( + "fmt" + "math" +) + +func main() { + fmt.Println("Happy", math.Pi, "Day") +} diff --git a/prog/pointers.go b/prog/pointers.go new file mode 100644 index 0000000..2cf149f --- /dev/null +++ b/prog/pointers.go @@ -0,0 +1,15 @@ +package main + +import "fmt" + +type Vertex struct { + X int + Y int +} + +func main() { + p := Vertex{1, 2} + q := &p + q.X = 1e9 + fmt.Println(p) +} diff --git a/prog/range-and-close.go b/prog/range-and-close.go new file mode 100644 index 0000000..c4f8d00 --- /dev/null +++ b/prog/range-and-close.go @@ -0,0 +1,22 @@ +package main + +import ( + "fmt" +) + +func fibonacci(n int, c chan int) { + x, y := 0, 1 + for i := 0; i < n; i++ { + c <- x + x, y = y, x+y + } + close(c) +} + +func main() { + c := make(chan int, 10) + go fibonacci(cap(c), c) + for i := range c { + fmt.Println(i) + } +} diff --git a/prog/range-continued.go b/prog/range-continued.go new file mode 100644 index 0000000..93041cf --- /dev/null +++ b/prog/range-continued.go @@ -0,0 +1,13 @@ +package main + +import "fmt" + +func main() { + pow := make([]int, 10) + for i := range pow { + pow[i] = 1 << uint(i) + } + for _, value := range pow { + fmt.Printf("%d\n", value) + } +} diff --git a/prog/range.go b/prog/range.go new file mode 100644 index 0000000..bb08ee6 --- /dev/null +++ b/prog/range.go @@ -0,0 +1,11 @@ +package main + +import "fmt" + +var pow = []int{1, 2, 4, 8, 16, 32, 64, 128} + +func main() { + for i, v := range pow { + fmt.Printf("2**%d = %d\n", i, v) + } +} diff --git a/prog/select.go b/prog/select.go new file mode 100644 index 0000000..8aab7de --- /dev/null +++ b/prog/select.go @@ -0,0 +1,28 @@ +package main + +import "fmt" + +func fibonacci(c, quit chan int) { + x, y := 0, 1 + for { + select { + case c <- x: + x, y = y, x+y + case <-quit: + fmt.Println("quit") + return + } + } +} + +func main() { + c := make(chan int) + quit := make(chan int) + go func() { + for i := 0; i < 10; i++ { + fmt.Println(<-c) + } + quit <- 0 + }() + fibonacci(c, quit) +} diff --git a/prog/short-variable-declarations.go b/prog/short-variable-declarations.go new file mode 100644 index 0000000..3fc005c --- /dev/null +++ b/prog/short-variable-declarations.go @@ -0,0 +1,10 @@ +package main + +import "fmt" + +func main() { + var x, y, z int = 1, 2, 3 + c, python, java := true, false, "no!" + + fmt.Println(x, y, z, c, python, java) +} diff --git a/prog/slices.go b/prog/slices.go new file mode 100644 index 0000000..9c5359e --- /dev/null +++ b/prog/slices.go @@ -0,0 +1,13 @@ +package main + +import "fmt" + +func main() { + p := []int{2, 3, 5, 7, 11, 13} + fmt.Println("p ==", p) + + for i := 0; i < len(p); i++ { + fmt.Printf("p[%d] == %d\n", + i, p[i]) + } +} diff --git a/prog/slicing-slices.go b/prog/slicing-slices.go new file mode 100644 index 0000000..173a6f5 --- /dev/null +++ b/prog/slicing-slices.go @@ -0,0 +1,15 @@ +package main + +import "fmt" + +func main() { + p := []int{2, 3, 5, 7, 11, 13} + fmt.Println("p ==", p) + fmt.Println("p[1:4] ==", p[1:4]) + + // 省略下标代表从 0 开始 + fmt.Println("p[:3] ==", p[:3]) + + // 省略上标代表到 len(s) 结束 + fmt.Println("p[4:] ==", p[4:]) +} diff --git a/prog/struct-fields.go b/prog/struct-fields.go new file mode 100644 index 0000000..490aff5 --- /dev/null +++ b/prog/struct-fields.go @@ -0,0 +1,14 @@ +package main + +import "fmt" + +type Vertex struct { + X int + Y int +} + +func main() { + v := Vertex{1, 2} + v.X = 4 + fmt.Println(v.X) +} diff --git a/prog/struct-literals.go b/prog/struct-literals.go new file mode 100644 index 0000000..d32ebd3 --- /dev/null +++ b/prog/struct-literals.go @@ -0,0 +1,18 @@ +package main + +import "fmt" + +type Vertex struct { + X, Y int +} + +var ( + p = Vertex{1, 2} // 类型为 Vertex + q = &Vertex{1, 2} // 类型为 *Vertex + r = Vertex{X: 1} // Y:0 被省略 + s = Vertex{} // X:0 和 Y:0 +) + +func main() { + fmt.Println(p, q, r, s) +} diff --git a/prog/structs.go b/prog/structs.go new file mode 100644 index 0000000..b447f7e --- /dev/null +++ b/prog/structs.go @@ -0,0 +1,12 @@ +package main + +import "fmt" + +type Vertex struct { + X int + Y int +} + +func main() { + fmt.Println(Vertex{1, 2}) +} diff --git a/prog/switch-evaluation-order.go b/prog/switch-evaluation-order.go new file mode 100644 index 0000000..814056d --- /dev/null +++ b/prog/switch-evaluation-order.go @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + "time" +) + +func main() { + fmt.Println("When's Saturday?") + today := time.Now().Weekday() + switch time.Saturday { + case today + 0: + fmt.Println("Today.") + case today + 1: + fmt.Println("Tomorrow.") + case today + 2: + fmt.Println("In two days.") + default: + fmt.Println("Too far away.") + } +} diff --git a/prog/switch-with-no-condition.go b/prog/switch-with-no-condition.go new file mode 100644 index 0000000..f3bbcd8 --- /dev/null +++ b/prog/switch-with-no-condition.go @@ -0,0 +1,18 @@ +package main + +import ( + "fmt" + "time" +) + +func main() { + t := time.Now() + switch { + case t.Hour() < 12: + fmt.Println("Good morning!") + case t.Hour() < 17: + fmt.Println("Good afternoon.") + default: + fmt.Println("Good evening.") + } +} diff --git a/prog/switch.go b/prog/switch.go new file mode 100644 index 0000000..c961ba7 --- /dev/null +++ b/prog/switch.go @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + "runtime" +) + +func main() { + fmt.Print("Go runs on ") + switch os := runtime.GOOS; os { + case "darwin": + fmt.Println("OS X.") + case "linux": + fmt.Println("Linux.") + default: + // freebsd, openbsd, + // plan9, windows... + fmt.Printf("%s.", os) + } +} diff --git a/prog/the-new-function.go b/prog/the-new-function.go new file mode 100644 index 0000000..b1a7bac --- /dev/null +++ b/prog/the-new-function.go @@ -0,0 +1,14 @@ +package main + +import "fmt" + +type Vertex struct { + X, Y int +} + +func main() { + v := new(Vertex) + fmt.Println(v) + v.X, v.Y = 11, 9 + fmt.Println(v) +} diff --git a/prog/variables-with-initializers.go b/prog/variables-with-initializers.go new file mode 100644 index 0000000..e332d23 --- /dev/null +++ b/prog/variables-with-initializers.go @@ -0,0 +1,10 @@ +package main + +import "fmt" + +var x, y, z int = 1, 2, 3 +var c, python, java = true, false, "no!" + +func main() { + fmt.Println(x, y, z, c, python, java) +} diff --git a/prog/variables.go b/prog/variables.go new file mode 100644 index 0000000..36fa989 --- /dev/null +++ b/prog/variables.go @@ -0,0 +1,10 @@ +package main + +import "fmt" + +var x, y, z int +var c, python, java bool + +func main() { + fmt.Println(x, y, z, c, python, java) +} diff --git a/prog/web-servers.go b/prog/web-servers.go new file mode 100644 index 0000000..0a3ec17 --- /dev/null +++ b/prog/web-servers.go @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "net/http" +) + +type Hello struct{} + +func (h Hello) ServeHTTP( + w http.ResponseWriter, + r *http.Request) { + fmt.Fprint(w, "Hello!") +} + +func main() { + var h Hello + http.ListenAndServe("localhost:4000", h) +} diff --git a/static/index.html b/static/index.html deleted file mode 100644 index c6357cb..0000000 --- a/static/index.html +++ /dev/null @@ -1,2095 +0,0 @@ - - - - - -Go 指南 - - - - - - - - - - - - - - - - - -
    - - -
    - - -
    -

    Hello, 世界

    -

    - 欢迎来到Go 编程语言指南. -

    - 该指南被分为三节。在每节的末尾有若干个练习需要读者完成。 -

    - 该指南可以进行交互。点击“Run”按钮(或按 Shift + Enter)可以在远程服务器上你的电脑上编译并执行程序。 - 结果展示在代码的下面。 -

    - 这些例子程序展示了 Go 的各个方面。在指南中的程序可以成为你积累经验的开始。 -

    - 编辑程序并且再次执行它。 -

    - 当你准备好继续了,点击“Next”按钮或按 PageDown 键。 -

    package main
    -
    -import "fmt"
    -
    -func main() {
    -	fmt.Println("Hello, 世界")
    -}
    -
    - -
    -

    Go 本地化

    -

    - 该指南也有其他语言版本: -

    -

    - (如果你愿意翻译该指南到其他语言,从 - https://code.google.com/p/go-tour - 检出代码,翻译 static/index.html, - 并根据 appengine/README 的介绍将其部署到 - App Engine 上。) -

    - 点击“Next”按钮或者按 PageDown 继续。 -

    - - -
    -

    离线 Go 指南

    -

    - 在没有互联网接入的情况下该指南可以作为独立的程序使用。 -

    - 指南独立运行会在本地的设备上构建和编译代码,这运行得更快。并且会包括一些在沙盒版本中没有的体验上的增强。 -

    - 为了在本地运行指南首先要安装 Go, - 然后使用 - go get 安装 - gotour: -

        go get code.google.com/p/go-tour/gotour
    -

    - 也可以安装中文版本: -

        go get bitbucket.org/mikespook/go-tour-zh/gotour
    -

    - 最后执行安装产生的 gotour 执行文件。 -

    - 如果不安装本地版本,点击“Next”按钮或者按 PageDown 继续。 -

    - (可以在任何时候点击“目录”按钮返回这个介绍。) -

    - -
    简介
    - -
    -

    -

    - 每个 Go 程序都是由包组成的。 -

    - 程序运行的入口是包 main。 -

    - 这个程序使用并导入了包 - "fmt""math"。 -

    - 按照惯例,包名与导入路径的最后一个目录一致。 -

    package main
    -
    -import (
    -	"fmt"
    -	"math"
    -)
    -
    -func main() {
    -	fmt.Println("Happy", math.Pi, "Day")
    -}
    -
    - -
    -

    导入

    -

    - 这个代码用圆括号组合了导入,这是“factored”导入语句。同样可以编写多个导入语句,例如: -

    import "fmt"
    -import "math"
    - 不过通常都会用 factored 格式来使代码工整。 -
    package main
    -
    -import (
    -	"fmt"
    -	"math"
    -)
    -
    -func main() {
    -	fmt.Printf("Now you have %g problems.",
    -		math.Nextafter(2, 3))
    -}
    -
    - -
    -

    导出名

    -

    - 在导入了一个包之后,就可以用其导出的名称来调用它。 -

    - 在 Go 中,首字母大写的名称是被导出的。 -

    - FooFOO 都是被导出的名称。 - 名称 foo 是不会被导出的。 -

    - 执行代码。然后将 math.pi 改名为 math.Pi 再试着执行一下。 -

    package main
    -
    -import (
    -	"fmt"
    -	"math"
    -)
    -
    -func main() {
    -	fmt.Println(math.pi)
    -}
    -
    - -
    -

    函数

    -

    - 函数可以没有参数或接受多个参数。 -

    - 在这个例子中,add 接受两个 int 类型的参数。 -

    - 注意类型在变量名之后。 -

    - (参考这篇关于 Go 语法定义的文章了解类型以这种形式出现的原因。) -

    package main
    -
    -import "fmt"
    -
    -func add(x int, y int) int {
    -	return x + y
    -}
    -
    -func main() {
    -	fmt.Println(add(42, 13))
    -}
    -
    - -
    -

    函数(续)

    -

    - 当两个或多个连续的函数命名参数是同一类型,则除了最后一个类型之外,其他都可以省略。 -

    - 在这个例子中 , -

    x int, y int
    -

    - 被缩写为 -

    -

    x, y int
    -
    package main
    -
    -import "fmt"
    -
    -func add(x, y int) int {
    -	return x + y
    -}
    -
    -func main() {
    -	fmt.Println(add(42, 13))
    -}
    -
    - -
    -

    多值返回

    -

    - 函数可以返回任意数量的返回值。 -

    - 这个函数返回了两个字符串。 -

    package main
    -
    -import "fmt"
    -
    -func swap(x, y string) (string, string) {
    -	return y, x
    -}
    -
    -func main() {
    -	a, b := swap("hello", "world")
    -	fmt.Println(a, b)
    -}
    -
    - -
    -

    命名返回值

    -

    - 函数接受参数。在 Go 中,函数可以返回多个“结果参数”,而不仅仅是一个值。它们可以像变量那样命名和使用。 -

    - 如果命名了返回值参数,一个没有参数的 return 语句,会将当前的值作为返回值返回。 -

    package main
    -
    -import "fmt"
    -
    -func split(sum int) (x, y int) {
    -	x = sum * 4/9
    -	y = sum - x
    -	return
    -}
    -
    -func main() {
    -	fmt.Println(split(17))
    -}
    -
    - -
    -

    变量

    -

    - var 语句定义了一个变量的列表;跟函数的参数列表一样,类型在后面。 - -

    package main
    -
    -import "fmt"
    -
    -var x, y, z int
    -var c, python, java bool
    -
    -func main() {
    -	fmt.Println(x, y, z, c, python, java)
    -}
    -
    - -
    -

    初始化变量

    -

    - 变量定义可以包含初始值,每个变量对应一个。 -

    - 如果初始化是使用表达式,则可以省略类型;变量从初始值中获得类型。 -

    package main
    -
    -import "fmt"
    -
    -var x, y, z int = 1, 2, 3
    -var c, python, java = true, false, "no!"
    -
    -func main() {
    -	fmt.Println(x, y, z, c, python, java)
    -}
    -
    - -
    -

    短声明变量

    -

    - 在函数中,:= 简洁赋值语句在明确类型的地方,可以用于替代 var 定义。 -

    - (:= 结构不能使用在函数外,函数外的每个语法块都必须以关键字开始。) -

    package main
    -
    -import "fmt"
    -
    -func main() {
    -	var x, y, z int = 1, 2, 3
    -	c, python, java := true, false, "no!"
    -
    -	fmt.Println(x, y, z, c, python, java)
    -}
    -
    - -
    -

    常量

    -

    - 常量的定义与变量类似,只不过使用 const 关键字。 -

    - 常量可以是字符、字符串、布尔或数字类型的值。 -

    package main
    -
    -import "fmt"
    -
    -const Pi = 3.14
    -
    -func main() {
    -	const World = "世界"
    -	fmt.Println("Hello", World)
    -	fmt.Println("Happy", Pi, "Day")
    -
    -	const Truth = true
    -	fmt.Println("Go rules?", Truth)
    -}
    -
    - -
    -

    数值常量

    -

    - 数值常量是高精度的。 -

    - 一个未指定类型的常量由上下文来决定其类型。 -

    - 也尝试一下输出 needInt(Big)吧。 -

    package main
    -
    -import "fmt"
    -
    -const (
    -	Big = 1<<100
    -	Small = Big>>99
    -)
    -
    -func needInt(x int) int { return x*10 + 1 }
    -func needFloat(x float64) float64 {
    -	return x*0.1
    -}
    -
    -func main() {
    -	fmt.Println(needInt(Small))
    -	fmt.Println(needFloat(Small))
    -	fmt.Println(needFloat(Big))
    -}
    -
    - -
    -

    For

    -

    - Go 只有一种循环结构,for 循环。 -

    - 基本的 for 循环看起来跟 C 或者 Java 中做的一样,除了没有了 ( ) 之外(甚至强制不能使用它们),而 { } 是必须的。 -

    package main
    -
    -import "fmt"
    -
    -func main() {
    -	sum := 0
    -	for i := 0; i < 10; i++ {
    -		sum += i
    -	}
    -	fmt.Println(sum)
    -}
    -
    - -
    -

    For(续)

    -

    - 跟 C 或者 Java 中一样,可以让前置、后置语句为空。 -

    package main
    -
    -import "fmt"
    -
    -func main() {
    -	sum := 1
    -	for ; sum < 1000; {
    -		sum += sum
    -	}
    -	fmt.Println(sum)
    -}
    -
    - -
    -

    For 是 Go 的 “while”

    -

    - 基于此可以省略分号: - C 的 while 在 Go 中也是用 for 实现。 -

    package main
    -
    -import "fmt"
    -
    -func main() {
    -	sum := 1
    -	for sum < 1000 {
    -		sum += sum
    -	}
    -	fmt.Println(sum)
    -}
    -
    - -
    -

    死循环

    -

    - 如果省略了循环条件,循环就不会结束, -因此一个死循环可以有更简洁地表达。 -

    package main
    -
    -func main() {
    -	for {
    -	}
    -}
    -
    - -
    -

    If

    -

    - if 语句看起来跟 C 或者 Java 中的一样,除了没有了 ( ) 之外(甚至强制不能使用它们),而 { } 是必须的。 -

    - (耳熟吗?) -

    package main
    -
    -import (
    -	"fmt"
    -	"math"
    -)
    -
    -func sqrt(x float64) string {
    -	if x < 0 {
    -		return sqrt(-x) + "i"
    -	}
    -	return fmt.Sprint(math.Sqrt(x))
    -}
    -
    -func main() {
    -	fmt.Println(sqrt(2), sqrt(-4))
    -}
    -
    - -
    -

    If 的简洁语句

    -

    - 跟 for 一样,if 语句可以在条件之前执行一个简单的语句。 -

    - 由这个语句定义的变量的作用域仅在 if 范围之内。 -

    - (在最后的 return 语句处使用 v 看看。) -

    package main
    -
    -import (
    -	"fmt"
    -	"math"
    -)
    -
    -func pow(x, n, lim float64) float64 {
    -	if v := math.Pow(x, n); v < lim {
    -		return v
    -	}
    -	return lim
    -}
    -
    -func main() {
    -	fmt.Println(
    -		pow(3, 2, 10),
    -		pow(3, 3, 20),
    -	)
    -}
    -
    - -
    -

    If 和 else

    -

    - 在 if 的简单语句处定义的变量同样可以在任何对应的 else 块中使用。 -

    package main
    -
    -import (
    -	"fmt"
    -	"math"
    -)
    -
    -func pow(x, n, lim float64) float64 {
    -	if v := math.Pow(x, n); v < lim {
    -		return v
    -	} else {
    -		fmt.Printf("%g >= %g\n", v, lim)
    -	}
    -   // 不能在这里使用 v,因此
    -	return lim
    -}
    -
    -func main() {
    -	fmt.Println(
    -		pow(3, 2, 10),
    -		pow(3, 3, 20),
    -	)
    -}
    -
    - -
    -

    基本类型

    -

    - Go 的基本类型有 -

    bool
    -
    -string
    -
    -int  int8  int16  int32  int64
    -uint uint8 uint16 uint32 uint64 uintptr
    -
    -byte // uint8 的别名
    -
    -rune // int32 的别名
    -     // 代表一个Unicode码点
    -
    -float32 float64
    -
    -complex64 complex128
    -
    package main
    -
    -import (
    -	"math/cmplx"
    -	"fmt"
    -)
    -
    -var (
    -	ToBe bool = false
    -	MaxInt uint64 = 1<<64 - 1
    -	z complex128 = cmplx.Sqrt(-5+12i)
    -)
    -
    -func main() {
    -	const f = "%T(%v)\n"
    -	fmt.Printf(f, ToBe, ToBe)
    -	fmt.Printf(f, MaxInt, MaxInt)
    -	fmt.Printf(f, z, z)
    -}
    -
    - -
    -

    结构体

    -

    - 一个结构体(struct)就是一个字段的集合。 -

    - (而 type 定义跟其字面意思相符。) -

    package main
    -
    -import "fmt"
    -
    -type Vertex struct {
    -	X int
    -	Y int
    -}
    -
    -func main() {
    -	fmt.Println(Vertex{1, 2})
    -}
    -
    - -
    -

    结构体字段

    -

    - 结构体字段使用点号来访问。 -

    package main
    -
    -import "fmt"
    -
    -type Vertex struct {
    -	X int
    -	Y int
    -}
    -
    -func main() {
    -	v := Vertex{1, 2}
    -	v.X = 4
    -	fmt.Println(v.X)
    -}
    -
    - -
    -

    指针

    -

    - Go 有指针,但是没有指针运算。 -

    - 结构体字段可以通过结构体指针来访问。通过指针间接的访问是透明的。 -

    package main
    -
    -import "fmt"
    -
    -type Vertex struct {
    -	X int
    -	Y int
    -}
    -
    -func main() {
    -	p := Vertex{1, 2}
    -	q := &p
    -	q.X = 1e9
    -	fmt.Println(p)
    -}
    -
    - -
    -

    结构体文法

    -

    - 结构体文法表示通过结构体字段的值作为列表来新分配一个结构体。 -

    - 使用 Name: 语法可以仅列出部分字段。(字段名的顺序无关。) -

    - 特殊的前缀 & 构造了指向结构体文法的指针。 -

    package main
    -
    -import "fmt"
    -
    -type Vertex struct {
    -	X, Y int
    -}
    -
    -var (
    -	p = Vertex{1, 2}  // has type Vertex
    -	q = &Vertex{1, 2} // has type *Vertex
    -	r = Vertex{X: 1}  // Y:0 is implicit
    -	s = Vertex{}      // X:0 and Y:0
    -)
    -
    -func main() {
    -	fmt.Println(p, q, r, s)
    -}
    -
    - -
    -

    new 函数

    -

    - 表达式 new(T) 分配了一个零初始化的 T 值,并返回指向它的指针。 -

    var t *T = new(T)
    -

    - 或 -

    t := new(T)
    -
    package main
    -
    -import "fmt"
    -
    -type Vertex struct {
    -	X, Y int
    -}
    -
    -func main() {
    -	v := new(Vertex)
    -	fmt.Println(v)
    -	v.X, v.Y = 11, 9
    -	fmt.Println(v)
    -}
    -
    - -
    -

    map

    -

    - map 映射键到值。 -

    - - map 在使用之前必须用 make 来创建(不是 new);一个值为 nil 的 map 是空的,并且不能赋值。 -

    package main
    -
    -import "fmt"
    -
    -type Vertex struct {
    -	Lat, Long float64
    -}
    -
    -var m map[string]Vertex
    -
    -func main() {
    -	m = make(map[string]Vertex)
    -	m["Bell Labs"] = Vertex{
    -		40.68433, -74.39967,
    -	}
    -	fmt.Println(m["Bell Labs"])
    -}
    -
    - -
    -

    map 的文法

    -

    - map 的文法跟结构体文法相似,不过键名是必须的。 -

    package main
    -
    -import "fmt"
    -
    -type Vertex struct {
    -	Lat, Long float64
    -}
    -
    -var m = map[string]Vertex{
    -	"Bell Labs": Vertex{
    -		40.68433, -74.39967,
    -	},
    -	"Google": Vertex{
    -		37.42202, -122.08408,
    -	},
    -}
    -
    -func main() {
    -	fmt.Println(m)
    -}
    -
    - -
    -

    map 的文法(续)

    -

    - 如果顶层类型只有类型名的话,可以在文法的元素中省略键名。 -

    package main
    -
    -import "fmt"
    -
    -type Vertex struct {
    -	Lat, Long float64
    -}
    -
    -var m = map[string]Vertex{
    -	"Bell Labs": {40.68433, -74.39967},
    -	"Google":    {37.42202, -122.08408},
    -}
    -
    -func main() {
    -	fmt.Println(m)
    -}
    -
    - -
    -

    修改 map

    -

    - 在 map m 中插入或修改一个元素: -

    m[key] = elem
    -

    - 获得元素: -

    elem = m[key]
    -

    - 删除元素: -

    delete(m, key)
    -

    - 通过双赋值检测某个键存在: -

    elem, ok = m[key]
    -

    - 如果 keym 中, - oktrue。 - 否则,okfalse 并且 - elem 是 map 的元素类型的零值。 -

    - 同样的,当从 map 中读取某个不存在的键时,结果是 - map 的元素类型的零值。 -

    package main
    -
    -import "fmt"
    -
    -func main() {
    -	m := make(map[string]int)
    -
    -	m["Answer"] = 42
    -	fmt.Println("The value:", m["Answer"])
    -
    -	m["Answer"] = 48
    -	fmt.Println("The value:", m["Answer"])
    -
    -	delete(m, "Answer")
    -	fmt.Println("The value:", m["Answer"])
    -
    -	v, ok := m["Answer"]
    -	fmt.Println("The value:", v, "Present?", ok)
    -}
    -
    - -
    -

    slice

    -

    - slice 指向数组的值,并且同时包含了长度信息。 -

    - []T 是一个元素类型为 T 的 slice。 -

    package main
    -
    -import "fmt"
    -
    -func main() {
    -	p := []int{2, 3, 5, 7, 11, 13}
    -	fmt.Println("p ==", p)
    -
    -	for i := 0; i < len(p); i++ {
    -		fmt.Printf("p[%d] == %d\n",
    -			i, p[i])
    -	}
    -}
    -
    - -
    -

    对 slice 切片

    -

    - slice 可以重新切片,创建一个新的 slice 值指向相同的数组。 -

    - 表达式 -

    s[lo:hi]
    -

    - 表示从 lohi-1 的 slice 元素,含有两端。 - 因此

    s[lo:lo]
    -

    - 是空的,而 -

    s[lo:lo+1]
    -

    - 有一个元素。 -

    package main
    -
    -import "fmt"
    -
    -func main() {
    -	p := []int{2, 3, 5, 7, 11, 13}
    -	fmt.Println("p ==", p)
    -	fmt.Println("p[1:4] ==", p[1:4])
    -
    -	// missing low index implies 0
    -	fmt.Println("p[:3] ==", p[:3])
    -
    -	// missing high index implies len(s)
    -	fmt.Println("p[4:] ==", p[4:])
    -}
    -
    - -
    -

    构造 slice

    -

    - slice 由函数 make 创建。这会分配一个零长度的数组并且返回一个 slice 指向这个数组: -

    a := make([]int, 5)  // len(a)=5
    - slice 有长度和容量。slice 的容量是底层数组可以增长的最大长度。 -

    - 为了指定容量,可传递第三个参数到 make: -

    -

    b := make([]int, 0, 5) // len(b)=0, cap(b)=5
    - slice 可以通过“重新切片”来扩容(增长到容量上限): -

    -

    b = b[:cap(b)] // len(b)=5, cap(b)=5
    -b = b[1:]      // len(b)=4, cap(b)=4
    -
    package main
    -
    -import "fmt"
    -
    -func main() {
    -	a := make([]int, 5)
    -	printSlice("a", a)
    -	b := make([]int, 0, 5)
    -	printSlice("b", b)
    -	c := b[:2]
    -	printSlice("c", c)
    -	d := c[2:5]
    -	printSlice("d", d)
    -}
    -
    -func printSlice(s string, x []int) {
    -	fmt.Printf("%s len=%d cap=%d %v\n",
    -		s, len(x), cap(x), x)
    -}
    -
    - -
    -

    空 slice

    -

    - slice 的零值是 nil。 -

    - 一个 nil 的 slice 的长度和容量是 0。 -

    - (了解更多关于 slice 的内容,参阅文章 - “slice:使用和内幕”。) -

    package main
    -
    -import "fmt"
    -
    -func main() {
    -	var z []int
    -	fmt.Println(z, len(z), cap(z))
    -	if z == nil {
    -		fmt.Println("nil!")
    -	}
    -}
    -
    - -
    -

    函数为值

    -

    - 函数也是值。 -

    package main
    -
    -import (
    -	"fmt"
    -	"math"
    -)
    -
    -func main() {
    -	hypot := func(x, y float64) float64 {
    -		return math.Sqrt(x*x + y*y)
    -	}
    -
    -	fmt.Println(hypot(3, 4))
    -}
    -
    - -
    -

    函数的闭包

    -

    - 并且函数是完全闭包的。 -

    - 函数 adder 返回一个闭包。每个闭包被绑定到了特定的 sum 变量上。 -

    package main
    -
    -import "fmt"
    -
    -func adder() func(int) int {
    -	sum := 0
    -	return func(x int) int {
    -		sum += x
    -		return sum
    -	}
    -}
    -
    -func main() {
    -	pos, neg := adder(), adder()
    -	for i := 0; i < 10; i++ {
    -		fmt.Println(
    -			pos(i),
    -			neg(-2*i),
    -		)
    -	}
    -}
    -
    - -
    -

    range

    -

    - for 循环的 range 格式可以对 slice 或者 map 进行迭代循环。 -

    package main
    -
    -import "fmt"
    -
    -var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
    -
    -func main() {
    -	for i, v := range pow {
    -	    fmt.Printf("2**%d = %d\n", i, v)
    -	}
    -}
    -
    - -
    -

    range(续)

    -

    - 可以将值赋值给 _ 来忽略序号和值。 -

    - 如果只需要索引值,去掉“, value”的部分即可。 -

    package main
    -
    -import "fmt"
    -
    -func main() {
    -	pow := make([]int, 10)
    -	for i := range pow {
    -		pow[i] = 1<<uint(i)
    -	}
    -	for _, value := range pow {
    -		fmt.Printf("%d\n", value)
    -	}
    -}
    -
    - -
    -

    switch

    -

    - 你可能已经猜到 switch 可能的形式了。 -

    - case 体会自动终止,除非用 fallthrough 语句作为结尾。 - -

    package main
    -
    -import (
    -	"fmt"
    -	"runtime"
    -)
    -
    -func main() {
    -	fmt.Print("Go runs on ")
    -	switch os := runtime.GOOS; os {
    -	case "darwin":
    -		fmt.Println("OS X.")
    -	case "linux":
    -		fmt.Println("Linux.")
    -	default:
    -		// freebsd, openbsd,
    -		// plan9, windows...
    -		fmt.Printf("%s.", os)
    -	}
    -}
    -
    - -
    -

    switch 的执行顺序

    -

    - switch 的条件从上到下的执行,当匹配成功的时候停止。 -

    - (例如, -

    switch i {
    -case 0:
    -case f():
    -}
    -

    - 当 i==0 时不会调用 f。) - -

    -注意:Go playground 中的时间总是从 2009-11-10 23:00:00 UTC 开始, -a value whose significance is left as an exercise for the reader. - -

    package main
    -
    -import (
    -	"fmt"
    -	"time"
    -)
    -
    -func main() {
    -	fmt.Println("When's Saturday?")
    -	today := time.Now().Weekday()
    -	switch time.Saturday {
    -	case today+0:
    -		fmt.Println("Today.")
    -	case today+1:
    -		fmt.Println("Tomorrow.")
    -	case today+2:
    -		fmt.Println("In two days.")
    -	default:
    -		fmt.Println("Too far away.")
    -	}
    -}
    -
    - -
    -

    没有条件的 switch

    -

    - 没有条件的 switch 同 switch true 一样。 -

    - 这一构造使得可以用更清晰的形式来编写长的 if-then-else 链。 -

    package main
    -
    -import (
    -	"fmt"
    -	"time"
    -)
    -
    -func main() {
    -	t := time.Now()
    -	switch {
    -	case t.Hour() < 12:
    -	    fmt.Println("Good morning!")
    -	case t.Hour() < 17:
    -	    fmt.Println("Good afternoon.")
    -	default:
    -	    fmt.Println("Good evening.")
    -	}
    -}
    -
    - -
    -

    练习:循环和函数

    -

    - 作为练习函数和循环的简单途径,用牛顿法实现开方函数。 -

    - 在这个例子中,牛顿法是通过选择一个初始点 z 然后重复这一过程求 Sqrt(x) 的近似值: -

    - 牛顿法 -
    -

    - 为了做到这个,只需要重复计算 10 次,并且观察不同的值(1,2,3,……)是如何逐步逼近结果的。 -

    - 然后,修改循环条件,使得当值停止改变(或改变非常小)的时候退出循环。观察迭代次数是否变化。结果与 math.Sqrt 接近吗? -

    - 提示:定义并初始化一个浮点值,向其提供一个浮点语法或使用转换: -

    z := float64(1)
    -z := 1.0
    - -
    package main
    -
    -import (
    -	"fmt"
    -)
    -
    -func Sqrt(x float64) float64 {
    -}
    -
    -func main() {
    -	fmt.Println(Sqrt(2))
    -}
    -
    - -
    -

    练习:map

    -

    - 实现 WordCount。它应当返回一个含有 s 中每个 “word” 数量的 map。函数 wc.Test 针对这个函数执行一个测试用例,并打印成功或者失败。 -

    - 你会发现 strings.Fields 很有帮助。 - -

    package main
    -
    -import (
    -	"tourbitbucket.org/mikespook/go-tour-zh/wc"
    -)
    -
    -func WordCount(s string) map[string]int {
    -	return map[string]int{"x": 1}
    -}
    -
    -func main() {
    -	wc.Test(WordCount)
    -}
    -
    - -
    -

    练习:slice

    -

    - 实现 Pic。它应当接受一个 slice - 的长度 dy,和 slice 中每个元素的长度的 8 位无符号整数 - dx。当执行这个程序,它会将整数转换为灰度(好吧,蓝度)图片进行展示。 -

    - 图片的实现已经完成。可能用到的函数包括 x^y(x+y)/2x*y。 -

    - (需要使用循环来分配 [][]uint8 中的每个 []uint8。) -

    - (使用 uint8(intValue) 在类型之间转换。) - -

    package main
    -
    -import "tourbitbucket.org/mikespook/go-tour-zh/pic"
    -
    -func Pic(dx, dy int) [][]uint8 {
    -}
    -
    -func main() {
    -	pic.Show(Pic)
    -}
    -
    - -
    -

    练习:斐波纳契闭包

    -

    - 现在来通过函数找些乐趣。 -

    - 实现一个 fibonacci 函数,返回一个函数(一个闭包)可以返回连续的斐波纳契数。 - -

    package main
    -
    -import "fmt"
    -
    -// fibonacci 函数会返回一个返回 int 的函数。
    -func fibonacci() func() int {
    -}
    -
    -func main() {
    -	f := fibonacci()
    -	for i := 0; i < 10; i++ {
    -		fmt.Println(f())
    -	}
    -}
    -
    - -
    -

    进阶练习:复数立方根

    -

    - 让我们通过 complex64complex128 来探索一下 Go 内建的复数。 - 对于立方根,牛顿法需要大量循环: -

    - 牛顿法 -
    -

    - 找到 2 的立方根,确保算法能够工作。在 math/cmplx -包中有 -Pow 函数。 - -

    package main
    -
    -import "fmt"
    -
    -func Cbrt(x complex128) complex128 {
    -}
    -
    -func main() {
    -	fmt.Println(Cbrt(2))
    -}
    -
    - - -
    方法和接口
    - -
    -

    方法和接口

    -
    - -
    -

    方法

    -

    - Go 没有类。然而,仍然可以在结构体类型上定义方法。 -

    - 方法接收者出现在 func 关键字和方法名之间的参数中。 -

    package main
    -
    -import (
    -	"fmt"
    -	"math"
    -)
    -
    -type Vertex struct {
    -	X, Y float64
    -}
    -
    -func (v *Vertex) Abs() float64 {
    -	return math.Sqrt(v.X*v.X + v.Y*v.Y)
    -}
    -
    -func main() {
    -	v := &Vertex{3, 4}
    -	fmt.Println(v.Abs())
    -}
    -
    - -
    -

    方法(续)

    -

    - 事实上,可以对包中的任意类型定义任意方法,而不仅仅是结构体。 -

    - 不能对来自其他包的类型或基础类型定义方法。 -

    package main
    -
    -import (
    -	"fmt"
    -	"math"
    -)
    -
    -type MyFloat float64
    -
    -func (f MyFloat) Abs() float64 {
    -	if f < 0 {
    -		return float64(-f)
    -	}
    -	return float64(f)
    -}
    -
    -func main() {
    -	f := MyFloat(-math.Sqrt2)
    -	fmt.Println(f.Abs())
    -}
    -
    - -
    -

    接收者为指针的方法

    -

    - 方法可以与命名类型或命名类型的指针关联。 -

    - 刚刚看到的两个 Abs 方法。一个是在 *Vertex 指针类型上,而另一个在 MyFloat 值类型上。 -

    - 有两个原因需要使用指针接收者。首先避免在每个方法调用中拷贝值(如果值类型是大的结构体的话会更有效率)。其次,方法可以修改接收者指向的值。 -

    - 尝试修改 Abs 的定义,同时 Scale 方法使用 Vertex 代替 *Vertex 作为接收者。 -

    - 当 vVertex 的时候 Scale 方法没有任何作用。Scale 修改 v。当 v 是一个值(非指针),方法看到的是 Vertex 的副本,并且无法修改原始值。 -

    - Abs 的工作方式是一样的。只不过,仅仅读取 v。所以读取的是原始值(通过指针)还是那个值的副本并没有关系。 -

    package main
    -
    -import (
    -	"fmt"
    -	"math"
    -)
    -
    -type Vertex struct {
    -	X, Y float64
    -}
    -
    -func (v *Vertex) Scale(f float64) {
    -	v.X = v.X * f
    -	v.Y = v.Y * f
    -}
    -
    -func (v *Vertex) Abs() float64 {
    -	return math.Sqrt(v.X*v.X + v.Y*v.Y)
    -}
    -
    -func main() {
    -	v := &Vertex{3, 4}
    -	v.Scale(5)
    -	fmt.Println(v, v.Abs())
    -}
    -
    - -
    -

    接口

    -

    - 接口类型是由一组方法定义的集合。 -

    - 接口类型的值可以存放实现这些方法的任何值。 - -

    package main
    -
    -import (
    -	"fmt"
    -	"math"
    -)
    -
    -type Abser interface {
    -	Abs() float64
    -}
    -
    -func main() {
    -	var a Abser
    -	f := MyFloat(-math.Sqrt2)
    -	v := Vertex{3, 4}
    -
    -	a = f  // a MyFloat implements Abser
    -	a = &v // a *Vertex implements Abser
    -	a = v  // a Vertex, does NOT
    -	       // implement Abser
    -
    -	fmt.Println(a.Abs())
    -}
    -
    -type MyFloat float64
    -
    -func (f MyFloat) Abs() float64 {
    -	if f < 0 {
    -		return float64(-f)
    -	}
    -	return float64(f)
    -}
    -
    -type Vertex struct {
    -	X, Y float64
    -}
    -
    -func (v *Vertex) Abs() float64 {
    -	return math.Sqrt(v.X*v.X + v.Y*v.Y)
    -}
    -
    - -
    -

    隐式接口

    -

    - 类型通过实现那些方法来实现接口。 -

    - 没有显式声明的必要。 -

    - 隐式接口解藕了实现接口的包和定义接口的包:互不依赖。 -

    - 因此,也就无需在每一个实现上增加新的接口名称,这样同时也鼓励了明确的接口定义。(对比其他语言) -

    - 包 io 定义了 ReaderWriter;不一定要这么做。 -

    package main
    -
    -import (
    -	"fmt"
    -	"os"
    -)
    -
    -type Reader interface {
    -	Read(b []byte) (n int, err error)
    -}
    -
    -type Writer interface {
    -	Write(b []byte) (n int, err error)
    -}
    -
    -type ReadWriter interface {
    -	Reader
    -	Writer
    -}
    -
    -func main() {
    -	var w Writer
    -
    -	// os.Stdout implements Writer
    -	w = os.Stdout
    -
    -	fmt.Fprintf(w, "hello, writer\n")
    -}
    -
    - -
    -

    错误

    -

    - 错误是可以用字符串描述自己的任何东西。 - 主要思路是由预定义的内建接口类型 - error,和其返回返回字符串窜的方法 Error 构成。 -

    type error interface {
    -	Error() string
    -}
    - -

    - 当用 fmt 包的多种不同的打印函数输出一个 error - 时,会自动的调用该方法。 - -

    package main
    -
    -import (
    -	"fmt"
    -	"time"
    -)
    -
    -type MyError struct {
    -	When time.Time
    -	What string
    -}
    -
    -func (e *MyError) Error() string {
    -	return fmt.Sprintf("at %v, %s",
    -		e.When, e.What)
    -}
    -
    -func run() error {
    -	return &MyError{
    -		time.Now(),
    -		"it didn't work",
    -	}
    -}
    -
    -func main() {
    -	if err := run(); err != nil {
    -		fmt.Println(err)
    -	}
    -}
    -
    - -
    -

    Web 服务器

    -

    - 包 http - 通过任何实现了 http.Handler 的值来响应 HTTP 请求: -

    package http
    -
    -type Handler interface {
    -	ServeHTTP(w ResponseWriter, r *Request)
    -}
    -

    - 在这个例子中,类型 Hello 实现了 http.Handler。 -

    - - 访问 http://localhost:4000/ 会看到来自程序的问候。 - - - 注意: 这个例子无法在基于 web 的指南用户界面运行。为了尝试编写 - web 服务器,可能需要安装 Go。 - -

    package main
    -
    -import (
    -	"fmt"
    -	"net/http"
    -)
    -
    -type Hello struct{}
    -
    -func (h Hello) ServeHTTP(
    -		w http.ResponseWriter,
    -		r *http.Request) {
    -	fmt.Fprint(w, "Hello!")
    -}
    -
    -func main() {
    -	var h Hello
    -	http.ListenAndServe("localhost:4000",h)
    -}
    -
    - -
    -

    图片

    -

    - Package image 定义了 Image 接口: -

    package image
    -
    -type Image interface {
    -	ColorModel() color.Model
    -	Bounds() Rectangle
    -	At(x, y int) color.Color
    -}
    -

    - (参阅文档了解全部信息。) -

    - 同样,color.Colorcolor.Model 也是接口,但是通常因为直接使用预定义的实现 image.RGBAColorimage.RGBAColorModel 而忽略它们。 - -

    package main
    -
    -import (
    -	"fmt"
    -	"image"
    -)
    -
    -func main() {
    -	m := image.NewRGBA(image.Rect(0, 0, 100, 100))
    -	fmt.Println(m.Bounds())
    -	fmt.Println(m.At(0, 0).RGBA())
    -}
    -
    - -
    -

    练习:错误

    -

    - 从之前的练习中复制 Sqrt 函数,并修改使其返回 error 值。 -

    - Sqrt 接收到一个负数时,应当返回一个非 nil 的错误值。复数同样也不被支持。 -

    - 创建一个新类型 -

    type ErrNegativeSqrt float64
    -

    - 使其成为一个 error,通过 -

    func (e ErrNegativeSqrt) Error() string
    -

    - 方法就可以让 ErrNegativeSqrt(-2).Error() 返回 - "cannot Sqrt negative number: -2"。 -

    - 注意:Error 方法内调用 fmt.Print(e) 将会让程序陷入死循环。 - 可以通过先转换 e 来避免这个: - fmt.Print(float64(e))。为什么? -

    - 修改 Sqrt 函数,使其接受一个负数时,返回 ErrNegativeSqrt 值。 -

    package main
    -
    -import (
    -	"fmt"
    -)
    -
    -func Sqrt(f float64) (float64, error) {
    -	return 0, nil
    -}
    -
    -func main() {
    -	fmt.Println(Sqrt(2))
    -	fmt.Println(Sqrt(-2))
    -}
    -
    - -
    -

    练习:HTTP 处理

    -

    - 实现下面的类型,并在其上定义 ServeHTTP 方法。 - 在 web 服务器中注册它们来处理指定的路径。 -

    type String string
    -	
    -type Struct struct {
    -	Greeting string
    -	Punct    string
    -	Who      string
    -}
    -

    - 例如,可以使用如下方式注册处理方法: -

    http.Handle("/string", String("I'm a frayed knot."))
    -http.Handle("/struct", &Struct{"Hello", ":", "Gophers!"})
    -
    package main
    -
    -import (
    -	"net/http"
    -)
    -
    -func main() {
    -	// your http.Handle calls here
    -	http.ListenAndServe("localhost:4000", nil)
    -}
    -
    - -
    -

    练习:图片

    -

    - 还记得之前编写的图片生成器吗? - 现在来编写另一个,不过这次将会返回一个 image.Image 的实现来代替 slice 的数据。 -

    - 自定义的 Image 类型,要实现必要的方法,并且调用 pic.ShowImage。 -

    - Bounds 应当返回一个 image.Rectangle,例如 image.Rect(0, 0, w, h)。 -

    - ColorModel 应当返回 image.RGBAColorModel。 -

    - At 应当返回一个颜色;在这个例子里,在最后一个图片生成器的值 v 匹配 color.RGBAColor{v, v, 255, 255}。 - -

    package main
    -
    -import (
    -	"image"
    -	"tourbitbucket.org/mikespook/go-tour-zh/pic"
    -)
    -
    -type Image struct{}
    -
    -func main() {
    -	m := Image{}
    -	pic.ShowImage(m)
    -}
    -
    - -
    -

    练习:Rot13 读取器

    -

    - 一般的模式是 io.Reader 包裹另一个 io.Reader,用某些途径修改特定的流。 -

    - 例如, gzip.NewReader - 函数输入一个 io.Reader(gzip 的数据流)并且返回一个同样实现了 io.Reader 的 - *gzip.Reader(解压缩后的数据流)。 -

    - 实现一个实现了 io.Readerrot13Reader,用 - ROT13 - 修改数据流中的所有的字母进行密文替换。 -

    - rot13Reader 已经提供。通过实现其 Read 方法使得它匹配 io.Reader。 -

    package main
    -
    -import (
    -	"io"
    -	"os"
    -	"strings"
    -)
    -
    -type rot13Reader struct {
    -	r io.Reader
    -}
    -
    -func main() {
    -	s := strings.NewReader(
    -		"Lbh penpxrq gur pbqr!")
    -	r := rot13Reader{s}
    -	io.Copy(os.Stdout, &r)
    -}
    -
    - -
    并发
    - -
    -

    并发

    -
    - -
    -

    goroutine

    -

    - goroutine 是由 Go 运行时环境管理的轻量级线程。 -

    go f(x, y, z)
    -

    - 开启一个新的 goroutine 执行 -

    f(x, y, z)
    -

    - fxyz 是当前 - goroutine 中定义的,但是在新的 goroutine 中运行 f。 -

    - goroutine 在相同的地址空间中运行,因此访问共享内存必须进行同步。 - sync 提供了这种可能,不过在 Go 中并不经常用到,因为有其他的办法。 (在接下来的内容中会涉及到。) -

    package main
    -
    -import (
    -	"fmt"
    -	"runtimetime"
    -)
    -
    -func say(s string) {
    -	for i := 0; i < 5; i++ {
    -		runtime.Gosched()time.Sleep(100 * time.Millisecond)
    -		fmt.Println(s)
    -	}
    -}
    -
    -func main() {
    -	go say("world")
    -	say("hello")
    -}
    -
    - -
    -

    channel

    - -

    - channel 是有类型的管道,可以用 channel 操作符 <- 对其发送或者接收值。 -

    ch <- v    // 将 v 送入 channel ch。
    -v := <-ch  // 从 ch 接收,并且赋值给 v。
    -

    - (“箭头”就是数据流的方向。) - -

    - 和 map 与 slice 一样,channel 使用前必须创建: -

    ch := make(chan int)
    - -

    - 默认情况下,在另一端准备好之前,发送和接收都会阻塞。这使得 - goroutine 可以在没有明确的锁或竞态变量的情况下进行同步。 -

    package main
    -
    -import "fmt"
    -
    -func sum(a []int, c chan int) {
    -	sum := 0
    -	for _, v := range a {
    -		sum += v
    -	}
    -	c <- sum  // send sum to c
    -}
    -
    -func main() {
    -	a := []int{7, 2, 8, -9, 4, 0}
    -
    -        c := make(chan int)
    -	go sum(a[:len(a)/2], c)
    -	go sum(a[len(a)/2:], c)
    -	x, y := <-c, <-c  // 从 c 中接收
    -
    -	fmt.Println(x, y, x + y)
    -}
    -
    - - -
    -

    缓冲 channel

    - -

    - channel 可以是带缓冲的。为 make 提供第二个参数作为缓冲长度来初始化一个缓冲 channel: -

    ch := make(chan int, 100)
    - -

    - 向缓冲 channel 发送数据的时候,只有在缓冲区满的时候才会阻塞。当缓冲区清空的时候接受阻塞。 - -

    - 修改例子使得缓冲区被填满,然后看看会发生什么。 - -

    package main
    -
    -import "fmt"
    -
    -func main() {
    -	c := make(chan int, 2)
    -	c <- 1
    -	c <- 2
    -	fmt.Println(<-c)
    -	fmt.Println(<-c)
    -}
    -
    - -
    -

    range 和 close

    -

    - 发送者可以 close 一个 channel 来表示再没有值会被发送了。接收者可以通过赋值语句的第二参数来测试 - channel 是否被关闭:当没有值可以接收并且 channel 已经被关闭,那么 -

    v, ok := <-ch
    -

    - ok 会被设置为 false。 -

    - 循环 for i := range c 会不断从 channel 接收值,直到它被关闭。 -

    - 注意: 只有发送者才能关闭 channel,而不是接收者。向一个已经关闭的 channel 发送数据会引起 panic。 -

    - 还要注意:channel 与文件不同;通常情况下无需关闭它们。只有在需要告诉接收者没有更多的数据的时候才有必要进行关闭,例如中断一个 range。 -

    package main
    -
    -import (
    -	"fmt"
    -)
    -
    -func fibonacci(n int, c chan int) {
    -        x, y := 0, 1
    -	for i := 0; i < n; i++ {
    -		c <- x
    -                x, y = y, x + y
    -        }
    -        close(c)
    -}
    -
    -func main() {
    -        c := make(chan int, 10)
    -	go fibonacci(cap(c), c)
    -        for i := range c {
    -                fmt.Println(i)
    -        }
    -}
    -
    - -
    -

    select

    -

    - select 语句使得一个 goroutine 在多个通讯操作上等待。 -

    - select 会阻塞,直到条件分支中的某个可以继续执行,这时就会执行那个条件分支。当多个都准备好的时候,会随机选择一个。 -

    package main
    -
    -import "fmt"
    -
    -func fibonacci(c, quit chan int) {
    -        x, y := 0, 1
    -        for {
    -                select {
    -		case c <- x:
    -                          x, y = y, x + y
    -		case <-quit:
    -			fmt.Println("quit")
    -                        return
    -                }
    -        }
    -}
    -
    -func main() {
    -        c := make(chan int)
    -	quit := make(chan int)
    -	go func() {
    -		for i := 0; i < 10; i++ {
    -			fmt.Println(<-c)
    -		}
    -		quit <- 0
    -	}()
    -	fibonacci(c, quit)
    -}
    -
    - -
    -

    默认选择

    -

    - 当 select 中的其他条件分支都没有准备好的时候,default 分支会被执行。 -

    - 为了非阻塞的发送或者接收,可使用 default 分支: -

    select {
    -case i := <-c:
    -	// use i
    -default:
    -	// receiving from c would block
    -}
    -

    - - 注意: 这个例子无法在指南的基于 web - 的用户界面中运行,因为沙盒环境是没有定时器概念的。需要安装、 - Go 来了解这个例子如何运行。 - -

    package main
    -
    -import (
    -	"fmt"
    -	"time"
    -)
    -
    -func main() {
    -        tick := time.Tick(1e8)
    -        boom := time.After(5e8)
    -        for {
    -                select {
    -		case <-tick:
    -                        fmt.Println("tick.")
    -		case <-boom:
    -                        fmt.Println("BOOM!")
    -                        return
    -                default:
    -                        fmt.Println("    .")
    -                        time.Sleep(5e7)
    -                }
    -        }
    -	}
    -
    - -
    -

    练习:等价二叉树

    -

    - 可以用多种不同的二叉树的叶子节点存储相同的数列值。例如,这里有两个二叉树保存了序列 1,1,2,3,5,8,13。 - 二叉树 -

    - 用于检查两个二叉树是否存储了相同的序列的函数在多数语言中都是相当复杂的。这里将使用 Go 的并发和 - channel 来编写一个简单的解法。 -

    - 这个例子使用了 tree 包,定义了类型: -

    type Tree struct {
    -	Left  *Tree
    -	Value int
    -	Right *Tree
    -}
    -
    - -
    -

    练习:等价二叉树

    -

    - 1. 实现 Walk 函数。 -

    - 2. 测试 Walk 函数。 -

    - 函数 tree.New(k) 构造了一个随机结构的二叉树,保存了值 - k2k3k,...,10k。 -

    - 创建一个新的 channel ch 并且对其进行步进: -

    go Walk(tree.New(1), ch)
    -

    - 然后从 channel 中读取并且打印 10 个值。应当是值 1,2,3,...,10。 -

    - 3.Walk 实现 Same 函数来检测是否 t1t2 存储了相同的值。 -

    - 4. 测试 Same 函数。 -

    - Same(tree.New(1), tree.New(1)) 应当返回 true,而 - Same(tree.New(1), tree.New(2)) 应当返回 false。 - -

    package main
    -
    -import "tourbitbucket.org/mikespook/go-tour-zh/tree"
    -
    -// Walk 步进 tree t 将所有的值从 tree 发送到 channel ch。
    -func Walk(t *tree.Tree, ch chan int)
    -
    -// Same 检测树 t1 和 t2 是否含有相同的值。
    -func Same(t1, t2 *tree.Tree) bool
    -
    -func main() {
    -}
    -
    - -
    -

    练习:Web 爬虫

    -

    - 在这个练习中,将会使用 Go 的并发特性来并行执行 web 爬虫。 -

    - 修改 Crawl 函数来并行的抓取 URLs,并且保证不重复。 -

    package main
    -
    -import (
    -	"fmt"
    -)
    -
    -type Fetcher interface {
    -        // Fetch 返回 URL 的 body 内容,并且将在这个页面上找到的 URL 放到一个 slice 中。
    -	Fetch(url string) (body string, urls []string, err error)
    -}
    -
    -// Crawl 使用 fetcher 从某个 URL 开始递归的爬取页面,直到达到最大深度。
    -func Crawl(url string, depth int, fetcher Fetcher) {
    -        // TODO: 并行的抓取 URL。
    -        // TODO: 不重复抓取页面。
    -        // 下面并没有实现上面两种情况:
    -	if depth <= 0 {
    -		return
    -	}
    -	body, urls, err := fetcher.Fetch(url)
    -	if err != nil {
    -		fmt.Println(err)
    -		return
    -	}
    -	fmt.Printf("found: %s %q\n", url, body)
    -	for _, u := range urls {
    -		Crawl(u, depth-1, fetcher)
    -	}
    -	return
    -}
    -
    -func main() {
    -	Crawl("http://golang.org/", 4, fetcher)
    -}
    -
    -
    -// fakeFetcher 是返回若干结果的 Fetcher。
    -type fakeFetcher map[string]*fakeResult
    -
    -type fakeResult struct {
    -	body string
    -	urls     []string
    -}
    -
    -func (f *fakeFetcher) Fetch(url string) (string, []string, error) {
    -	if res, ok := (*f)[url]; ok {
    -		return res.body, res.urls, nil
    -	}
    -	return "", nil, fmt.Errorf("not found: %s", url)
    -}
    -
    -// fetcher 是填充后的 fakeFetcher。
    -var fetcher = &fakeFetcher{
    -	"http://golang.org/": &fakeResult{
    -		"The Go Programming Language",
    -		[]string{
    -			"http://golang.org/pkg/",
    -			"http://golang.org/cmd/",
    -		},
    -	},
    -	"http://golang.org/pkg/": &fakeResult{
    -		"Packages",
    -		[]string{
    -			"http://golang.org/",
    -			"http://golang.org/cmd/",
    -			"http://golang.org/pkg/fmt/",
    -			"http://golang.org/pkg/os/",
    -		},
    -	},
    -	"http://golang.org/pkg/fmt/": &fakeResult{
    -		"Package fmt",
    -		[]string{
    -			"http://golang.org/",
    -			"http://golang.org/pkg/",
    -		},
    -	},
    -	"http://golang.org/pkg/os/": &fakeResult{
    -		"Package os",
    -		[]string{
    -			"http://golang.org/",
    -			"http://golang.org/pkg/",
    -		},
    -	},
    -}
    -
    - -
    -

    Where to Go from here...

    -

    - 可以从 installing Go 或者下载 - Go App Engine SDK。 -

    -

    - 一旦在机器上安装好了 Go, - - Go Documentation - - 是应当继续阅读的内容 - 作为开始。 - 它包含了参考、指南、视频等等更多资料。 -

    -

    - 了解如何组织 Go 代码并在其上工作,参阅 this screencast or read 如何编写 Go 代码。 -

    -

    - 在标准库上需要帮助的话,参考 - package - reference。语言本身的帮助,可以阅读令人愉快的 - Language - Spec。 -

    -

    - 进一步探索 Go 的并发模型,参阅 - Share Memory by Communicating - 的代码之旅。 -

    - First Class Functions in Go - 为 Go 的函数类型提供了有趣的展示。 -

    - Go Blog 有着众多的关于 Go 的文章信息。 -

    - 访问 golang.org 了解更多内容。 -

    - -
    - - -
    - - - diff --git a/static/mode.js b/static/mode.js deleted file mode 100644 index 01cd2ea..0000000 --- a/static/mode.js +++ /dev/null @@ -1 +0,0 @@ -var tourMode = "local"; diff --git a/static/newton.png b/static/newton.png new file mode 100644 index 0000000..540ec83 Binary files /dev/null and b/static/newton.png differ diff --git a/static/newton3.png b/static/newton3.png new file mode 100644 index 0000000..526a1e5 Binary files /dev/null and b/static/newton3.png differ diff --git a/static/tour.css b/static/tour.css index 598bc11..2901037 100644 --- a/static/tour.css +++ b/static/tour.css @@ -159,23 +159,23 @@ ol li { border-bottom-right-radius: 5px; overflow: auto; } - #output pre { margin: 5px; padding: 0; background: none; font-size: 16px; } - -#output div.loading { - margin: 5px; - padding: 0; - font-style: italic; +#output .loading { color: #999; } -#output pre.error { - margin: 5px; - color: #B00; +#output .stdout, #output pre { + color: black; +} +#output .stderr, #output .error { + color: #a11; +} +#output .system, #output .exit { + color: #375EAB; } #output img { @@ -189,7 +189,7 @@ pre, code { font-size: 14px; } -pre.source { +div.source { display: none; } diff --git a/static/fig4.png b/static/tree.png similarity index 100% rename from static/fig4.png rename to static/tree.png diff --git a/talks/2012-04-25-NewYorkMeetup/index.html b/talks/2012-04-25-NewYorkMeetup/index.html deleted file mode 100644 index eed0bc8..0000000 --- a/talks/2012-04-25-NewYorkMeetup/index.html +++ /dev/null @@ -1,1027 +0,0 @@ - - - -A Tour of Go - - - - - - - - - - - - - - - - - - - - - - - - -

    - -

    A Tour of Go

    - - -
    - Loading slides... -
    - - -
      - -
      - -
      Welcome
      - -
      -

      Let's Go

      -
      -

      Sameer Ajmani -

      Google NYC Tech Talk Meetup -

      April 25, 2012 -

      -
      - -
      -

      Go offline

      -

      - This talk contains several examples that will only work if you run the - gotour stand-alone program on your local machine. You can flip through - the following slides to read the talk and code, but most of the examples - will fail mysteriously if you're - using tour.golang.org. -

      - To run the talk locally first - install Go, - then use - go get to install - gotour: -

          go get code.google.com/p/go-tour/gotour
      -

      - and run the resultant gotour executable. Finally, navigate - to - http://127.0.0.1:3999/talks/2012-04-25-NewYorkMeetup - to access this talk locally. -

      - Otherwise, click the "next" button or type PageDown to continue. -

      - (You may return to these instructions at any time by clicking the - "index" button.) -

      - -
      -

      Why Go?

      - -

      Statically typed languages are efficient, but typically bureaucratic and - overly complex. - -

      Dynamic languages can be easy to use, but are error-prone, inefficient, and - break down at scale. - -

      Concurrent programming is hard (threads, locks, headache). - -

      "Speed, reliability, simplicity: pick two." (sometimes just one) - -

      Can't we do better? -

      - -
      -

      Go 1

      - -

      Go began in September 2007. -

      "Consensus drove the design. Nothing went into the language - until [Ken Thompson, Robert Griesemer, and myself] all agreed - that it was right. Some features didn't get resolved until after - a year or more of discussion." -- Rob Pike
      -

      The first release of Go was in November 2009. -

      Go version 1 (Go 1) was released March 28, 2012. -

      - -
      -

      What is Go?

      - -

      Go is ... -

        -
      • a general-purpose, open source programming language -
      • concise, expressive, and readable -
      • statically-typed, with "duck typing" via implicit interfaces -
      • garbage-collected -
      • compiled to native machine code: compiles fast, runs fast -
      • especially good for concurrent programming -
      -

      Start at http://golang.org -

      - -
      -

      Hello, 世界

      -

      Here's "Hello, world" in Go. -

      I'm running a modified version of http://tour.golang.org. -

      -package main - -import "fmt" - -func main() { - fmt.Println("Hello, 世界") -} -
      -
      - -
      -

      Hello, world 2.0

      -

      Serving "Hello, world" - at http://localhost:8080/world - -

      import "net/http" imports the named package as http;
      - http.HandleFunc references HandleFunc from that package. - -

      In handler, what's the type of name? -

      -package main - -import ( - "fmt" - "net/http" -) - -func main() { - http.HandleFunc("/", handler) - http.ListenAndServe(":8080", nil) -} - -func handler(w http.ResponseWriter, - r *http.Request) { - name := r.URL.Path[1:] - fmt.Fprint(w, "Hello, "+ name) -} -
      -
      - -
      -

      Simple type system

      -

      Go is statically typed, but type inference saves repitition. - -

      Java:
      - Date now = new Date(); - -

      C/C++:
      - time_t now = time(NULL); - -

      Go:
      - now := time.Now()
      - i := 1
      - pi := math.Pi
      - greeting := "Hello!"
      - -

      -package main - -import "fmt" - -var w int - -var ( - x = 1 - y, z = 2, 3 -) - -func main() { - var c = true - python, java := false, "no!" - fmt.Println(w, x, y, z, c, python, java) -} -
      -
      - -
      -

      Types and Functions

      -

      Declare a type with type, and declare a function - with func. -

      -package main - -import "fmt" - -type Vertex struct { - X, Y float64 -} - -func VertexToString(v Vertex) string { - return fmt.Sprintf("(%f, %f)", v.X, v.Y) -} - -func main() { - var v Vertex - v.X, v.Y = 3, 4 - fmt.Println(VertexToString(v)) -} -
      -
      - -
      -

      Composite Literals

      -

      - Go provides excellent composite literals. These makes it easy to - declare values for structs, maps, arrays, and slices, including nested - values. -

      -package main - -import "fmt" - -type Vertex struct { - X, Y float64 -} - -func VertexToString(v Vertex) string { - return fmt.Sprintf("(%f, %f)", v.X, v.Y) -} - -func main() { - v := Vertex{3, 4} - fmt.Println(VertexToString(v)) -} -
      -
      - -
      -

      Methods

      -

      - Go does not have classes. However, you can define methods on struct - types. -

      - The method receiver appears as an argument between - the func keyword and the method name. -

      -package main - -import "fmt" - -type Vertex struct { - X, Y float64 -} - -func (v Vertex) String() string { - return fmt.Sprintf("(%f, %f)", v.X, v.Y) -} - -func main() { - v := Vertex{3, 4} - fmt.Println(v.String()) -} -
      -
      - -
      -

      Methods

      -

      - In fact, you can define a method on any type you define in your - package, not just structs. -

      - You cannot define a method on a type from another package, or on a - basic type. -

      -package main - -import "fmt" - -type Celsius float32 -type Fahrenheit float32 - -func (c Celsius) String() string { - return fmt.Sprintf("%.2f°C", c) -} - -func (f Fahrenheit) String() string { - return fmt.Sprintf("%.2f°F", f) -} - -func (c Celsius) Fahrenheit() Fahrenheit { - return Fahrenheit(c*9/5 + 32) // conversion -} - -func main() { - c := Celsius(100) - fmt.Println(c, "==", c.Fahrenheit()) - - - // Celsius, Fahrenheit, and float32 - // are different types! - // tryMe := c + Fahrenheit(32) -} -
      -
      - -
      -

      Interfaces

      -

      - Did you notice I forgot to type c.String() in - the fmt calls on the previous slide? The fmt - package automatically calls String on anything that - implements fmt.Stringer. -

      - But I didn't have to say "implements Stringer" anywhere. -

      - There is no explicit declaration of intent. -

      - An interface type is defined by a set of methods. -

      - A value of interface type can hold any value that - implements those methods. -

      - This decouples implementation packages from the packages - that define the interfaces: neither depends on the other. -

      -package main - -import "fmt" - -type Stringer interface { - String() string -} - -func main() { - var s Stringer - v := Vertex{3, 4} - c := Celsius(100) - - s = v // Vertex implements Stringer - s = c // Celsius implements Stringer - fmt.Println(v, c, s) -} - -type Vertex struct { X, Y float64 } - -func (v Vertex) String() string { - return fmt.Sprintf("(%f, %f)", v.X, v.Y) -} - -type Celsius float32 - -func (c Celsius) String() string { - return fmt.Sprintf("%.2f°C", c) -} - -
      -
      - -
      -

      Hello, world 2.1

      -

      Serving "Hello, world" - at http://localhost:8081/world -

      - Package http serves HTTP requests using any value - that implements http.Handler: -

      -package http
      -
      -type Handler interface {
      -	ServeHTTP(w ResponseWriter,
      -	          r *Request)
      -}
      -	
      -

      And fmt.Fprint can write to any value that implements io.Writer: -

      -package io
      -
      -type Writer interface {
      -	Write(b []byte) (n int, err error)
      -}
      -	
      - In this example, the type Hello - implements http.Handler, and the - type http.ResponseWriter - implements io.Writer. -

      - Hello can contain state, but beware: calls to ServeHTTP may - run concurrently. -

      -package main - -import ( - "fmt" - "net/http" -) - -type Hello struct { - greeting string -} - -func (h Hello) ServeHTTP(w http.ResponseWriter, - r *http.Request) { - name := r.URL.Path[1:] - fmt.Fprint(w, h.greeting, ", ", name) -} - -func main() { - h := Hello{"Namaste"} - http.ListenAndServe("localhost:8081", h) -} -
      -
      - -
      -

      Concurrency

      - -

      Based on Communicating Sequential Processes (CSP), first described in 1978 by - C.A.R. Hoare. - -

      In UNIX we think about processes connected by pipes: -

      find ~/go/src/pkg | grep _test.go$ | xargs wc -l -

      Each tool is designed to do one thing and to do it well. - -

      In the real world, we assign tasks to individuals connected by communication:
      -factory assembly lines, phone trees, software projects. - -

      The Go analogue: goroutines connected by channels. -

      - -
      -

      Goroutines

      -

      - Goroutines are like threads: -

        -
      • They share memory.
      • -
      - But cheaper: -
        -
      • Smaller, segmented stacks.
      • -
      • Many goroutines per operating system thread.
      • -
      • Managed by the Go runtime.
      • -
      -
      - -
      -

      Goroutines

      -

      - Start a new goroutine with the go keyword: -

      go f(x, y, z)
      -

      - starts a new goroutine running -

      f(x, y, z)
      -

      - The evaluation - of f, x, y, and z - happens in the current goroutine and the execution of f - happens in the new goroutine. -

      - Goroutines run in the same address space, so access to shared memory - must be synchronized. The sync package provides useful primitives, - but you don't need them so much in Go thanks to channels. -

      -package main - -import ( - "fmt" - "math/rand" - "time" -) - -func say(s string) { - for i := 0; i < 5; i++ { - time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) - fmt.Println(s) - } -} - -func main() { - rand.Seed(time.Now().UnixNano()) - go say(" world") - say("hello") -} -
      -
      - -
      -

      Channels

      - -

      - A channel is a typed conduit through which you can send and receive - values with the channel operator, <-. -

      -ch <- v    // Send v to channel ch.
      -v := <-ch  // Receive from ch, and
      -           // assign value to v.
      -
      -

      - (The data flows in the direction of the arrow.) - -

      - Channels must be created using make: -

      -ch := make(chan int)
      -
      -

      - By default, sends and receives block until the other side is ready. - This allows goroutines to synchronize without explicit locks or - condition variables. -

      -package main - -import "fmt" - -func sum(a []int, c chan int) { - sum := 0 - for i := 0; i < len(a); i++ { - sum += a[i] - } - c <- sum // send sum to c -} - -func main() { - a := []int{7, 2, 8, -9, 4, 0} - - c := make(chan int) - go sum(a[:len(a)/2], c) - go sum(a[len(a)/2:], c) - x, y := <-c, <-c // receive from c - - fmt.Println(x, y, x + y) -} -
      -
      - - -
      -

      Buffered Channels

      - -

      - Channels can be buffered. Provide the buffer length as the - second argument to make to initialize a buffered channel: -

      -ch := make(chan int, 100)
      -
      - -

      - Sends to a buffered channel block only when the buffer is full. - Receives block when the buffer is empty. -

      -package main - -import "fmt" - -func main() { - c := make(chan int, 2) - c <- 1 - c <- 2 - fmt.Println(<-c) - fmt.Println(<-c) -} -
      -
      - -
      -

      Range and Close

      -

      - A sender can close a channel to indicate that no more - values will be sent. Receivers can test whether a channel has been - closed by assigning a second parameter to the receive expression: after -

      -v, ok := <-ch
      -

      - ok is false if there are no more values to - receive and the channel is closed. -

      - The loop for i := range c receives values from the - channel repeatedly until it is closed. -

      - -

      -package main - -import "fmt" - -func fibonacci(n int, c chan int) { - x, y := 1, 1 - for i := 0; i < n; i++ { - c <- x - x, y = y, x + y - } - close(c) -} - -func main() { - c := make(chan int, 10) - go fibonacci(cap(c), c) - for i := range c { - fmt.Println(i) - } -} -
      -
      - -
      -

      Select

      -

      - The select statement lets a goroutine wait on multiple - communication operations. -

      - A select blocks until one of its cases can run, then it - executes that case. It chooses one at random if multiple are ready. -

      - A select can have a default case that runs if - the others would block. This lets you write non-blocking channel ops: -

      -select {
      -case c1 <- x:
      -  // sent x
      -case y := <-c2:
      -  // received y
      -default:
      -  // nothing ready
      -}
      -
      -
      -package main - -import "fmt" - -func fibonacci(c, quit chan int) { - x, y := 1, 1 - for { - select { - case c <- x: - x, y = y, x + y - case <-quit: - fmt.Println("quit") - return - } - } -} - -func main() { - c := make(chan int) - quit := make(chan int) - go fibonacci(c, quit) - for i := 0; i < 10; i++ { - fmt.Println(<-c) - } - quit <- 0 -} -
      -
      - -
      -

      Example: Google Search

      -

      Q: What does Google search do? - -

      A: Given a query, return a page of search results (and some ads). - -

      Q: How do we get the search results? - -

      A: Send the query to Web search, Image search, YouTube, Maps, News, - etc., then mix the results. - -

      How do we implement this? -

      - - -
      -

      Example: Google Search 1.0

      - -

      The Google function takes a query and returns a slice - of Results. - -

      Google - invokes Web, Image, - and Video searches serially, appending them to - the results slice. -

      -package main - -import ( - "fmt" - "math/rand" - "time" -) - -type Result string -type Search func(query string) Result - -var ( - Web = fakeSearch("web") - Image = fakeSearch("image") - Video = fakeSearch("video") -) - -func Google(query string) (results []Result) { - results = append(results, Web(query)) - results = append(results, Image(query)) - results = append(results, Video(query)) - return -} - -func fakeSearch(kind string) Search { - return func(query string) Result { - time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) - return Result(fmt.Sprintf("%s result for %q\n", kind, query)) - } -} - -func main() { - rand.Seed(time.Now().UnixNano()) - start := time.Now() - results := Google("golang") - elapsed := time.Since(start) - fmt.Println(results) - fmt.Println(elapsed) -} -
      -
      - -
      -

      Example: Google Search 2.1

      - -

      Google invokes Web, Image, - and Video searches concurrently, and waits for the expected - number of results. - -

      No locks. No condition variables. No callbacks. -

      -package main - -import ( - "fmt" - "math/rand" - "time" -) - -type Result string -type Search func(query string) Result - -var ( - Web = fakeSearch("web") - Image = fakeSearch("image") - Video = fakeSearch("video") -) - -func Google(query string) (results []Result) { - c := make(chan Result, 3) - go func() { c <- Web(query) } () - go func() { c <- Image(query) } () - go func() { c <- Video(query) } () - - for i := 0; i < cap(c); i++ { - result := <-c - results = append(results, result) - } - return -} - -func fakeSearch(kind string) Search { - return func(query string) Result { - time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) - return Result(fmt.Sprintf("%s result for %q\n", kind, query)) - } -} - -func main() { - rand.Seed(time.Now().UnixNano()) - start := time.Now() - results := Google("golang") - elapsed := time.Since(start) - fmt.Println(results) - fmt.Println(elapsed) -} -
      -
      - -
      -

      Example: Google Search 2.2

      - -

      Don't wait for slow backends. - -

      No locks. No condition variables. No callbacks. -

      -package main - -import ( - "fmt" - "math/rand" - "time" -) - -type Result string -type Search func(query string) Result - -var ( - Web = fakeSearch("web") - Image = fakeSearch("image") - Video = fakeSearch("video") -) - -func Google(query string) (results []Result) { - c := make(chan Result, 3) - go func() { c <- Web(query) } () - go func() { c <- Image(query) } () - go func() { c <- Video(query) } () - - timeout := time.After(80 * time.Millisecond) - for i := 0; i < cap(c); i++ { - select { - case result := <-c: - results = append(results, result) - case <-timeout: - fmt.Println("timed out") - return - } - } - return -} - -func fakeSearch(kind string) Search { - return func(query string) Result { - time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) - return Result(fmt.Sprintf("%s result for %q\n", kind, query)) - } -} - -func main() { - rand.Seed(time.Now().UnixNano()) - start := time.Now() - results := Google("golang") - elapsed := time.Since(start) - fmt.Println(results) - fmt.Println(elapsed) -} -
      -
      - - -
      -

      Example: Query failover

      -

      Q: How do we avoid timing out our requests to slow backends? - -

      A: Replicate the backends. Send requests to multiple replicas, and - use the first response. -

      - -
      -

      Example: Query failover

      - -

      First runs searches on the replicas - concurrently and returns the first result. -

      -package main - -import ( - "fmt" - "math/rand" - "time" -) - -type Result string -type Search func(query string) Result - -func First(query string, replicas ...Search) Result { - c := make(chan Result, len(replicas)) - searchReplica := func(i int) { - c <- replicas[i](query) - } - for i := range replicas { - go searchReplica(i) - } - return <-c -} - -func main() { - rand.Seed(time.Now().UnixNano()) - start := time.Now() - result := First("query", - fakeSearch("replica 1"), - fakeSearch("replica 2")) - elapsed := time.Since(start) - fmt.Println(result) - fmt.Println(elapsed) -} - -func fakeSearch(kind string) Search { - return func(query string) Result { - time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) - return Result(fmt.Sprintf("%s result for %q\n", kind, query)) - } -} -
      -
      - -
      -

      Example: Google Search 3.0

      - -

      Reduces tail latency using replicated search backends. - -

      -package main - -import ( - "fmt" - "math/rand" - "time" -) - -type Result string -type Search func(query string) Result - -var ( - Web1 = fakeSearch("web1") - Web2 = fakeSearch("web2") - Image1 = fakeSearch("image1") - Image2 = fakeSearch("image2") - Video1 = fakeSearch("video1") - Video2 = fakeSearch("video2") -) - -func Google(query string) (results []Result) { - c := make(chan Result, 3) - go func() { c <- First(query, Web1, Web2) } () - go func() { c <- First(query, Image1, Image2) } () - go func() { c <- First(query, Video1, Video2) } () - - timeout := time.After(80 * time.Millisecond) - for i := 0; i < cap(c); i++ { - select { - case result := <-c: - results = append(results, result) - case <-timeout: - fmt.Println("timed out") - return - } - } - return -} - -func First(query string, replicas ...Search) Result { - c := make(chan Result, len(replicas)) - searchReplica := func(i int) { - c <- replicas[i](query) - } - for i := range replicas { - go searchReplica(i) - } - return <-c -} - -func fakeSearch(kind string) Search { - return func(query string) Result { - time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) - return Result(fmt.Sprintf("%s result for %q\n", kind, query)) - } -} - -func main() { - rand.Seed(time.Now().UnixNano()) - start := time.Now() - results := Google("golang") - elapsed := time.Since(start) - fmt.Println(results) - fmt.Println(elapsed) -} - -
      -
      - -
      -

      There's a lot more to Go

      -
        -
      • arrays, maps, slices
      • -
      • struct and interface embedding
      • -
      • defer, panic, recover
      • -
      • type switches and reflection
      • -
      • the go tool
      • -
      -
      - - -
      -

      Where to Go from here...

      - -
      - -
      - - - diff --git a/template/action.tmpl b/template/action.tmpl new file mode 100644 index 0000000..1bd47de --- /dev/null +++ b/template/action.tmpl @@ -0,0 +1,42 @@ +{{/*{ +This is the action template. +It determines how the formatting actions are rendered. +*/}} + +{{define "section"}} +

      {{.Title}}

      + {{range .Elem}}{{elem $.Template .}}{{end}} +{{end}} + +{{define "list"}} +
        + {{range .Bullet}} +
      • {{style .}}
      • + {{end}} +
      +{{end}} + +{{define "text"}} + {{if .Pre}} +
      {{range .Lines}}{{.}}{{end}}
      + {{else}} +

      + {{range $i, $l := .Lines}}{{if $i}}{{template "newline"}} + {{end}}{{style $l}}{{end}} +

      + {{end}} +{{end}} + +{{define "code"}} + {{.Text}} +{{end}} + +{{define "image"}} + +{{end}} + +{{define "link"}} + +{{end}} + +{{define "html"}}{{.HTML}}{{end}} diff --git a/template/tour.tmpl b/template/tour.tmpl new file mode 100644 index 0000000..9a76670 --- /dev/null +++ b/template/tour.tmpl @@ -0,0 +1,77 @@ +{{/* This is the tour template. */}} + +{{define "root"}} + + + + + +{{.Title}} + + + + + + + + +
      + + +
      + +{{range .Sections}} +
      + {{elem $.Template .}} +
      +{{end}} + +
      + + +
      + + + +{{end}} + +{{define "TOC"}} +
        + {{range .}} +
      • {{.Title}}
      • + {{with .Sections}}{{template "TOC" .}}{{end}} + {{end}} +
      +{{end}} + +{{define "newline"}} +{{/* No automatic line break. Paragraphs are free-form. */}} +{{end}} diff --git a/tour.article b/tour.article new file mode 100644 index 0000000..eae7a47 --- /dev/null +++ b/tour.article @@ -0,0 +1,829 @@ +Go 指南 + +Go 作者组 +http://golang.org + +# Throughout this file are a series of lines that begin with +# the string "#appengine: ". These lines are included in +# the tour content when deployed as an App Engine app. +# Furthermore, a single non-blank line that immediately follows +# a run of "#appengine: " lines will be hidden. This is so that +# App Engine-specific content may replace existing content. +# For example, this paragraph +# We are running +# #appengine: on App Engine. +# locally. +# Yay! +# reads as "We are running on App Engine. Yay!" on App Engine, +# and "We are running locally. Yay!" otherwise. + +* Hello, 世界 + +欢迎来到[[http://golang.org/][Go 编程语言]]指南。 + +该指南被分为三节。在每节的末尾有若干个练习需要读者完成。 + +该指南可以进行交互。点击“运行”按钮(或按 Shift + Enter)可以在 +#appengine: 远程服务器上 +你的电脑上 +编译并执行程序。 +结果展示在代码的下面。 + +这些例子程序展示了 Go 的各个方面。在指南中的程序可以成为你积累经验的开始。 + +编辑程序并且再次执行它。 + +当你准备好继续了,点击“向后”按钮或按 PageDown 键。 + +.play prog/hello.go + +* Go 本地化 + +该指南也有其他语言版本: + +- [[http://go-tour-br.appspot.com/][Brazilian Portuguese — Português do Brasil]] +- [[http://go-tour-ca.appspot.com/][Catalan — Català]] +- [[http://go-tour-he.appspot.com/][Hebrew — עִבְרִית]] +- [[http://go-tour-jp.appspot.com/][Japanese — 日本語]] +- [[http://go-tour-ro.appspot.com/][Romanian - Română]] +- [[http://go-tour-zh.appspot.com/][Chinese — 普通话]] + +点击“向后”按钮或者按 PageDown 继续。 + +* 包 + +每个 Go 程序都是由包组成的。 + +程序运行的入口是包 `main`。 + +这个程序使用并导入了包 `"fmt"` 和 `"math"`。 + +按照惯例,包名与导入路径的最后一个目录一致。 + +.play prog/packages.go + +* 导入 + +这个代码用圆括号组合了导入,这是“factored”导入语句。同样可以编写多个导入语句,例如: + + import "fmt" + import "math" + +.play prog/imports.go + +* 导出名 + +在导入了一个包之后,就可以用其导出的名称来调用它。 + +在 Go 中,首字母大写的名称是被导出的。 + +`Foo` 和 `FOO` 都是被导出的名称。名称 `foo` 是不会被导出的。 + +执行代码。然后将 `math.pi` 改名为 `math.Pi` 再试着执行一下。 + +.play prog/exported-names.go + +* 函数 + +函数可以没有参数或接受多个参数。 + +在这个例子中,`add` 接受两个 `int` 类型的参数。 + +注意类型在变量名 _之后_。 + +(参考 [[http://golang.org/doc/articles/gos_declaration_syntax.html][这篇关于 Go 语法定义的文章]]了解类型以这种形式出现的原因。) + +.play prog/functions.go + +* 函数(续) + +当两个或多个连续的函数命名参数是同一类型,则除了最后一个类型之外,其他都可以省略。 + +在这个例子中 , + + x int, y int + +被缩写为 + + x, y int + +.play prog/functions-continued.go + +* 多值返回 + +函数可以返回任意数量的返回值。 + +这个函数返回了两个字符串。 + +.play prog/multiple-results.go + +* 命名返回值 + +函数接受参数。在 Go 中,函数可以返回多个“结果参数”,而不仅仅是一个值。它们可以像变量那样命名和使用。 + +如果命名了返回值参数,一个没有参数的 `return` 语句,会将当前的值作为返回值返回。 + +.play prog/named-results.go + +* 变量 + +`var` 语句定义了一个变量的列表;跟函数的参数列表一样,类型在后面。 + +.play prog/variables.go + +* 初始化变量 + +变量定义可以包含初始值,每个变量对应一个。 + +如果初始化是使用表达式,则可以省略类型;变量从初始值中获得类型。 + +.play prog/variables-with-initializers.go + +* 短声明变量 + +在函数中,`:=` 简洁赋值语句在明确类型的地方,可以用于替代 `var` 定义。 + +(`:=` 结构不能使用在函数外,函数外的每个语法块都必须以关键字开始。) + +.play prog/short-variable-declarations.go + +* 基本类型 + +Go 的基本类型有Basic types + + bool + + string + + int int8 int16 int32 int64 + uint uint8 uint16 uint32 uint64 uintptr + + byte // uint8 的别名 + + rune // int32 的别名 + // 代表一个Unicode码 + + float32 float64 + + complex64 complex128 + +.play prog/basic-types.go + +* 常量 + +常量的定义与变量类似,只不过使用 `const` 关键字。 + +常量可以是字符、字符串、布尔或数字类型的值。 + +.play prog/constants.go + +* 数值常量 + +数值常量是高精度的 _值_。 + +一个未指定类型的常量由上下文来决定其类型。 + +也尝试一下输出 `needInt(Big)` 吧。 + +.play prog/numeric-constants.go + +* for + +Go 只有一种循环结构——`for` 循环。 + +基本的 `for` 循环除了没有了 `( )` 之外(甚至强制不能使用它们),看起来跟 C 或者 Java 中做的一样,而 `{ }` 是必须的。 + +.play prog/for.go + +* for(续) + +跟 C 或者 Java 中一样,可以让前置、后置语句为空。 + +.play prog/for-continued.go + +* for 是 Go 的 “while” + +基于此可以省略分号:C 的 `while` 在 Go 中叫做 `for`。 + +.play prog/for-is-gos-while.go + +* 死循环 + +如果省略了循环条件,循环就不会结束,因此可以用更简洁地形式表达死循环。 + +.play prog/forever.go + +* if + +`if` 语句除了没有了 `( )` 之外(甚至强制不能使用它们),看起来跟 C 或者 Java 中的一样,而 `{ }` 是必须的。 + +(耳熟吗?) + +.play prog/if.go + +* if 的便捷语句 + +跟 `for` 一样,`if` 语句可以在条件之前执行一个简单的语句。 + +由这个语句定义的变量的作用域仅在 `if` 范围之内。 + +(在最后的 `return` 语句处使用 `v` 看看。) + +.play prog/if-with-a-short-statement.go + +* if 和 else + +在 `if` 的便捷语句定义的变量同样可以在任何对应的 `else` 块中使用。 + +.play prog/if-and-else.go + +* 练习:循环和函数 + +作为练习函数和循环的简单途径,用牛顿法实现开方函数。 + +在这个例子中,牛顿法是通过选择一个初始点 _z_ 然后重复这一过程求 `Sqrt(x)` 的近似值: + +.image static/newton.png + +为了做到这个,只需要重复计算 10 次,并且观察不同的值(1,2,3,……)是如何逐步逼近结果的。 + 然后,修改循环条件,使得当值停止改变(或改变非常小)的时候退出循环。观察迭代次数是否变化。结果与 [[http://golang.org/pkg/math/#Sqrt][math.Sqrt] 接近吗? + +提示:定义并初始化一个浮点值,向其提供一个浮点语法或使用转换: + + z := float64(1) + z := 1.0 + +.play prog/exercise-loops-and-functions.go + +* 结构体 + +一个结构体(`struct`)就是一个字段的集合。 + +(而 `type` 的含义跟其字面意思相符。) + +.play prog/structs.go + +* 结构体字段 + +结构体字段使用点号来访问。 + +.play prog/struct-fields.go + +* 指针 + +Go 有指针,但是没有指针运算。 + +结构体字段可以通过结构体指针来访问。通过指针间接的访问是透明的。 + +.play prog/pointers.go + +* 结构体文法 + +结构体文法表示通过结构体字段的值作为列表来新分配一个结构体。 + +使用 `Name:` 语法可以仅列出部分字段。(字段名的顺序无关。) + +特殊的前缀 `&` 构造了指向结构体文法的指针。 + +.play prog/struct-literals.go + +* new 函数 + +表达式 `new(T)` 分配了一个零初始化的 `T` 值,并返回指向它的指针。 + + var t *T = new(T) + +或 + + t := new(T) + +.play prog/the-new-function.go + +# TODO(campoy): Arrays section + +* slice + +slice 指向一个数组,并且包含了长度信息。 + +`[]T` 是一个元素类型为 `T` 的 slice。 + +.play prog/slices.go + +* 对 slice 切片 + +slice 可以重新切片,创建一个新的 slice 值指向相同的数组。 + +表达式 + + s[lo:hi] + +表示从 `lo` 到 `hi-1` 的 slice 元素,含两端。因此 + + s[lo:lo] + +是空的,而 + + s[lo:lo+1] + +有一个元素。 + +.play prog/slicing-slices.go + +* 构造 slice + +slice 由函数 `make` 创建。这会分配一个零长度的数组并且返回一个 slice 指向这个数组: + + a := make([]int, 5) // len(a)=5 + +为了指定容量,可传递第三个参数到 `make`: + + b := make([]int, 0, 5) // len(b)=0, cap(b)=5 + + b = b[:cap(b)] // len(b)=5, cap(b)=5 + b = b[1:] // len(b)=4, cap(b)=4 + +.play prog/making-slices.go + +* 空 slice + +slice 的零值是 `nil`。 + +一个 nil 的 slice 的长度和容量是 0。 + +(了解更多关于 slice 的内容,参阅文章[[http://golang.org/doc/articles/slices_usage_and_internals.html][slice:使用和内幕]]。) + +.play prog/nil-slices.go + +# TODO(campoy): Appending to slices section + +* range + +`for` 循环的 `range` 格式可以对 slice 或者 map 进行迭代循环。 + +.play prog/range.go + +* range(续) + +可以将值赋值给 `_` 来忽略序号和值。 + +如果只需要索引值,去掉“`,`value`”的部分即可。 + +.play prog/range-continued.go + +* 练习:slice +实现 `Pic`。它返回一个 slice 的长度 `dy`,和 slice 中每个元素的长度的 8 位无符号整数 `dx`。当执行这个程序,它会将整数转换为灰度(好吧,蓝度)图片进行展示。 + +图片的实现已经完成。可能用到的函数包括 `>x^y`,`(x+y)/2` 和 `x*y`。 + +(需要使用循环来分配 `[][]uint8` 中的每个 `[]uint8`。) + +(使用 `uint8(intValue)` 在类型之间进行转换。) + +.play prog/exercise-slices.go + +* map + +map 映射键到值。 + +map 在使用之前必须用 `make` 而不是 `new` 来创建;值为 `nil` 的 map 是空的,并且不能赋值。 + +.play prog/maps.go + +* map 的文法 + +map 的文法跟结构体文法相似,不过必须有键名。 + +.play prog/map-literals.go + +* map 的文法(续) + +如果顶级的类型只有类型名的话,可以在文法的元素中省略键名。 + +.play prog/map-literals-continued.go + +* 修改 map + +在 map `m` 中插入或修改一个元素: + + m[key] = elem + +获得元素: + + elem = m[key] + +删除元素: + + delete(m, key) + +通过双赋值检测某个键存在: + + elem, ok = m[key] + +如果 `key` 在 `m` 中,`ok` 为 `true`。否则,`ok` 为 `false`,并且 `elem` 是 map 的元素类型的零值。 + +同样的,当从 map 中读取某个不存在的键时,结果是 map 的元素类型的零值。 + +.play prog/mutating-maps.go + +# TODO(campoy): Range on maps section + +* 练习:map + +实现 `WordCount`。它应当返回一个含有 `s` 中每个 “词” 个数的 map。函数 `wc.Test` 针对这个函数执行一个测试用例,并打印成功或者失败。 + +你会发现 [[http://golang.org/pkg/strings/#Fields][strings.Fields]] 很有帮助。 + +.play prog/exercise-maps.go + +* 函数为值 + +函数也是值。Function values + +.play prog/function-values.go + +* 函数的闭包 + +并且函数是完全闭包的。 + +函数 `adder` 返回一个闭包。每个闭包被绑定到了特定的 `sum` 变量上。 + +.play prog/function-closures.go + +* 练习:斐波纳契闭包 + +现在来通过函数做些有趣的事情。 + +实现一个 `fibonacci` 函数,返回一个函数(一个闭包)可以返回连续的斐波纳契数。 + +.play prog/exercise-fibonacci-closure.go + +* switch + +你可能已经猜到 `switch` 可能的形式了。 + +除非使用 `fallthrough` 语句作为结尾,否则 case 部分会自动终止。 + +.play prog/switch.go + +* switch 的执行顺序 + +switch 的条件从上到下的执行,当匹配成功的时候停止。 + +(例如, + + switch i { + case 0: + case f(): + } + +当 `i==0` 时不会调用 `f`。) + +#appengine: 注意:Go playground 中的时间总是从 2009-11-10 23:00:00 UTC 开始, +#appengine: 如何校验这个值作为一个练习留给读者完成。 + +.play prog/switch-evaluation-order.go + +* 没有条件的 switch + +没有条件的 switch 同 `switch true` 一样。 + +这一构造使得可以用更清晰的形式来编写长的 if-then-else 链。 + +.play prog/switch-with-no-condition.go + +* 进阶练习:复数立方根 + +让我们通过 `complex64` 和 `complex128` 来探索一下 Go 内建的复数。对于立方根,牛顿法需要大量循环: + +.image static/newton3.png + +找到 2 的立方根,确保算法能够工作。在 `math/cmplx` 包中有 [[http://golang.org/pkg/math/cmplx/#Pow][Pow]] 函数。 + +.play prog/advanced-exercise-complex-cube-roots.go + +* 方法和接口 + +* 方法 + +Go 没有类。然而,仍然可以在结构体类型上定义方法。 + +_方法接收者_ 出现在 `func` 关键字和方法名之间的参数中。 + +.play prog/methods.go + +* 方法(续) + +事实上,可以对包中的 _任意_ 类型定义任意方法,而不仅仅是针对结构体。 + +不能对来自其他包的类型或基础类型定义方法。 + +.play prog/methods-continued.go + +* 接收者为指针的方法 + +方法可以与命名类型或命名类型的指针关联。 + +刚刚看到的两个 `Abs` 方法。一个是在 `*Vertex` 指针类型上,而另一个在 `MyFloat` 值类型上。 + 有两个原因需要使用指针接收者。首先避免在每个方法调用中拷贝值(如果值类型是大的结构体的话会更有效率)。其次,方法可以修改接收者指向的值。 + +尝试修改 `Abs` 的定义,同时 `Scale` 方法使用 `Vertex` 代替 `*Vertex` 作为接收者。 + +当 `v` 是 `Vertex` 的时候 `Scale` 方法没有任何作用。`Scale` 修改 `v`。当 `v` 是一个值(非指针),方法看到的是 `Vertex` 的副本,并且无法修改原始值。 + +`Abs` 的工作方式是一样的。只不过,仅仅读取 `v`。所以读取的是原始值(通过指针)还是那个值的副本并没有关系。 + +.play prog/methods-with-pointer-receivers.go + +* 接口 + +接口类型是由一组方法定义的集合。 + +接口类型的值可以存放实现这些方法的任何值。 + +.play prog/interfaces.go + +* 隐式接口 + +类型通过实现那些方法来实现接口。 + +_没有显式声明的必要。_ + +隐式接口解藕了实现接口的包和定义接口的包:互不依赖。 + +因此,也就无需在每一个实现上增加新的接口名称,这样同时也鼓励了明确的接口定义。 + +[[http://golang.org/pkg/io/][包 io]] 定义了 `Reader` 和 `Writer`;其实不一定要这么做。 + +.play prog/interfaces-are-satisfied-implicitly.go + +* 错误 + +错误是可以用字符串描述自己的任何东西。主要思路是由预定义的内建接口类型 `error`,和方法 `Error`,返回字符串: + + type error interface { + Error() string + } + +当用 `fmt` 包的多种不同的打印函数输出一个 `error` 时,会自动的调用该方法。 + +.play prog/errors.go + +* 练习:错误 + +从之前的练习中复制 `Sqrt` 函数,并修改使其返回 `error` 值。 + +`Sqrt` 接收到一个负数时,应当返回一个非 nil 的错误值。复数同样也不被支持。 + +创建一个新类型 + + type ErrNegativeSqrt float64 + +为其实现 + + func (e ErrNegativeSqrt) Error() string + +使其成为一个 `error`, 该方法就可以让 `ErrNegativeSqrt(-2).Error()` 返回 `"cannot Sqrt negative number: -2"`。 + +*注意:* 在 `Error` 方法内调用 `fmt.Print(e)` 将会让程序陷入死循环。可以通过先转换 `e` 来避免这个问题:`fmt.Print(float64(e))`。请思考这是为什么呢? + +修改 `Sqrt` 函数,使其接受一个负数时,返回 `ErrNegativeSqrt` 值。 + +.play prog/exercise-errors.go + +* Web 服务器 + +[[http://golang.org/pkg/net/http/][包 http]] 通过任何实现了 `http.Handler` 的值来响应 HTTP 请求: + + package http + + type Handler interface { + ServeHTTP(w ResponseWriter, r *Request) + } + +在这个例子中,类型 `Hello` 实现了 `http.Handler`。 + +访问 [[http://localhost:4000/][http://localhost:4000/]] 会看到来自程序的问候。 + +#appengine: *注意:* 这个例子无法在基于 web 的指南用户界面运行。为了尝试编写 +#appengine: web 服务器,可能需要[[http://golang.org/doc/install/][安装 Go]]。 + +.play prog/web-servers.go + +* 练习:HTTP 处理 + +实现下面的类型,并在其上定义 ServeHTTP 方法。在 web 服务器中注册它们来处理指定的路径。 + + type String string + + type Struct struct { + Greeting string + Punct string + Who string + } + +例如,可以使用如下方式注册处理方法: + + http.Handle("/string", String("I'm a frayed knot.")) + http.Handle("/struct", &Struct{"Hello", ":", "Gophers!"}) + +.play prog/exercise-http-handlers.go + +* 图片 + +[[http://golang.org/pkg/image/#Image][Package image]] 定义了 `Image` 接口: + + package image + + type Image interface { + ColorModel() color.Model + Bounds() Rectangle + At(x, y int) color.Color + } + +(参阅[[http://golang.org/pkg/image/#Image][文档]]了解全部信息。) + +同样,`color.Color` 和 `color.Model` 也是接口,但是通常因为直接使用预定义的实现 `image.RGBAColor` 和 `image.RGBAColorModel` 而被忽视了。 + +.play prog/images.go + +* 练习:图片 + +还记得之前编写的图片生成器吗?现在来另外编写一个,不过这次将会返回 `image.Image` 来代替 slice 的数据。 + +自定义的 `Image` 类型,要实现[[http://golang.org/pkg/image/#Image][必要的方法]],并且调用 `pic.ShowImage`。 + +`Bounds` 应当返回一个 `image.Rectangle`,例如 `image.Rect(0, 0, w, h)`。 + +`ColorModel` 应当返回 `image.RGBAColorModel`。 + +`At` 应当返回一个颜色;在这个例子里,在最后一个图片生成器的值 `v` 匹配 `color.RGBAColor{v, v, 255, 255}`。 + +.play prog/exercise-images.go + +* 练习:Rot13 读取器 + +一般的模式是 [[http://golang.org/pkg/io/#Reader][io.Reader]] 包裹另一个 `io.Reader`,用某些途径修改特定的流。 + +例如,[[http://golang.org/pkg/compress/gzip/#NewReader][gzip.NewReader]] 函数输入一个 `io.Reader`(gzip 的数据流)并且返回一个同样实现了 `io.Reader` 的 `*gzip.Reader`(解压缩后的数据流)。 + +实现一个实现了 `io.Reader` 的 `rot13Reader`,用 [[http://en.wikipedia.org/wiki/ROT13][ROT13]] 修改数据流中的所有的字母进行密文替换。 + +`rot13Reader` 已经提供。通过实现其 `Read` 方法使得它匹配 `io.Reader`。 + +.play prog/exercise-rot-reader.go + +* 并发 + +* goroutine + +_goroutine_ 是由 Go 运行时环境管理的轻量级线程。 + + go f(x, y, z) + +开启一个新的 goroutine 执行 + + f(x, y, z) + +`f`,`x`,`y` 和 `z` 是当前 goroutine 中定义的,但是在新的 goroutine 中运行 `f`。 + +goroutine 在相同的地址空间中运行,因此访问共享内存必须进行同步。`[[http://golang.org/pkg/sync/][sync]]` 提供了这种可能,不过在 Go 中并不经常用到,因为有其他的办法。(在接下来的内容中会涉及到。) + +.play prog/goroutines.go + +* channel + +channel 是有类型的管道,可以用 channel 操作符 `<-` 对其发送或者接收值。 + + ch <- v // 将 v 送入 channel ch。 + v := <-ch // 从 ch 接收,并且赋值给 v。 + +(“箭头”就是数据流的方向。) + +和 map 与 slice 一样,channel 使用前必须创建: + + ch := make(chan int) + +默认情况下,在另一端准备好之前,发送和接收都会阻塞。这使得 goroutine 可以在没有明确的锁或竞态变量的情况下进行同步。 + +.play prog/channels.go + +* 缓冲 channel + +channel 可以是 _带缓冲的_。为 `make` 提供第二个参数作为缓冲长度来初始化一个缓冲 channel: + + ch := make(chan int, 100) + +向缓冲 channel 发送数据的时候,只有在缓冲区满的时候才会阻塞。当缓冲区清空的时候接受阻塞。 + +修改例子使得缓冲区被填满,然后看看会发生什么。 + +.play prog/buffered-channels.go + +* range 和 close + +发送者可以 `close` 一个 channel 来表示再没有值会被发送了。接收者可以通过赋值语句的第二参数来测试 channel 是否被关闭:当没有值可以接收并且 channel 已经被关闭,那么经过 + + v, ok := <-ch + +之后 `ok` 会被设置为 `false`。 + +循环 `for i := range c` 会不断从 channel 接收值,直到它被关闭。 + +*注意:* 只有发送者才能关闭 channel,而不是接收者。向一个已经关闭的 channel 发送数据会引起 panic。 +*还要注意:* channel 与文件不同;通常情况下无需关闭它们。只有在需要告诉接收者没有更多的数据的时候才有必要进行关闭,例如中断一个 `range`。 + +.play prog/range-and-close.go + +* select + +`select` 语句使得一个 goroutine 在多个通讯操作上等待。 + +`select` 会阻塞,直到条件分支中的某个可以继续执行,这时就会执行那个条件分支。当多个都准备好的时候,会随机选择一个。 + +.play prog/select.go + +* 默认选择 + +当 `select` 中的其他条件分支都没有准备好的时候,`default` 分支会被执行。 + +为了非阻塞的发送或者接收,可使用 `default` 分支: + + select { + case i := <-c: + // 使用 i + default: + // 从 c 读取会阻塞 + } + +.play prog/default-selection.go + +* 练习:等价二叉树 + +可以用多种不同的二叉树的叶子节点存储相同的数列值。例如,这里有两个二叉树保存了序列 1,1,2,3,5,8,13。 + +.image static/tree.png + +用于检查两个二叉树是否存储了相同的序列的函数在多数语言中都是相当复杂的。这里将使用 Go 的并发和 channel 来编写一个简单的解法。 + +这个例子使用了 `tree` 包,定义了类型: + + type Tree struct { + Left *Tree + Value int + Right *Tree + } + +* 练习:等价二叉树 + +*1.* 实现 `Walk` 函数。 + +*2.* 测试 `Walk` 函数。 + +函数 `tree.New(k)` 构造了一个随机结构的二叉树,保存了值 `k`,`2k`,`3k`,...,`10k`。 +创建一个新的 channel `ch` 并且对其进行步进: + + go Walk(tree.New(1), ch) + +然后从 channel 中读取并且打印 10 个值。应当是值 1,2,3,...,10。 + +*3.* 用 `Walk` 实现 `Same` 函数来检测是否 `t1` 和 `t2` 存储了相同的值。 + +*4.* 测试 `Same` 函数。 + +`Same(tree.New(1), tree.New(1))` 应当返回 true,而 `Same(tree.New(1), tree.New(2))` 应当返回 false。 + +.play prog/exercise-equivalent-binary-trees.go + +* 练习:Web 爬虫 + +在这个练习中,将会使用 Go 的并发特性来并行执行 web 爬虫。 + +修改 `Crawl` 函数来并行的抓取 URLs,并且保证不重复。 + +.play prog/exercise-web-crawler.go + +* Where to Go from here... + +#appengine: 你可以从 +#appengine: [[http://golang.org/doc/install/][安装 Go]] 开始,或者下载 +#appengine: [[http://code.google.com/appengine/downloads.html#Google_App_Engine_SDK_for_Go][Go App Engine SDK]]. + +#appengine: 一旦安装了 Go, +[[http://golang.org/doc/][Go 文档]] 是一个极好的 +#appengine: 应当继续阅读的内容。 +开始。 +它包含了参考、指南、视频等等更多资料。 + +了解如何组织 Go 代码并在其上工作,参阅 [[http://www.youtube.com/watch?v=XCsL89YtqCs][这个视频]],或者阅读[[http://golang.org/doc/code.html][如何编写 Go 代码]] + +在标准库上需要帮助的话,参考[[http://golang.org/pkg/][包手册]]。语言本身的帮助,阅读[[http://golang.org/ref/spec][语言规范]]是件令人愉快的事情。 + +进一步探索 Go 的并发模型,参阅 [[http://golang.org/doc/codewalk/sharemem/][Share Memory by Communicating]] 的代码之旅。 + +[[http://golang.org/doc/codewalk/functions/][First Class Functions in Go]] 展示了有趣的函数类型。 + +[[http://blog.golang.org/][Go Blog]] 有着众多的关于 Go 的文章信息。 + +[[http://www.mikespook.com/tag/golang/][mikespook 的博客]]有大量中文的关于 Go 的文章和翻译。 + +访问 [[http://golang.org][golang.org]] 了解更多内容。