Chapter 1 Tutorial

Exercise 1.1: Modify theechoprogram to also print os.Args[0], the name of the command that invoked it.

// Copyright © 2017 [email protected]
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/

// 1.1 prints itself and its command-line arguments.
package main

import (
    "fmt"
    "os"
    "strings"
)

func main() {
    fmt.Println(strings.Join(os.Args, " "))
}

Exercise 1.2: Modify theechoprogram to print the index and value of each of its arguments, one per line.

// Copyright © 2017 [email protected]
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/

// 1.2 prints the index and value of each of its arguments, one per line.
package main

import (
    "fmt"
    "os"
)

func main() {
    for i, v := range os.Args[1:] {
        fmt.Println(i, v)
    }
}

Exercise 1.3: Experiment to measure the difference in running time between our potentially inefficient versions and the one that usesstrings.Join. (Section 1.6 illustrates part of the time package, and Section 11.4 shows how to write benchmark tests for systematic performance evaluation.)

// Copyright © 2017 [email protected]
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/

// 1.3 measure the time of different implementation of echo
package main

import (
    "fmt"
    "os"
    "strings"
    "time"
)

func echo1() string {
    start := time.Now()
    defer func() {
        fmt.Printf("echo1: %v ns\n", time.Since(start).Nanoseconds())
    }()

    var s, sep string
    for i := 1; i < len(os.Args); i++ {
        s += sep + os.Args[i]
        sep = " "
    }
    return s
}

func echo2() string {
    start := time.Now()
    defer func() {
        fmt.Printf("echo2: %v ns\n", time.Since(start).Nanoseconds())
    }()

    s, sep := "", ""
    for _, arg := range os.Args[1:] {
        s += sep + arg
        sep = " "
    }
    return s
}

func echo3() string {
    start := time.Now()
    defer func() {
        fmt.Printf("echo3: %v ns\n", time.Since(start).Nanoseconds())
    }()

    return strings.Join(os.Args[1:], " ")
}

func main() {
    echo1()
    echo2()
    echo3()
}

// go run main.go a b c d e f g h i j k l m n o p q r s t u v w x y z
// output:
// echo1: 4743 ns
// echo2: 2495 ns
// echo3: 1097 ns

Exercise 1.4: Modifydup2to print the names of all files in which each duplicated line occurs.

// Copyright © 2017 [email protected]
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/

// 1.4 prints the count and text of lines that appear more than once
// in the input.  It reads from stdin or from a list of named files.
// It also print the names of all files in which each duplicated line occurs.
package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    counts := make(map[string][]string)
    files := os.Args[1:]
    if len(files) == 0 {
        countLines(os.Stdin, counts)
    } else {
        for _, arg := range files {
            f, err := os.Open(arg)
            if err != nil {
                fmt.Fprintf(os.Stderr, "dup2: %v\n", err)
                continue
            }
            countLines(f, counts)
            f.Close()
        }
    }
    for line, files := range counts {
        if len(files) > 1 {
            fmt.Printf("%d\t%s\t%s\n", len(files), line, files)
        }
    }
}

func countLines(f *os.File, counts map[string][]string) {
    input := bufio.NewScanner(f)
    for input.Scan() {
        counts[input.Text()] = append(counts[input.Text()], f.Name())
    }
    // NOTE: ignoring potential errors from input.Err()
}

Exercise 1.5: Change the Lissajous program’s color palette to green on black, for added authenticity. To create the web color#RRGGBB, usecolor.RGBA{0xRR, 0xGG, 0xBB, 0xff}, where each pair of hexadecimal digits represents the intensity of the red, green, or blue component of the pixel.

var palette = []color.Color{color.White, color.RGBA{0, 0xff, 0, 0xff}}

Exercise 1.6: Modify theLissajousprogram to produce images in multiple colors by adding more values topaletteand then displaying them by changing the third argument ofSetColorIndexin some interesting way.

    // ....
    var colorIndex uint8 = blackIndex
    if i%3 == 0 {
        colorIndex = uint8(rand.Uint32()%5 + 1)
    }

    for t := 0.0; t < cycles*2*math.Pi; t += res {
        x := math.Sin(t)
        y := math.Sin(t*freq + phase)
        img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5), colorIndex)
    }
    // ...

Exercise1.7: The function callio.Copy(dst,src)reads from src and writes to dst. Use it instead ofioutil.ReadAllto copy the response body toos.Stdoutwithout requiring a buffer large enough to hold the entire stream. Be sure to check the error result ofio.Copy.

// Copyright © 2017 [email protected]
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/

// Fetch prints the content found at each specified URL.
package main

import (
    "fmt"
    "net/http"
    "os"
    "io"
)

func main() {
    for _, url := range os.Args[1:] {
        resp, err := http.Get(url)
        if err != nil {
            fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
            os.Exit(1)
        }
        _, err = io.Copy(os.Stdout, resp.Body)
        resp.Body.Close()
        if err != nil {
            fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)
            os.Exit(1)
        }
    }
}

Exercise 1.8: Modifyfetchto add the prefix http:// to each argument URL if it is missing. You might want to usestrings.HasPrefix.

// Copyright © 2017 [email protected]
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/

// Fetch prints the content found at each specified URL.
package main

import (
    "fmt"
    "net/http"
    "os"
    "io"
    "io/ioutil"
    "strings"
)

func main() {
    for _, url := range os.Args[1:] {

        if !strings.HasPrefix(url, "http://") {
            url = "http://" + url
        }

        resp, err := http.Get(url)
        if err != nil {
            fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
            os.Exit(1)
        }
        b, err := ioutil.ReadAll(resp.Body)
        _, err = io.Copy(os.Stdout, resp.Body)
        resp.Body.Close()
        if err != nil {
            fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)
            os.Exit(1)
        }
        fmt.Printf("%s", b)
    }
}

Exercise 1.9: Modifyfetchto also print the HTTP status code, found inresp.Status.

// Copyright © 2017 [email protected]
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/

// Fetch prints the content found at each specified URL.
package main

import (
    "fmt"
    "net/http"
    "os"
    "io"
    "io/ioutil"
)

func main() {
    for _, url := range os.Args[1:] {
        resp, err := http.Get(url)
        if err != nil {
            fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
            os.Exit(1)
        }
        b, err := ioutil.ReadAll(resp.Body)
        _, err = io.Copy(os.Stdout, resp.Body)
        resp.Body.Close()
        if err != nil {
            fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)
            os.Exit(1)
        }
        fmt.Printf("%s", b)
        fmt.Printf("Status: %s\n",resp.Status)
    }
}

Exercise 1.10: Find a web site that produces a large amount of data. Investigate caching by running fetchall twice in succession to see whether the reported time changes much. Do you get the same content each time? Modify fetchall to print its output to a file so it can be examined.

// Copyright © 2017 [email protected]
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/

// Fetchall fetches URLs in parallel and reports their times and sizes.
package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "time"
    "strings"
)

// reported elapse time changed with successive run and content is not the same
func main() {
    start := time.Now()
    ch := make(chan string)
    for _, url := range os.Args[1:] {
        go fetch(url, ch) // start a goroutine
    }
    for range os.Args[1:] {
        fmt.Println(<-ch) // receive from channel ch
    }
    fmt.Printf("%.2fs elapsed\n", time.Since(start).Seconds())
}

func fetch(url string, ch chan<- string) {
    i, j := strings.Index(url, "."), strings.LastIndex(url, ".")
    // in case of only one "." in url
    if i == j {
        i = 0
    }
    output, err := os.Create(url[i+1:j] + "-dump.html")

    start := time.Now()
    if err != nil {
        ch <- fmt.Sprintf("while create output file: %v", err)
        return
    }

    resp, err := http.Get(url)
    if err != nil {
        ch <- fmt.Sprint(err) // send to channel ch
        return
    }

    nbytes, err := io.Copy(output, resp.Body)
    resp.Body.Close() // don't leak resources
    output.Close()
    if err != nil {
        ch <- fmt.Sprintf("while reading %s: %v", url, err)
        return
    }
    secs := time.Since(start).Seconds()
    ch <- fmt.Sprintf("%.2fs  %7d  %s", secs, nbytes, url)
}

// ..whether the reported time changes much.
// Yes 
// Do you get the same content each time?
// No

Exercise 1.11: Try fetchall with longer argument lists, such as samples from the top million web sites available atalexa.com. How does the program behave if a web site just doesn’t respond? (Section 8.9 describes mechanisms for coping in such cases.)

Get http://www.google.com: dial tcp: i/o timeout

or

Get http://www.google.com: EOF

Exercise 1.12: Modify the Lissajous server to read parameter values from the URL. For example, you might arrange it so that a URL like http://localhost:8000/?cycles=20 sets the number of cycles to 20 instead of the default 5. Use the strconv.Atoi function to convert the string parameter into an integer. You can see its documentation with go doc strconv.Atoi.

// Copyright © 2017 [email protected]
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/

// Run with "web" command-line argument for web server.

// Lissajous generates GIF animations of random Lissajous figures.
package main

import (
    "image"
    "image/color"
    "image/gif"
    "io"
    "math"
    "math/rand"
    "os"
)

//!-main
// Packages not needed by version in book.
import (
    "log"
    "net/http"
    "time"
    "strconv"
)

//!+main

var palette = []color.Color{color.White, color.Black}

const (
    whiteIndex = 0 // first color in palette
    blackIndex = 1 // next color in palette
)

var cycleParam int

func main() {
    //!-main
    // The sequence of images is deterministic unless we seed
    // the pseudo-random number generator using the current time.
    // Thanks to Randall McPherson for pointing out the omission.
    rand.Seed(time.Now().UTC().UnixNano())

    if len(os.Args) > 1 && os.Args[1] == "web" {
        //!+http
        handler := func(w http.ResponseWriter, r *http.Request) {
            if err := r.ParseForm(); err != nil {
                log.Print(err)
            }
            for k, v := range r.Form {
                if k == "cycles" {
                    var err error
                    cycleParam, err = strconv.Atoi(v[0]);
                    if err != nil {
                        log.Print(err)
                        cycleParam = 0
                    }
                }
            }

            lissajous(w)
        }
        http.HandleFunc("/", handler)
        //!-http
        log.Fatal(http.ListenAndServe("localhost:8000", nil))
        return
    }
    //!+main
    lissajous(os.Stdout)
}

func lissajous(out io.Writer) {
    const (
        cycles  = 5     // number of complete x oscillator revolutions
        res     = 0.001 // angular resolution
        size    = 100   // image canvas covers [-size..+size]
        nframes = 64    // number of animation frames
        delay   = 8     // delay between frames in 10ms units
    )

    var cyc float64
    if cycleParam > 0 {
        cyc = float64(cycleParam)
    } else {
        cyc = cycles
    }

    freq := rand.Float64() * 3.0 // relative frequency of y oscillator
    anim := gif.GIF{LoopCount: nframes}
    phase := 0.0 // phase difference
    for i := 0; i < nframes; i++ {
        rect := image.Rect(0, 0, 2*size+1, 2*size+1)
        img := image.NewPaletted(rect, palette)
        for t := 0.0; t < cyc*2*math.Pi; t += res {
            x := math.Sin(t)
            y := math.Sin(t*freq + phase)
            img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5),
                blackIndex)
        }
        phase += 0.1
        anim.Delay = append(anim.Delay, delay)
        anim.Image = append(anim.Image, img)
    }
    gif.EncodeAll(out, &anim) // NOTE: ignoring encoding errors
}

Notes

  • Goroutines: A goroutine is a concurrent function execution. A channelis a communication mechanism that allows one goroutine to pass values of a specified type to another goroutine. The function main runs in a goroutine and the go statement creates additional goroutines.

  • When one goroutine attempts a send or receive on a channel, it blocks until another goroutine attempts the corresponding receive or send operation, at which point the value is transferred and both goroutines proceed.

  • Having main do all the printing ensures that output from each goroutine is processed as a unit, with no danger of interleaving if two goroutines finish at the same time.

  • The server runs the handler for each incoming request in a separate goroutine so that it can serve multiple requests simultaneously.

  • Function literal: that is, an anonymous function defined at its point of use.

  • Control flow: The optional default case matches if none of the other cases does; it may be placed anywhere. Cases do not fall through from one to the next as in C-like languages (though there is a rarely used fallthrough statement that overrides this behavior).

  • Pointers: Go provides pointers, that is, values that contain the address of a variable. In some languages, notably C, pointers are relatively unconstrained. In other languages, pointers are disguised as ‘‘references,’’and there’s not much that can be done with them except pass them around. Go takes a position somewhere in the middle. Pointers are explicitly visible. The&operator yields the address of a variable, and the*operator retrieves the variable that the pointer refers to, but there is no pointer arithmetic.

  • Methods and interfaces: A method is a function associated with a named type; Go is unusual in that methods may be attached to almost any named type.

results matching ""

    No results matching ""