第七章 示例說明(重要)

前面是理論部分,這里是實(shí)踐

示例:表達(dá)式求值

實(shí)現(xiàn)簡單算數(shù)表達(dá)式的求值器

package seven 

import (
    "fmt"
    "math"
    "testing"
    "strings"
)

type Expr interface {
    // 根據(jù)給定的環(huán)境變量返回表達(dá)式的值
    Eval(env Env) float64
    Check(vars map[Var]bool) error
}
type Var string
type literal float64

type unary struct {
    op   rune
    x, y Expr
}
type binary struct {
    op   rune
    x, y Expr
}
type call struct {
    fn   string
    args []Expr
}

type Env map[Var]float64

func (v Var) Eval(env Env) float64 {
    return env[v]
}

func (l literal) Eval(_ Env) float64 {
    return float64(l)
}

func (u unary) Eval(env Env) float64 {
    switch u.op {
    case '+':
        return +u.x.Eval(env)
    case '-':
        return -u.x.Eval(env)
    }
    panic(fmt.Sprintf("unsupported unary operator: %q", u.op))
}

func (b binary) Eval(env Env) float64 {
    switch b.op {
    case '+':
        return b.y.Eval(env) + b.x.Eval(env)
    case '-':
        return b.y.Eval(env) - b.x.Eval(env)
    case '*':
        return b.y.Eval(env) * b.x.Eval(env)
    case '/':
        return b.y.Eval(env) / b.x.Eval(env)
    }
    panic(fmt.Sprintf("unsupported unary operator: %q", b.op))
}

func (c call) Eval(env Env) float64 {
    switch c.fn {
    case "pow":
        return math.Pow(c.args[0].Eval(env), c.args[1].Eval(env))
    case "sin":
        return math.Sin(c.args[0].Eval(env))
    case "sqrt":
        return math.Sqrt(c.args[0].Eval(env))
    }
    panic(fmt.Sprintf("unsupported unary operator: %s", c.fn))
}

func (v Var) Check(vars map[Var]bool) error {
    vars[v] = true
    return nil
}

func (literal) Check(vars map[Var]bool) error {
    return nil
}
func (u unary) Check(vars map[Var]bool) error {
    if !strings.ContainsRune("+-", u.op) {
        return fmt.Errorf("unexpect unary op %q", u.op)
    }
    return u.x.Check(vars)
}

func (b binary) Check(vars map[Var]bool) error {
    if !strings.ContainsRune("+-*/", b.op) {
        return fmt.Errorf("unexpect binary op %q", b.op)
    }
    if err := b.x.Check(vars); err != nil {
        return err
    }
    return b.y.Check(vars)
}

var numParams = map[string]int{"pow": 2, "sin": 1, "sqrt": 1}

func (c call) Check(vars map[Var]bool) error {
    arity, ok := numParams[c.fn]
    if !ok {
        return fmt.Errorf("unknow function %q", c.fn)
    }
    if len(c.args) != arity {
        return fmt.Errorf("call to %s has %d args, want %d", c.fn, len(c.args), arity)
    }

    for _, arg := range c.args {
        if err := arg.Check(vars); err != nil {
            return err
        }
    }
    return nil
}

//測試函數(shù)
func TestEval(t *testing.T) {
    tests := []struct {
        expr string
        env  Env
        want string
    }{
        {"sqrt(A/pi)", Env{"A": 87616, "pi": math.Pi}, "167"},
        {"pow(x,3)+pow(y,3)", Env{"x": 12, "y": 1}, "1729"},
        {"pow(x,3)+pow(y,3)", Env{"x": 9, "y": 10}, "1729"},
        {"5/9*(F-32)", Env{"F": -40}, "-40"},
        {"5/9*(F-32)", Env{"F": -40}, "0"},
        {"5/9*(F-32)", Env{"F": -40}, "100"},
    }
    var prevExpr string
    for _, test := range tests {
        if test.expr != prevExpr {
            fmt.Print("\n%s\n", test.expr)
            prevExpr = test.expr
        }
        expr, err := Parse(test.expr)
        if err != nil {
            t.Error(err)
            continue
        }
        got := fmt.Sprintf("%.6g", expr.Eval(test.env))
        fmt.Printf("\t%v => %s\n", test.env, got)
        if got != test.want {
            t.Errorf("%s.Eval() in %v =%q ,want %q\n", test.expr, test.env, got, test.want)

        }
    }
}



類型斷言

類型斷言是一個(gè)使用在接口值上的操作。語法上它看起來像x.(T)被稱為斷言類型,這里x表示一個(gè)接口的類型和T表示一個(gè)類型,一個(gè)類型斷言檢查它操作對象的動(dòng)態(tài)類型是否和斷言的類型匹配。

var  w io.Writer
w = os.Stdout
f :=w.(*os.File) //success 檢查成功
c :=w.(*bytes.Buffer) //panic 異常  
cs,ok :=w.(*bytes.Buffer) //正常執(zhí)行,OK返回false

//常用態(tài)
if f,ok :=w.(*os.File); ok{
  //具體事項(xiàng)
}

基于類型斷言區(qū)別錯(cuò)誤

引言:思考在os包中文件操作返回的錯(cuò)誤集合。I/O可以因?yàn)槿魏螖?shù)量的原因失敗,但是有三種經(jīng)常的錯(cuò)誤必須進(jìn)行不同的處理:文件已經(jīng)存在(針對創(chuàng)建操作),找不到文件(針對讀取操作),權(quán)限拒絕。os包中提供了 三個(gè)幫助函數(shù)

package os
func IsExits(err error) bool
func IsNotExits(err error) bool
func IsPermission(err error) bool

os包中定義了一個(gè)PathError 類型來描述在文件路徑操作中涉及到的失敗并且定義了一個(gè)LinkError的變量來描述設(shè)計(jì)到兩個(gè)文件路徑的操作,像Symlink和Rename.
PathError的實(shí)體內(nèi)容:

package os 
type PathError struct{
  Op string
  Path string
  Err error
}
func (e *PathError) Error() string{
  return e.Op+" "+e.Path+" " +e.Err.Error()
}

大多數(shù)調(diào)用方都不知道PathError并且通過調(diào)用錯(cuò)誤本身的Error方法來統(tǒng)一處理所有的錯(cuò)誤。盡管PathError的Error方法簡單地把這些字段連起來生成錯(cuò)誤信息,一般的調(diào)用:

_,err :=os.Open("/dir")
fmt.Println(err)
fmt.Printf("%#v\n",err)
fmt.Println(os.IsNotExits(err))//true

基于類型斷言詢問行為

package fmt
func formatOneValue(x interface{}) string{
  if err,ok:=x.(error);ok{//判斷是否有錯(cuò)誤生成
    return err.Error()
  }

  if str,ok :=x.(Stringer);ok{//判斷是否有正確信息生成。用第二個(gè)返回值OK來進(jìn)行基礎(chǔ)判斷
    return str.String()
  }

}


類型開關(guān)

引言:switch 語句可以簡化ifelse type switch (類型開關(guān)) 可以簡化類型斷言的 if-else鏈
使用關(guān)鍵字面量type來簡化類型判斷

switch x.(type){
    case nil:
   case int,uint:
   case bool:
   case string:
  default:
}

示例:基于標(biāo)記的XML解碼

應(yīng)用包為:encoding/xml

作者的建議

當(dāng)設(shè)計(jì)一個(gè)新的包時(shí),新的程序員先創(chuàng)建接口的集合,然后再定義符合他們的具體類型。這種方式的結(jié)果就是有很多的接口,他們中每一個(gè)僅有一個(gè)實(shí)現(xiàn)類型,這樣是不必要的。這種接口是平白添加了一層封裝而已。平臺加了一層運(yùn)行時(shí)消耗。你可以使用導(dǎo)出機(jī)制來限制一個(gè)類型的方法和一個(gè)結(jié)構(gòu)體的字段是否為包外可見,接口只有當(dāng)有兩個(gè)或者兩個(gè)以上的具體類型必須以相同的方式進(jìn)行處理的時(shí)候才需要。
當(dāng)一個(gè)接口只被單一的具體類型實(shí)現(xiàn)的時(shí)候有一種是需要封裝接口的。就是由于它的依賴,這個(gè)具體的類型不能和這個(gè)接口存在一個(gè)相同的包中,這種情況下一個(gè)接口是解耦這兩個(gè)包的一個(gè)很好的方式。
因?yàn)間o中只有當(dāng)兩個(gè)或者以上的類型實(shí)現(xiàn)一個(gè)接口才使用接口,它們必定會從任意特定的實(shí)現(xiàn)細(xì)節(jié)中抽象出來。結(jié)果就是有更少和更簡單方法的更小的接口。當(dāng)新的類型出現(xiàn)時(shí),小的接口更容易滿足。對于接口設(shè)計(jì)的一個(gè)好的標(biāo)準(zhǔn)就是只考慮你需要的東西。
我們完成了對methods和接口的學(xué)習(xí)過程,go良好的支持面向?qū)ο箫L(fēng)格的編程,但這不是說你僅僅只能使用它,不是任何事物都需要被當(dāng)成一個(gè)對象;獨(dú)立的函數(shù)有他們自己的用處,未封裝的數(shù)據(jù)類型也是一樣。一定要眼光放開,不要局限。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、通過CocoaPods安裝項(xiàng)目名稱項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請求組件 FMDB本地?cái)?shù)據(jù)庫組件 SD...
    陽明AI閱讀 16,210評論 3 119
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,590評論 19 139
  • 1
    SupermanTomato閱讀 203評論 0 0
  • 知識不是力量,重復(fù)才是力量 念丈夫好:每天固定上班時(shí)間,固定的事情,樂此不疲,對待顧客總能開開心心的,并能和老顧客...
    廖小蘭閱讀 266評論 0 0

友情鏈接更多精彩內(nèi)容