對於 Go 中的實用函式我有話說
目標:讓 Go 中支援類似下面這樣的函式
func Max(collection ...interface{}) interface{}
問題:如果用反射實現的話,效率是問題。
解決方案:json.Unmarshal 就是用反射實現的,jsoniter 通過用 unsafe.Pointer 加上快取的 decoder 實現了 6 倍的速度提升。所以嘗試用同樣的技術原理,寫一個概念驗證的原型 https://github.com/v2pro/wombat
實現的 API 類似這樣
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/v2pro/plz"
)
func Test_max_min(t *testing.T) {
should := require.New(t)
should.Equal(3, plz.Max(1, 3, 2))
should.Equal(1, plz.Min(1, 3, 2))
type User struct {
Score int
}
should.Equal(User{3}, plz.Max(
User{1}, User{3}, User{2},
"Score"))
}
其中的原理是從 interface{}
中提取 unsafe.Pointer。 然後用 Accessor 獲得具體的值。這個 Accessor 的概念是和類對應的,而不是和值對應的。也就是相當於 type.GetIntValue(interface{})
這樣的意思。這個在 Java 的反射 API 中是支援的,而 Go 沒有提供這樣的 API。利用 Accessor 我們可以一次性計算好整個任務,然後快取起來。這樣執行期的成本大概就是虛擬函式呼叫的成本。
type Accessor interface {
// === static ===
fmt.GoStringer
Kind() Kind
// map
Key() Accessor
// array/map
Elem() Accessor
// struct
NumField() int
Field(index int) StructField
// array/struct
RandomAccessible() bool
New() (interface{}, Accessor)
// === runtime ===
IsNil(ptr unsafe.Pointer) bool
// variant
VariantElem(ptr unsafe.Pointer) (elem unsafe.Pointer, elemAccessor Accessor)
InitVariant(ptr unsafe.Pointer, template Accessor) (elem unsafe.Pointer, elemAccessor Accessor)
// map
MapIndex(ptr unsafe.Pointer, key unsafe.Pointer) (elem unsafe.Pointer) // only when random accessible
SetMapIndex(ptr unsafe.Pointer, key unsafe.Pointer, elem unsafe.Pointer) // only when random accessible
IterateMap(ptr unsafe.Pointer, cb func(key unsafe.Pointer, elem unsafe.Pointer) bool)
FillMap(ptr unsafe.Pointer, cb func(filler MapFiller))
// array/struct
ArrayIndex(ptr unsafe.Pointer, index int) (elem unsafe.Pointer) // only when random accessible
IterateArray(ptr unsafe.Pointer, cb func(index int, elem unsafe.Pointer) bool)
FillArray(ptr unsafe.Pointer, cb func(filler ArrayFiller))
// primitives
Skip(ptr unsafe.Pointer) // when the value is not needed
String(ptr unsafe.Pointer) string
SetString(ptr unsafe.Pointer, val string)
Bool(ptr unsafe.Pointer) bool
SetBool(ptr unsafe.Pointer, val bool)
Int(ptr unsafe.Pointer) int
SetInt(ptr unsafe.Pointer, val int)
Int8(ptr unsafe.Pointer) int8
SetInt8(ptr unsafe.Pointer, val int8)
Int16(ptr unsafe.Pointer) int16
SetInt16(ptr unsafe.Pointer, val int16)
Int32(ptr unsafe.Pointer) int32
SetInt32(ptr unsafe.Pointer, val int32)
Int64(ptr unsafe.Pointer) int64
SetInt64(ptr unsafe.Pointer, val int64)
Uint(ptr unsafe.Pointer) uint
SetUint(ptr unsafe.Pointer, val uint)
Uint8(ptr unsafe.Pointer) uint8
SetUint8(ptr unsafe.Pointer, val uint8)
Uint16(ptr unsafe.Pointer) uint16
SetUint16(ptr unsafe.Pointer, val uint16)
Uint32(ptr unsafe.Pointer) uint32
SetUint32(ptr unsafe.Pointer, val uint32)
Uint64(ptr unsafe.Pointer) uint64
SetUint64(ptr unsafe.Pointer, val uint64)
Float32(ptr unsafe.Pointer) float32
SetFloat32(ptr unsafe.Pointer, val float32)
Float64(ptr unsafe.Pointer) float64
SetFloat64(ptr unsafe.Pointer, val float64)
}
利用這個 Accessor 可以幹很多事情,除了各種函數語言程式設計常用的 utility(map/filter/sorted/...)之外。還可以實現一個 plz.Copy
的函式
func Copy(dst, src interface{}) error
Copy 可以用於各種物件繫結的場景
- Go 不同型別物件之間的值拷貝(struct&map 互相轉換,相容指標)
- JSON 編解碼
- 拷貝 http.Request 到我的 struct 上
- 拷貝 sql rows 到我的 struct 上
- Mysql/thrift/redis 等其他協議的編解碼
還可以用來實現 plz.Validate
的函式
func Validate(obj interface{}) error
甚至有可能的話,還可以把 .net 的 linq 的概念拿過來
func Query(obj interface{}, query string) (result interface{}, err error)
當然這個工作量非常浩大,比一個 JSON 解析庫繁瑣得多。現在只實現了幾個概念原型:
用興趣的朋友可以來發 issue:https://github.com/v2pro/wombat/issues
更多原創文章乾貨分享,請關注公眾號
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- 關於輕應用,我有話要說...
- 對於你們駁來駁去的《停止學習框架》,我有話說!框架
- 對話方塊函式函式
- 我最想對七年級的我說的幾句話
- 對於Python中回撥函式的理解Python函式
- 如何將AI應用於晶片設計?谷歌有話說!AI晶片谷歌
- 針對Sybase資料庫無法啟動的情況,我有話要說資料庫
- 對話式 AI 應用的降本增效實踐AI
- 基於ChatGPT用AI實現自然對話ChatGPTAI
- [譯] 解析 Go 中的函式呼叫Go函式
- Python基礎之白話說函式Python函式
- 面試官:來說一說Go語言的函式呼叫慣例面試Go函式
- Go channel 實現歸併排序中的 merge 函式Go排序函式
- 同事有話說 | 那些所謂的敏捷儀式感敏捷
- 排序(對於 sort 函式的使用)排序函式
- 留下你最想說的話,我來用ai回覆你AI
- 說說 Vue.js 中的 functional 函式化元件Vue.jsFunction函式元件
- 關於openssl應用的對話 (轉)
- 說說我對 WSGI 的理解
- 我對C++中虛擬函式、純虛擬函式在實現多型中作用的一點淺薄認識 (轉)C++函式多型
- Go實現PHP常用函式GoPHP函式
- 我的HTML會說話——從實用出發,談談HTML的語義化HTML
- Go plan9 彙編:說透函式棧Go函式
- 介紹PHP中的10個實用函式PHP函式
- 對JavaScript中函式物件的理解JavaScript函式物件
- MySQL 日期函式、時間函式在實際場景中的應用MySql函式
- 說說函式索引函式索引
- 說說在 Python 中如何測試函式Python函式
- 我可是遊戲主角,為啥連說話的權利都沒有?!遊戲
- 說說對 Node 中的 fs 模組的理解? 有哪些常用方法?
- 使用Kotlin語言兩年後,我有話要說Kotlin
- 我想對所有新程式設計師說的一些話程式設計師
- Oracle中關於函式的使用Oracle函式
- 關於qt中的tr()函式QT函式
- 對程式設計師說點實在話程式設計師
- 有獎徵文:2009系統架構師大會,我有圖發,我有話說!架構
- 對於靜態成員來說是類的建構函式,對於例項成員是類的原型物件。函式原型物件
- 深入理解 Go 中的 new() 和 make() 函式Go函式