mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-09-12 06:41:33 +00:00
good good study, day day up!
This commit is contained in:
41
vendor/gopl.io/ch11/echo/echo.go
generated
vendored
Normal file
41
vendor/gopl.io/ch11/echo/echo.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 308.
|
||||
//!+
|
||||
|
||||
// Echo prints its command-line arguments.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
n = flag.Bool("n", false, "omit trailing newline")
|
||||
s = flag.String("s", " ", "separator")
|
||||
)
|
||||
|
||||
var out io.Writer = os.Stdout // modified during testing
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if err := echo(!*n, *s, flag.Args()); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "echo: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func echo(newline bool, sep string, args []string) error {
|
||||
fmt.Fprint(out, strings.Join(args, sep))
|
||||
if newline {
|
||||
fmt.Fprintln(out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//!-
|
45
vendor/gopl.io/ch11/echo/echo_test.go
generated
vendored
Normal file
45
vendor/gopl.io/ch11/echo/echo_test.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// Test of echo command. Run with: go test gopl.io/ch11/echo
|
||||
|
||||
//!+
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEcho(t *testing.T) {
|
||||
var tests = []struct {
|
||||
newline bool
|
||||
sep string
|
||||
args []string
|
||||
want string
|
||||
}{
|
||||
{true, "", []string{}, "\n"},
|
||||
{false, "", []string{}, ""},
|
||||
{true, "\t", []string{"one", "two", "three"}, "one\ttwo\tthree\n"},
|
||||
{true, ",", []string{"a", "b", "c"}, "a,b,c\n"},
|
||||
{false, ":", []string{"1", "2", "3"}, "1:2:3"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
descr := fmt.Sprintf("echo(%v, %q, %q)",
|
||||
test.newline, test.sep, test.args)
|
||||
|
||||
out = new(bytes.Buffer) // captured output
|
||||
if err := echo(test.newline, test.sep, test.args); err != nil {
|
||||
t.Errorf("%s failed: %v", descr, err)
|
||||
continue
|
||||
}
|
||||
got := out.(*bytes.Buffer).String()
|
||||
if got != test.want {
|
||||
t.Errorf("%s = %q, want %q", descr, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
45
vendor/gopl.io/ch11/storage1/storage.go
generated
vendored
Normal file
45
vendor/gopl.io/ch11/storage1/storage.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 311.
|
||||
|
||||
// Package storage is part of a hypothetical cloud storage server.
|
||||
//!+main
|
||||
package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/smtp"
|
||||
)
|
||||
|
||||
var usage = make(map[string]int64)
|
||||
|
||||
func bytesInUse(username string) int64 { return usage[username] }
|
||||
|
||||
// Email sender configuration.
|
||||
// NOTE: never put passwords in source code!
|
||||
const sender = "notifications@example.com"
|
||||
const password = "correcthorsebatterystaple"
|
||||
const hostname = "smtp.example.com"
|
||||
|
||||
const template = `Warning: you are using %d bytes of storage,
|
||||
%d%% of your quota.`
|
||||
|
||||
func CheckQuota(username string) {
|
||||
used := bytesInUse(username)
|
||||
const quota = 1000000000 // 1GB
|
||||
percent := 100 * used / quota
|
||||
if percent < 90 {
|
||||
return // OK
|
||||
}
|
||||
msg := fmt.Sprintf(template, used, percent)
|
||||
auth := smtp.PlainAuth("", sender, password, hostname)
|
||||
err := smtp.SendMail(hostname+":587", auth, sender,
|
||||
[]string{username}, []byte(msg))
|
||||
if err != nil {
|
||||
log.Printf("smtp.SendMail(%s) failed: %s", username, err)
|
||||
}
|
||||
}
|
||||
|
||||
//!-main
|
53
vendor/gopl.io/ch11/storage2/quota_test.go
generated
vendored
Normal file
53
vendor/gopl.io/ch11/storage2/quota_test.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
//!+test
|
||||
package storage
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCheckQuotaNotifiesUser(t *testing.T) {
|
||||
var notifiedUser, notifiedMsg string
|
||||
notifyUser = func(user, msg string) {
|
||||
notifiedUser, notifiedMsg = user, msg
|
||||
}
|
||||
|
||||
const user = "joe@example.org"
|
||||
usage[user] = 980000000 // simulate a 980MB-used condition
|
||||
|
||||
CheckQuota(user)
|
||||
if notifiedUser == "" && notifiedMsg == "" {
|
||||
t.Fatalf("notifyUser not called")
|
||||
}
|
||||
if notifiedUser != user {
|
||||
t.Errorf("wrong user (%s) notified, want %s",
|
||||
notifiedUser, user)
|
||||
}
|
||||
const wantSubstring = "98% of your quota"
|
||||
if !strings.Contains(notifiedMsg, wantSubstring) {
|
||||
t.Errorf("unexpected notification message <<%s>>, "+
|
||||
"want substring %q", notifiedMsg, wantSubstring)
|
||||
}
|
||||
}
|
||||
|
||||
//!-test
|
||||
|
||||
/*
|
||||
//!+defer
|
||||
func TestCheckQuotaNotifiesUser(t *testing.T) {
|
||||
// Save and restore original notifyUser.
|
||||
saved := notifyUser
|
||||
defer func() { notifyUser = saved }()
|
||||
|
||||
// Install the test's fake notifyUser.
|
||||
var notifiedUser, notifiedMsg string
|
||||
notifyUser = func(user, msg string) {
|
||||
notifiedUser, notifiedMsg = user, msg
|
||||
}
|
||||
// ...rest of test...
|
||||
}
|
||||
//!-defer
|
||||
*/
|
49
vendor/gopl.io/ch11/storage2/storage.go
generated
vendored
Normal file
49
vendor/gopl.io/ch11/storage2/storage.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 312.
|
||||
|
||||
// Package storage is part of a hypothetical cloud storage server.
|
||||
package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/smtp"
|
||||
)
|
||||
|
||||
var usage = make(map[string]int64)
|
||||
|
||||
func bytesInUse(username string) int64 { return usage[username] }
|
||||
|
||||
// E-mail sender configuration.
|
||||
// NOTE: never put passwords in source code!
|
||||
const sender = "notifications@example.com"
|
||||
const password = "correcthorsebatterystaple"
|
||||
const hostname = "smtp.example.com"
|
||||
|
||||
const template = `Warning: you are using %d bytes of storage,
|
||||
%d%% of your quota.`
|
||||
|
||||
//!+factored
|
||||
var notifyUser = func(username, msg string) {
|
||||
auth := smtp.PlainAuth("", sender, password, hostname)
|
||||
err := smtp.SendMail(hostname+":587", auth, sender,
|
||||
[]string{username}, []byte(msg))
|
||||
if err != nil {
|
||||
log.Printf("smtp.SendEmail(%s) failed: %s", username, err)
|
||||
}
|
||||
}
|
||||
|
||||
func CheckQuota(username string) {
|
||||
used := bytesInUse(username)
|
||||
const quota = 1000000000 // 1GB
|
||||
percent := 100 * used / quota
|
||||
if percent < 90 {
|
||||
return // OK
|
||||
}
|
||||
msg := fmt.Sprintf(template, used, percent)
|
||||
notifyUser(username, msg)
|
||||
}
|
||||
|
||||
//!-factored
|
21
vendor/gopl.io/ch11/word1/word.go
generated
vendored
Normal file
21
vendor/gopl.io/ch11/word1/word.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 303.
|
||||
//!+
|
||||
|
||||
// Package word provides utilities for word games.
|
||||
package word
|
||||
|
||||
// IsPalindrome reports whether s reads the same forward and backward.
|
||||
// (Our first attempt.)
|
||||
func IsPalindrome(s string) bool {
|
||||
for i := range s {
|
||||
if s[i] != s[len(s)-1-i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//!-
|
43
vendor/gopl.io/ch11/word1/word_test.go
generated
vendored
Normal file
43
vendor/gopl.io/ch11/word1/word_test.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
//!+test
|
||||
package word
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPalindrome(t *testing.T) {
|
||||
if !IsPalindrome("detartrated") {
|
||||
t.Error(`IsPalindrome("detartrated") = false`)
|
||||
}
|
||||
if !IsPalindrome("kayak") {
|
||||
t.Error(`IsPalindrome("kayak") = false`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonPalindrome(t *testing.T) {
|
||||
if IsPalindrome("palindrome") {
|
||||
t.Error(`IsPalindrome("palindrome") = true`)
|
||||
}
|
||||
}
|
||||
|
||||
//!-test
|
||||
|
||||
// The tests below are expected to fail.
|
||||
// See package gopl.io/ch11/word2 for the fix.
|
||||
|
||||
//!+more
|
||||
func TestFrenchPalindrome(t *testing.T) {
|
||||
if !IsPalindrome("été") {
|
||||
t.Error(`IsPalindrome("été") = false`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanalPalindrome(t *testing.T) {
|
||||
input := "A man, a plan, a canal: Panama"
|
||||
if !IsPalindrome(input) {
|
||||
t.Errorf(`IsPalindrome(%q) = false`, input)
|
||||
}
|
||||
}
|
||||
|
||||
//!-more
|
29
vendor/gopl.io/ch11/word2/word.go
generated
vendored
Normal file
29
vendor/gopl.io/ch11/word2/word.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 305.
|
||||
//!+
|
||||
|
||||
// Package word provides utilities for word games.
|
||||
package word
|
||||
|
||||
import "unicode"
|
||||
|
||||
// IsPalindrome reports whether s reads the same forward and backward.
|
||||
// Letter case is ignored, as are non-letters.
|
||||
func IsPalindrome(s string) bool {
|
||||
var letters []rune
|
||||
for _, r := range s {
|
||||
if unicode.IsLetter(r) {
|
||||
letters = append(letters, unicode.ToLower(r))
|
||||
}
|
||||
}
|
||||
for i := range letters {
|
||||
if letters[i] != letters[len(letters)-1-i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//!-
|
148
vendor/gopl.io/ch11/word2/word_test.go
generated
vendored
Normal file
148
vendor/gopl.io/ch11/word2/word_test.go
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
package word
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
//!+bench
|
||||
|
||||
import "testing"
|
||||
|
||||
//!-bench
|
||||
|
||||
//!+test
|
||||
func TestIsPalindrome(t *testing.T) {
|
||||
var tests = []struct {
|
||||
input string
|
||||
want bool
|
||||
}{
|
||||
{"", true},
|
||||
{"a", true},
|
||||
{"aa", true},
|
||||
{"ab", false},
|
||||
{"kayak", true},
|
||||
{"detartrated", true},
|
||||
{"A man, a plan, a canal: Panama", true},
|
||||
{"Evil I did dwell; lewd did I live.", true},
|
||||
{"Able was I ere I saw Elba", true},
|
||||
{"été", true},
|
||||
{"Et se resservir, ivresse reste.", true},
|
||||
{"palindrome", false}, // non-palindrome
|
||||
{"desserts", false}, // semi-palindrome
|
||||
}
|
||||
for _, test := range tests {
|
||||
if got := IsPalindrome(test.input); got != test.want {
|
||||
t.Errorf("IsPalindrome(%q) = %v", test.input, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//!-test
|
||||
|
||||
//!+bench
|
||||
func BenchmarkIsPalindrome(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
IsPalindrome("A man, a plan, a canal: Panama")
|
||||
}
|
||||
}
|
||||
|
||||
//!-bench
|
||||
|
||||
//!+example
|
||||
|
||||
func ExampleIsPalindrome() {
|
||||
fmt.Println(IsPalindrome("A man, a plan, a canal: Panama"))
|
||||
fmt.Println(IsPalindrome("palindrome"))
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
//!-example
|
||||
|
||||
/*
|
||||
//!+random
|
||||
import "math/rand"
|
||||
|
||||
//!-random
|
||||
*/
|
||||
|
||||
//!+random
|
||||
// randomPalindrome returns a palindrome whose length and contents
|
||||
// are derived from the pseudo-random number generator rng.
|
||||
func randomPalindrome(rng *rand.Rand) string {
|
||||
n := rng.Intn(25) // random length up to 24
|
||||
runes := make([]rune, n)
|
||||
for i := 0; i < (n+1)/2; i++ {
|
||||
r := rune(rng.Intn(0x1000)) // random rune up to '\u0999'
|
||||
runes[i] = r
|
||||
runes[n-1-i] = r
|
||||
}
|
||||
return string(runes)
|
||||
}
|
||||
|
||||
func TestRandomPalindromes(t *testing.T) {
|
||||
// Initialize a pseudo-random number generator.
|
||||
seed := time.Now().UTC().UnixNano()
|
||||
t.Logf("Random seed: %d", seed)
|
||||
rng := rand.New(rand.NewSource(seed))
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
p := randomPalindrome(rng)
|
||||
if !IsPalindrome(p) {
|
||||
t.Errorf("IsPalindrome(%q) = false", p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//!-random
|
||||
|
||||
/*
|
||||
// Answer for Exercicse 11.1: Modify randomPalindrome to exercise
|
||||
// IsPalindrome's handling of punctuation and spaces.
|
||||
|
||||
// WARNING: the conversion r -> upper -> lower doesn't preserve
|
||||
// the value of r in some cases, e.g., µ Μ, ſ S, ı I
|
||||
|
||||
// randomPalindrome returns a palindrome whose length and contents
|
||||
// are derived from the pseudo-random number generator rng.
|
||||
func randomNoisyPalindrome(rng *rand.Rand) string {
|
||||
n := rng.Intn(25) // random length up to 24
|
||||
runes := make([]rune, n)
|
||||
for i := 0; i < (n+1)/2; i++ {
|
||||
r := rune(rng.Intn(0x200)) // random rune up to \u99
|
||||
runes[i] = r
|
||||
r1 := r
|
||||
if unicode.IsLetter(r) && unicode.IsLower(r) {
|
||||
r = unicode.ToUpper(r)
|
||||
if unicode.ToLower(r) != r1 {
|
||||
fmt.Printf("cap? %c %c\n", r1, r)
|
||||
}
|
||||
}
|
||||
runes[n-1-i] = r
|
||||
}
|
||||
return "?" + string(runes) + "!"
|
||||
}
|
||||
|
||||
func TestRandomNoisyPalindromes(t *testing.T) {
|
||||
// Initialize a pseudo-random number generator.
|
||||
seed := time.Now().UTC().UnixNano()
|
||||
t.Logf("Random seed: %d", seed)
|
||||
rng := rand.New(rand.NewSource(seed))
|
||||
|
||||
n := 0
|
||||
for i := 0; i < 1000; i++ {
|
||||
p := randomNoisyPalindrome(rng)
|
||||
if !IsPalindrome(p) {
|
||||
t.Errorf("IsNoisyPalindrome(%q) = false", p)
|
||||
n++
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "fail = %d\n", n)
|
||||
}
|
||||
*/
|
Reference in New Issue
Block a user