#477 报任安书

2021-02-12

在网易云音乐中听《软件那些事》聊这篇文章聊了好多期,我也想说一说,不吐不快。
PS: 我有点看不惯那家伙的一些做派,所以就听了一期还是两期,可能《报任安书》还远没有讲完。
PS: 如果抛去上面说的这点,听的士司机(他自己说的)闲聊还是挺有意思。

#476 Golang SHA 函数

2021-02-12

哈希算法(hash)是一种将任意长度数据映射成固定长度数据的方法。有时也叫摘要算法。

有非常多不同的哈希算法,其中最常见的是 md5 和 sha (sha1/sha256/sha512)两种。Golang md5 在 2021/01/14, Go MD5 中已经写过了。这里就记录一下 Golang sha 的使用方法。

// crypto.sha1
func New() hash.Hash
func Sum(data []byte) [Size]byte {
    if boringEnabled {
        return boringSHA1(data)
    }
    var d digest
    d.Reset()
    d.Write(data)
    return d.checkSum()
}
func (d *digest) MarshalBinary() ([]byte, error)
func (d *digest) UnmarshalBinary(b []byte) error
func (d *digest) Reset()
func (d *digest) Size() int
func (d *digest) BlockSize() int
func (d *digest) Write(p []byte) (nn int, err error)
func (d *digest) Sum(in []byte) []byte
func (d *digest) checkSum() [Size]byte
func (d *digest) ConstantTimeSum(in []byte) []byte
func (d *digest) constSum() [Size]byte

// crypto.sha256
func New() hash.Hash
func New224() hash.Hash // sha224
func Sum224(data []byte) [Size224]byte
func Sum256(data []byte) [Size]byte

// crypto.sha512
func New() hash.Hash
func New384() hash.Hash
func New512_224() hash.Hash
func New512_256() hash.Hash
func Sum384(data []byte) [Size384]byte
func Sum512(data []byte) [Size]byte
func Sum512_224(data []byte) [Size224]byte
func Sum512_256(data []byte) [Size256]byte

用法都一样:

h := sha1.New()  // hash.Hash
io.WriteString(h, "His money is twice tainted:")
io.WriteString(h, " 'taint yours and 'taint mine.")
fmt.Printf("% x", h.Sum(nil))
h := sha1.New()
if _, err := io.Copy(h, f); err != nil {
    log.Fatal(err)
}
fmt.Printf("% x", h.Sum(nil))

hash.Hash 接口

type Hash interface {
    io.Writer
    Sum(b []byte) []byte
    Reset()
    Size() int
    BlockSize() int
}

函数

  • sha1.New() -> hash.Hash
  • sha1.Sum(data []byte) -> [Size]byte

  • sha256.New() -> hash.Hash

  • sha256.Sum256(data []byte) -> [Size]byte
  • sha256.New224() -> hash.Hash
  • sha256.Sum224(data []byte) -> [Size224]byte

  • sha512.New() -> hash.Hash

  • sha512.Sum512(data []byte) -> [Size]byte
  • sha512.New384() -> hash.Hash
  • sha512.Sum384(data []byte) -> [Size384]byte
  • sha512.New512_224() -> hash.Hash
  • sha512.Sum512_224(data []byte) -> [Size224]byte
  • sha512.New512_256() -> hash.Hash
  • sha512.Sum512_256(data []byte) -> [Size256]byte
package main

import (
    "crypto/md5"
    "crypto/sha1"
    "crypto/sha256"
    "crypto/sha512"
    "fmt"
    "hash/fnv"
    "io"
    "log"
    "os"
)

func main() {
    filepath := "go.mod"
    f, err := os.Open(filepath)
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()

    fi, err := f.Stat()
    fmt.Printf("%#v err:%#v\n", fi, err)
    data := make([]byte, fi.Size())
    n, err := f.Read(data)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%d, %#v, err:%#v\n", n, data, err)

    {
        hash := fnv.New32a()
        hash.Write(data)
        result := hash.Sum32()
        fmt.Printf("FNV-1a 32-bit hash: %d\n\n", result)
    }

    {
        fmt.Printf("MD5    : %x\n", md5.Sum(data))
        fmt.Printf("Sha1   : %x\n", sha1.Sum(data))
        fmt.Printf("Sha256 : %x\n", sha256.Sum256(data))
        fmt.Printf("Sha512 : %x\n", sha512.Sum512(data))
    }

    {
        f.Seek(0, 0)
        hasher := md5.New()
        _, err = io.Copy(hasher, f)
        if err != nil {
            log.Fatal(err)
        }
        sum := hasher.Sum(nil)
        fmt.Printf("MD5 2  : %x\n", sum)
    }
}

#474 瑞典的福利制度

2021-02-10

认识的人家里有亲人在瑞典生活,之前在那边读书,后来在那边工作,再后来身体不好,病得停严重,无法工作了,只能继续留在那边,依靠那边的福利生活,据说不工作每个月也有不少钱,而且主要是医疗免费。
我听说之后,对那边的福利制度挺好奇,在网上查了一下,还真是 🐮

#473 Golang 基础

2021-02-09

工作这么多年之后,学习一门新的语言,对于语法方面的东西应该是只用看看就行了(重点在其生态的学习)。
所以,这只是对 Go 语法做一个简单的梳理。

#471 Golang container

2021-02-09

堆 Heap (container/heap)

  • 最小堆
  • 完全二叉树
  • 父节点的值小于其子节点的值
type Interface interface {
    sort.Interface
    Push(x any) // add x as element Len()
    Pop() any   // remove and return element Len() - 1.
}

func Fix(h Interface, i int)
func Init(h Interface)
func Pop(h Interface) any
func Push(h Interface, x any)
func Remove(h Interface, i int) any
  1. 接口:
  2. heap.Interface:定义了操作堆的基本方法,包括获取堆中元素数量、比较元素优先级、交换元素位置、添加元素到堆中、从堆中取出元素等。
  3. 函数:
  4. heap.Init(h Interface):对一个实现了 heap.Interface 的堆进行初始化操作。
  5. heap.Push(h Interface, x interface{}):将元素 x 添加到堆 h 中。
  6. heap.Pop(h Interface) interface{}:从堆 h 中取出具有最高优先级的元素并返回。
  7. heap.Remove(h Interface, i int) interface{}:从堆 h 中移除指定索引 i 处的元素并返回。

使用方法:

  1. 定义一个用户自定义的类型,表示要存储在堆中的元素,并为该类型实现 heap.Interface 接口的方法。
  2. 使用 heap.Init() 对堆进行初始化,或者使用 make() 创建一个空的堆。
  3. 使用 heap.Push() 将元素添加到堆中。
  4. 使用 heap.Pop() 从堆中取出具有最高优先级的元素。
  5. 可选地,使用 heap.Remove() 移除堆中的特定元素。

container/heap 包提供了一种方便而高效的方式来管理具有优先级的元素。它在各种场景中都有应用,例如任务调度、事件处理、优先级队列等。通过实现自定义的 heap.Interface 接口方法,可以根据具体需求定义元素的优先级比较方式,使堆适应不同的应用场景。

List (container/list)

type Element
    func (e *Element) Next() *Element
    func (e *Element) Prev() *Element
type List
    func New() *List
    func (l *List) Back() *Element
    func (l *List) Front() *Element
    func (l *List) Init() *List
    func (l *List) InsertAfter(v any, mark *Element) *Element
    func (l *List) InsertBefore(v any, mark *Element) *Element
    func (l *List) Len() int
    func (l *List) MoveAfter(e, mark *Element)
    func (l *List) MoveBefore(e, mark *Element)
    func (l *List) MoveToBack(e *Element)
    func (l *List) MoveToFront(e *Element)
    func (l *List) PushBack(v any) *Element
    func (l *List) PushBackList(other *List)
    func (l *List) PushFront(v any) *Element
    func (l *List) PushFrontList(other *List)
    func (l *List) Remove(e *Element) any

container/list 包是 Go 语言标准库中的一个包,提供了双向链表(doubly linked list)的实现。双向链表是一种数据结构,其中每个节点都包含对前一个节点和后一个节点的引用。

container/list 包提供了 List 类型,该类型代表了一个双向链表。List 类型的对象可以用来存储和操作任意类型的元素。它支持在链表的前部和后部添加和删除元素,以及在链表中搜索、遍历和修改元素。

container/list 包的主要功能和方法包括:

  1. List 类型的方法:
  2. PushFront(v interface{}) *Element:在链表的前部插入一个值为 v 的元素,并返回该元素的指针。
  3. PushBack(v interface{}) *Element:在链表的后部插入一个值为 v 的元素,并返回该元素的指针。
  4. Remove(e *Element) interface{}:从链表中移除指定元素 e,并返回该元素的值。
  5. Front() *Element:返回链表的第一个元素。
  6. Back() *Element:返回链表的最后一个元素。
  7. Len() int:返回链表中元素的数量。
  8. Element 类型的方法:
  9. Next() *Element:返回当前元素的下一个元素。
  10. Prev() *Element:返回当前元素的前一个元素。
  11. Value() interface{}:返回当前元素的值。

使用 container/list 包的步骤通常是:

  1. 导入 container/list 包。
  2. 创建一个新的链表对象:myList := list.New()
  3. 使用 PushFront()PushBack() 方法向链表中添加元素。
  4. 使用 Front()Back() 方法获取链表的第一个或最后一个元素。
  5. 使用 Next()Prev() 方法遍历链表中的元素。
  6. 使用 Remove() 方法从链表中移除特定的元素。

container/list 包提供了一种简单而灵活的方式来管理和操作链表。它适用于需要频繁进行元素插入、删除和遍历的场景。然而,由于链表是一种指针结构,因此在随机访问和索引元素方面的性能较差。如果需要更高效的随机访问和索引操作,可以考虑使用切片(slice)或其他数据结构。

Ring (container/ring)

type Ring
    func New(n int) *Ring
    func (r *Ring) Do(f func(any))
    func (r *Ring) Len() int
    func (r *Ring) Link(s *Ring) *Ring
    func (r *Ring) Move(n int) *Ring
    func (r *Ring) Next() *Ring
    func (r *Ring) Prev() *Ring
    func (r *Ring) Unlink(n int) *Ring

container/ring 包是 Go 语言标准库中的一个包,提供了环形链表(circular list)的实现。环形链表是一种特殊的链表,其中最后一个节点的下一个节点是第一个节点,形成一个环状结构。

container/ring 包提供了 Ring 类型,该类型代表了一个环形链表。Ring 类型的对象可以用来存储和操作任意类型的元素。它支持在环形链表中向前和向后遍历、插入和删除元素,以及计算环形链表的长度。

container/ring 包的主要功能和方法包括:

  1. Ring 类型的方法:
  2. New(n int) *Ring:创建一个具有 n 个节点的新环形链表,并返回指向第一个节点的指针。
  3. Next() *Ring:返回下一个节点的指针。
  4. Prev() *Ring:返回上一个节点的指针。
  5. Move(n int) *Ring:将当前指针向前(负值)或向后(正值)移动 n 步,并返回新的指针。
  6. Link(s *Ring) *Ring:将当前环形链表连接到另一个环形链表 s 的前面,并返回连接后的环形链表。
  7. Unlink(n int) *Ring:从当前环形链表中移除下一个 n 个节点,并返回剩余的环形链表。
  8. Ring 类型的属性:
  9. Len() int:返回环形链表中节点的数量。
  10. Value() interface{}:返回当前节点的值。

使用 container/ring 包的步骤通常是:

  1. 导入 container/ring 包。
  2. 使用 New() 方法创建一个新的环形链表,并指定节点的数量。
  3. 使用 Next()Prev() 方法在环形链表中遍历节点。
  4. 使用 Value() 方法获取当前节点的值。
  5. 使用 Move() 方法向前或向后移动指针。
  6. 使用 Link() 方法将两个环形链表连接起来,或使用 Unlink() 方法移除节点。

container/ring 包提供了一种简单而灵活的方式来处理环形链表。它适用于需要在环形结构中进行元素遍历和操作的场景。可以使用环形链表来实现循环队列、轮转算法等应用。但是,与普通链表相比,环形链表的插入和删除操作相对较慢,因此如果需要频繁进行插入和删除操作,可能有更适合的数据结构选择。

#469 Golang 反射简单示例

2021-02-09

通过反射(Reflection),我们可以在程序运行时,动态的增删该一些信息(类型,方法,变量),用来完成一些特殊的任务。
主要是实现不同类型都支持的一些方法。在 ORM,序列化等方面都有应用。

这种动态获取信息的能力让程序的开发变得更加灵活,但也会损失部分性能,毕竟需要进行很多类型检查、值转换等操作。
而且,滥用的话会导致程序变得复杂,可读性下降。

示例 1

type Person struct {
    Name string
    Age  int
}

func Insert(db *sql.DB, v interface{}) error {
    value := reflect.ValueOf(v)
    typ := reflect.TypeOf(v)

    sql := "INSERT INTO persons ("
    placeholders := "VALUES ("
    for i := 0; i < typ.Elem().NumField(); i++ {
        fieldName := typ.Elem().Field(i).Name
        sql += fmt.Sprintf("%s, ", fieldName)
        placeholders += "?, "
    }
    sql = sql[:len(sql)-2] + ")"
    placeholders = placeholders[:len(placeholders)-2] + ")"

    args := make([]interface{}, typ.Elem().NumField())
    for i := 0; i < typ.Elem().NumField(); i++ {
        args[i] = value.Elem().Field(i).Interface()
    }

    _, err := db.Exec(sql+placeholders, args...)
    return err
}

示例 2

package main

import (
    "fmt"
    "reflect"
)

type Person struct {
    Name    string
    Age     int
    Address string
}

// 定义结构体方法
func (p Person) Hello() string {
    return "Hello, " + p.Name
}

func main() {
    p := Person{
        Name:    "Alice",
        Age:     30,
        Address: "123 Main St",
    }

    fmt.Println("获取变量的反射类型:===============")
    pType := reflect.TypeOf(p)                  // &reflect.rtype
    fmt.Printf("Type: %s, %#v\n", pType, pType) // main.Person
    // fmt.Println("Type:", pType.Elem()) // panic: reflect: Elem of invalid type main.Person
    pType2 := reflect.TypeOf(&p)
    fmt.Printf("Type: %s, %#v\n", pType2, pType2)               // *main.Person
    fmt.Printf("Type: %s, %#v\n", pType2.Elem(), pType2.Elem()) // main.Person (指针类型的基础类型)

    fmt.Println("获取变量的反射值:===============")
    pValue := reflect.ValueOf(p) // main.Person
    fmt.Printf("Value: %s, %#v\n", pValue, pValue)
    pValue2 := reflect.ValueOf(&p)
    fmt.Printf("Value: %s, %#v\n", pValue2, pValue2) // 地址
    fmt.Printf("Value: %s, %#v\n", pValue2.Elem(), pValue2.Elem())

    fmt.Println("遍历结构体字段:===============")
    for i := 0; i < pType.NumField(); i++ {
        field := pType.Field(i)
        value := pValue.Field(i)
        fmt.Printf("%s: %v\n", field.Name, value.Interface())
    }

    fmt.Println("修改结构体字段值:===============")
    ageField1 := pValue.FieldByName("Age")
    fmt.Printf("Value: %#v\n", ageField1)
    // ageField1.SetInt(31) // panic: reflect: reflect.Value.SetInt using unaddressable value
    // ageField1.Elem().SetInt(31) // panic: reflect: call of reflect.Value.Elem on int Value
    ageField2 := pValue2.Elem().FieldByName("Age")
    fmt.Printf("Value: %#v\n", ageField2)
    ageField2.SetInt(31) // 只有指针类型才能通过反射修改
    fmt.Println("New Age:", p.Age)

    fmt.Println("调用结构体方法:===============")
    helloMethod := pValue.MethodByName("Hello")
    result := helloMethod.Call(nil)
    fmt.Println(result)

    // panic: reflect: call of reflect.Value.FieldByName on ptr Value
    pValue2.FieldByName("Age")

    // panic: reflect: reflect.Value.SetInt using unaddressable value
    // a := reflect.Indirect(pValue)
    b := reflect.Indirect(pValue2)
    b.FieldByName("Age").SetInt(33)
    fmt.Printf("%#v\n", b)
}
  • reflect.TypeOf:获取变量的类型
  • reflect.ValueOf:获取变量的值

  • Type.NumField:获取结构体字段数量
  • Type.Field:获取结构体字段信息

  • Value.Field:获取结构体字段的值

  • Value.FieldByName:根据字段名获取结构体字段的值
  • Value.SetInt:设置整数类型字段的值
  • Value.MethodByName:根据方法名获取结构体方法
  • Value.Call:调用结构体方法