写在前面
AI时代的技术发展, 确实对个人知识库起到了非常大的帮助, 列举2个非常重要的点
- LLM能够快速学习 -> 个人学习受到输入/理解/记忆等方面的限制, 通常需要
漫长
的学习过程, LLM 却能做到快+上限不断提高
- 基于 LLM 的知识库, 比个人自己不断搜集整理的知识库,
简单+轻松
, 比如我自己的知识库,建立索引+维护索引
的难度不断提高, 维护的成本越来越大, 而 LLM 却相反, 反而是数据越多越智能
Go代码片段
hello
快速跑 go 代码:
main.go
->test
func main() {
fmt.Println(max(1, 2))
}
AI 提供的内容
上下文
更清晰, 自己知识库的内容反而需要再次查找下才能真正use it
func main() {
// go tool trace t.out
f, _ := os.Create("t.out")
_ = trace.Start(f)
defer trace.Stop()
ch := make(chan string)
go func() {
ch <- "this is a test"
}()
<-ch
}
- 保留了一些 gin 使用 demo, 最后只留下了关于 req 定义的内容, 这个属于
内部 API 规范
逐渐形成的- 关于 req 定义, 一个常见的错误:
tag 漏写/写错
package types
type Req struct {
// GET 分页查询
Page int `form:"page,default=1" binding:"gte=1"`
PageSize int `form:"page_size,default=10" binding:"gte=1,lte=100"`
// 导出
IsExport bool `form:"is_export"`
IsMock bool `form:"is_mock"` // mock数据, 只测试阶段使用
}
type XxxListRow struct{}
// XxxListResp 分页查询
type XxxListResp struct {
Rows []*XxxListRow `json:"rows"`
Cnt int64 `json:"cnt"`
}
type BaseResp struct {
Code int64 `json:"code"` // 0: success, 1: error
Msg string `json:"msg"` // error message, 0-ok, 1-error
Data any `json:"data"` // response data
}
// https://www.cloudwego.io/docs/hertz/tutorials/basic-feature/binding-and-validate/
var req struct {
User string `form:"user" json:"user" query:"user" vd:"(len($) > 0 && len($) < 128); msg:'Illegal format'"`
Password string `form:"password" json:"password" query:"password" vd:"(len($) > 0 && len($) < 128); msg:'Illegal format'"`
}
搜索结果和自己知识库的记录完全匹配 Go语言编程技巧
// 摘录部分
// Tip #51 避免使用init()
func init() {
fmt.Println("package basic init") // Tip #6 下划线导入
}
// Status Tip #53:枚举从1开始用于分类,从0用于默认情况
type Status int
const (
StatusNone Status = iota
StatusOk
)
// TrackTime Tip #1 一行代码测量函数的执行时间
func TrackTime(start time.Time) time.Duration {
elapsed := time.Since(start)
fmt.Println("elapsed: ", elapsed)
return elapsed
}
// MultistageDefer Tip #2 多阶段 defer -> 在另一个函数的开头和结尾处执行一个函数
func MultistageDefer() func() {
fmt.Println("run init")
return func() {
fmt.Println("run cleanup")
}
}
- dsa: datastruct & algorithm - go 中用来刷 leetcode 的 github 代码仓库推荐: https://kimi.moonshot.cn/share/ct2ohg3lvj5ibhh8dkv0
- 自己刷题积累了一些代码片段和模板, 使用 AI 获取的优质内容完全可以吊打自己写的这些
- 一点碎碎念: 大厂刷题
完全是浪费时间
, 刷题确实可以让你保持 coding 的手感, 但是成就感完全没法和拿得出手的开源项目比
// arr.go
package template
import (
"container/ring"
"fmt"
)
func arr() {
// arr array: fixed-length; Using pointer
a1 := [...]int{1, 2, 3}
a2 := [3]int{1, 2, 3}
fmt.Println(a1, a2)
// fmt.Printf("%x %x %t %T\n", arr1, arr2, arr1 == arr2, arr1) // %x array/slice %t bool %T type
// a slice: pointer(数据会发生共享!)+len()+cap(); not comparable; nil
// make([]int, 0, 3) // make([]int, 3) 会导致前 3 个已赋值, append() 从第 4 个开始
// https://github.com/golang/go/wiki/SliceTricks
// https://ueokande.github.io/go-slice-tricks/
a := []int{0, 1, 2} // a := make([]int, 3) 预先分配, 使用 a[i]
if len(a) == 0 { // nil时len==0
fmt.Println("empty")
}
a[2] = 4
// var a []int // a=nil; a = []int{} // a!=nil
// make([]T, len, cap) = make([]T, cap)[:len]
// append() cap扩容
a = append(a[:10], a[10+1:]...) // 删除第i个值
a = append([]int(nil), a...) // copy
var x int
a = append(a, x) // push
x, a = a[len(a)-1], a[:len(a)-1] // stack
x, a = a[0], a[1:] // queue
// tail = (tail+1)%N // 环形队列.固定长度缓冲区读写
ring.New(5)
// 矩阵
r, c := 3, 4
mat := make([][]int, r)
for i := 0; i < r; i++ {
mat[i] = make([]int, c)
}
var cnt1, cnt2 [26]int
fmt.Println(cnt1 == cnt2) // true
}
// arrFlip 数组翻转
func arrFlip[T any](a []T) {
// n := len(a)
// for i := 0; i < n/2; i++ {
// a[i], a[n-1-i] = a[n-1-i], a[i]
// }
l, r := 0, len(a)-1
for l < r {
a[l], a[r] = a[r], a[l]
l, r = l+1, r-1
}
}
func itoa(n int) (b []byte) {
for ; n > 0; n /= 10 {
b = append(b, byte(n%10+'0'))
}
arrFlip(b)
return b
}
- go 中使用并发的代码仓库推荐: https://kimi.moonshot.cn/share/ct2oo25stq1ov51o1jng
我在面试中经常会问的一个问题: 使用过哪些 go 并发编程原语 or 模式, 因为这是 go 编程中最容易犯错的内容
// or-done pattern by recursive
func or(channels ...<-chan any) <-chan any {
// 特殊情况,只有零个或者1个chan
switch len(channels) {
case 0:
return nil
case 1:
return channels[0]
}
orDone := make(chan any)
go func() {
defer close(orDone)
switch len(channels) {
case 2: // 2个也是一种特殊情况
select {
case <-channels[0]:
case <-channels[1]:
}
default: // 超过两个,二分法递归处理
m := len(channels) / 2
select {
case <-or(channels[:m]...):
case <-or(channels[m:]...):
}
}
}()
return orDone
}
// sync 包
// sync包的同步原语在使用后是不能复制的: vet.noCopy
var mu sync.Mutex // 零值不需要额外初始化
type Counter struct { // 传参是必须使用指针, 否则会导致mu复制
mu sync.Mutex // 1.放在要控制的字段上面并空行 2.内嵌字段 3.不可重入
cnt uint64
}
// 1.封装成方法 2.读写都需要
func (c *Counter) Incr() {
c.mu.Lock()
c.cnt++
c.mu.Unlock() // ‼️ 可以被任意协程调用 -> 谁申请谁释放
}
func (c *Counter) Cnt() uint64 {
c.mu.Lock()
defer c.mu.Unlock() // ✅ 推荐
return c.cnt
}
func (c *Counter) Cnt2() uint64 {
v := atomic.LoadInt32((*int32)(unsafe.Pointer(&c.mu))) // 使用 unsafe 获取未暴露的字段: mutex.state
return uint64(v)
}
- go 中使用设计模式的代码仓库推荐: https://kimi.moonshot.cn/share/ct2oq6234pe12t133g40
非 OOP 其实对设计模式一词相当无感, 其实设计模式更多是一些
约定俗成
和习以为常
, 比如 conf 中使用WithOptionXxx
, 任何读过 go 项目源码的都不陌生
// option.go
package dp
var defaultStuffClient = stuffClient{
retries: 3,
timeout: 2,
}
type StuffClientOption func(*stuffClient)
func WithRetries(r int) StuffClientOption {
return func(o *stuffClient) {
o.retries = r
}
}
func WithTimeout(t int) StuffClientOption {
return func(o *stuffClient) {
o.timeout = t
}
}
type StuffClient interface {
DoStuff() error
}
type stuffClient struct {
conn Connection
timeout int
retries int
}
type Connection struct{}
func NewStuffClient(conn Connection, opts ...StuffClientOption) StuffClient {
client := defaultStuffClient
for _, o := range opts {
o(&client)
}
client.conn = conn
return client
}
func (c stuffClient) DoStuff() error {
return nil
}
写在最后
写到 blog 中内容, 不到知识库中清理掉的 go 代码片段的 1/1000, 基本上平时不回顾知识库, 想要找到需要的代码片段的成本, 和从 0 开始搜索成本都差不多, 而且更麻烦的点在于, 知识库积累得越多, 维护索引成本越高, 使用成本也越高, 可以说是一件没法持续的事, 幸好 AI 提供了一条新路径, 上面也列举了很多从 0 开始搜索的例子, 无论是速度还是效果, 都和自己维护到知识库比起来要胜出不少, 甚至数量级的差异