Go語言實現(xiàn)運算符重載的方法詳解
前言
先帶來日常的 GScript 更新:新增了可變參數(shù)的特性,語法如下:
int add(string s, int ...num){ println(s); int sum = 0; for(int i=0;i<len(num);i++){ int v = num[i]; sum = sum+v; } return sum; } int x = add("abc", 1,2,3,4); println(x); assertEqual(x, 10);
得益于可變參數(shù),所以新增了格式化字符串的內(nèi)置函數(shù):
//formats according to a format specifier and writes to standard output. printf(string format, any ...a){} //formats according to a format specifier and returns the resulting string. string sprintf(string format, any ...a){}
下面重點看看 GScript 所支持的運算符重載是如何實現(xiàn)的。
使用
運算符重載其實也是多態(tài)的一種表現(xiàn)形式,我們可以重寫運算符的重載函數(shù),從而改變他們的計算規(guī)則。
println(100+2*2);
以這段代碼的運算符為例,輸出的結(jié)果自然是:104.
但如果我們是對兩個對象進(jìn)行計算呢,舉個例子:
class Person{ int age; Person(int a){ age = a; } } Person p1 = Person(10); Person p2 = Person(20); Person p3 = p1+p2;
這樣的寫法在 Java/Go
中都會報編譯錯誤,這是因為他們兩者都不支持運算符重載;
但 Python/C#
是支持的,相比之下我覺得 C#
的實現(xiàn)方式更符合 GScript
語法,所以參考 C# 實現(xiàn)了以下的語法規(guī)則。
Person operator + (Person p1, Person p2){ Person pp = Person(p1.age+p2.age); return pp; } Person p3 = p1+p2; println("p3.age="+p3.age); assertEqual(p3.age, 30);
有幾個硬性條件:
operator
目前支持的運算符有:+-*/ == != < <= > >=
實現(xiàn)
以前在使用 Python
運算符重載時就有想過它是如何實現(xiàn)的?但沒有深究,這次借著自己實現(xiàn)相關(guān)功能從而需要深入理解。
其中重點就為兩步:
- 編譯期間:記錄所有的重載函數(shù)和運算符的關(guān)系。
- 運行期:根據(jù)當(dāng)前的運算找到聲明的函數(shù),直接運行即可。
第一步的重點是掃描所有的重載函數(shù),將重載函數(shù)與運算符存放起來,需要關(guān)注的是函數(shù)的返回值與運算符類型。
// OpOverload 重載符 type OpOverload struct { function *Func tokenType int } // 運算符重載自定義函數(shù) opOverloads []*symbol.OpOverload
在編譯器中使用一個切片存放。
而在運行期中當(dāng)兩個入?yún)㈩愋拖嗤瑫r,則需要查找重載函數(shù)。
// GetOpFunction 獲取運算符重載函數(shù) // 通過返回值以及運算符號(+-*/) 匹配重載函數(shù) func (a *AnnotatedTree) GetOpFunction(returnType symbol.Type, tokenType int) *symbol.Func { for _, overload := range a.opOverloads { isType := overload.GetFunc().GetReturnType().IsType(returnType) if isType && overload.GetTokenType() == tokenType { return overload.GetFunc() } } return nil }
查找方式就是通過編譯期存放的數(shù)據(jù)進(jìn)行匹配,拿到重載函數(shù)后自動調(diào)用便實現(xiàn)了重載。
感興趣的朋友可以查看相關(guān)代碼:
總結(jié)
運算符重載其實并不是一個常用的功能;因為會改變運算符的語義,比如明明是加法卻在重載函數(shù)中寫為減法。
這會使得代碼閱讀起來困難,但在某些情況下我們又非常希望語言本身能支持運算符重載。
比如在 Go 中常用的一個第三方精度庫 decimal.Decimal
,進(jìn)行運算時只能使用 d1.Add(d2)
這樣的函數(shù),當(dāng)運算復(fù)雜時:
a5 = (a1.Add(a2).Add(a3)).Mul(a4);
a5 = (a1+a2+a3)*a4;
就不如下面這種直觀,所以有利有弊吧,多一個選項總不是壞事。
GScript 源碼: https://github.com/crossoverJie/gscript
到此這篇關(guān)于Go語言實現(xiàn)運算符重載的方法詳解的文章就介紹到這了,更多相關(guān)Go語言運算符重載內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go?內(nèi)聯(lián)優(yōu)化讓程序員愛不釋手
這篇文章主要介紹了Go?內(nèi)聯(lián)優(yōu)化讓程序員愛不釋手,內(nèi)聯(lián)是在編譯過程中自動進(jìn)行的一類基本優(yōu)化之一,文章圍繞主題展開更多詳細(xì)介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-06-06