04 Oct 2017
##
rot13 算法将每个字母按如下图所示的规则替换成另外的字母
有种常见的模式是一个 io.Reader 包装另一个 io.Reader ,然后通过某种方式修改其数据流。
rot13Reader 包装了 io.Reader, 然后从中读入数据流,通过 rot13 算法将数据流加密。
代码实现
package main
import (
"io"
"os"
"strings"
)
func rot13(b byte) byte {
var a, z byte
switch {
case 'a' <= b && b <= 'z':
a, z = 'a', 'z'
case 'A' <= b && b <= 'Z':
a, z = 'A', 'Z'
default:
return b
}
return (b-a+13)%(z-a+1) + a
}
type rot13Reader struct {
r io.Reader
}
func (r rot13Reader) Read(p []byte) (n int, err error) {
n, err = r.r.Read(p)
for i := 0; i < n; i++ {
p[i] = rot13(p[i])
}
return
}
func main() {
s := strings.NewReader(
"Lbh penpxrq gur pbqr!")
r := rot13Reader{s}
io.Copy(os.Stdout, &r)
}
运行结果
用tr命令实现 rot13 加密
echo "Lbh penpxrq gur pbqr" | tr 'A-Za-z' 'N-ZA-Mn-za-m'
03 Oct 2017
算法原理
牛顿法是通过选择一个起点 z 然后重复以下过程来求 Sqrt(x) 的近似值:
迭代一定次数之后,z 的值就是 Sqrt(x) 的近似值
详细算法原理可以看:如何通俗易懂地讲解牛顿迭代法
Go 语言实现
第一种实现,迭代5次后停止循环
package main
import (
"fmt"
"math"
)
func Sqrt(x float64) float64 {
z := 1.0
for i := 0; i < 5; i++ {
z -= (z*z - x) / (2 * z)
}
return z
}
func main() {
n := 2.0
fmt.Println(Sqrt(n))
fmt.Println(math.Sqrt(n))
}
迭代5次的运行结果
1.4142135623730951
1.4142135623730951
迭代到前后值相差为 1e-6 时,停止迭代
package main
import (
"fmt"
"math"
)
const delta = 1e-6
func Sqrt(x float64) float64 {
z := x
n := 0.0
for math.Abs(n-z) > delta {
n, z = z, z-(z*z-x)/(2*z)
}
return z
}
func main() {
const x = 2
mine, theirs := Sqrt(x), math.Sqrt(x)
fmt.Println(mine, theirs, mine-theirs)
}
03 Oct 2017
##
创建一个新的类型 ErrNegativeSqrt , 它实现 Error 方法
注意: 在 Error 方法内调用 fmt.Sprint(e) 会让程序陷入死循环。可以通过先转换 e 来避免这个问题:fmt.Sprint(float64(e))
代码实现
package main
import (
"fmt"
)
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
return fmt.Sprintf("cannot Sqrt negative number: %f", float64(e))
}
func Sqrt(x float64) (float64, error) {
if x < 0 {
return 0, ErrNegativeSqrt(x)
}
z := 1.0
for i := 0; i < 5; i++ {
z -= (z*z - x) / (2 * z)
}
return z, nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
运行结果
1.4142135623730951 <nil>
0 cannot Sqrt negative number: -2.000000
03 Oct 2017
介绍
io 包指定了 io.Reader 接口, 它表示从数据流的末尾进行读取。
Go 标准库包含了该接口的许多实现, 包括文件、网络连接、压缩和加密等等。
io.Reader 接口有一个 Read 方法:
func (T) Read(b []byte) (n int, err error)`
T 类型的方法 Read 用类型 T 的内部数据填充给定的字节切片 b 并返回填充的字节数和错误值。 在遇到数据流的结尾时,它会返回一个 io.EOF 错误。
示例代码
以下示例代码创建了一个 strings.Reader 并以每次 8 字节的速度读取它的输出。
package main
import (
"fmt"
"io"
"strings"
)
func main() {
r := strings.NewReader("Hello, Reader!")
b := make([]byte, 8)
for {
n, err := r.Read(b)
fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
fmt.Printf("b[:n] = %q\n", b[:n])
if err == io.EOF {
break
}
}
}
运行结果
n = 8 err = <nil> b = [72 101 108 108 111 44 32 82]
b[:n] = "Hello, R"
n = 6 err = <nil> b = [101 97 100 101 114 33 32 82]
b[:n] = "eader!"
n = 0 err = EOF b = [101 97 100 101 114 33 32 82]
b[:n] = ""
03 Oct 2017
接口 Stringer
fmt 包中定义的 Stringer 是最普遍的接口之一。
type Stringer interface {
String() string
}
Stringer 是一个可以用字符串描述自己的类型。fmt 包(还有很多包)都通过此接口来打印值。
通过让 IPAddr 类型实现 fmt.Stringer 来打印点号分隔的地址。
例如,IPAddr{1, 2, 3, 4} 应当打印为 “1.2.3.4” 。
实现
package main
import "fmt"
type IPAddr [4]byte
func (i IPAddr) String() string {
return fmt.Sprintf("%d.%d.%d.%d",i[0],i[1],i[2],i[3])
}
func main() {
hosts := map[string]IPAddr{
"loopback": {127, 0, 0, 1},
"googleDNS": {8, 8, 8, 8},
}
for name, ip := range hosts {
fmt.Printf("%v: %v\n", name, ip)
}
}
运行结果
loopback: 127.0.0.1
googleDNS: 8.8.8.8