This commit is contained in:
chai2010
2016-01-18 10:52:21 +08:00
parent edd55ba1f3
commit a91355f5f1
4 changed files with 210 additions and 198 deletions

View File

@@ -18,7 +18,7 @@ pow(x, 3) + pow(y, 3)
下面的五個具體類型表示了具體的表達式類型。Var類型表示對一個變量的引用。我們很快會知道爲什麽它可以被輸出。literal類型表示一個浮點型常量。unary和binary類型表示有一到兩個運算對象的運算符表達式這些操作數可以是任意的Expr類型。call類型表示對一個函數的調用我們限製它的fn字段隻能是powsin或者sqrt。
```go
// gopl.io/ch7/eval
gopl.io/ch7/eval
// A Var identifies a variable, e.g., x.
type Var string
@@ -27,20 +27,20 @@ type literal float64
// A unary represents a unary operator expression, e.g., -x.
type unary struct {
op rune // one of '+', '-'
x Expr
op rune // one of '+', '-'
x Expr
}
// A binary represents a binary operator expression, e.g., x+y.
type binary struct {
op rune // one of '+', '-', '*', '/'
x, y Expr
op rune // one of '+', '-', '*', '/'
x, y Expr
}
// A call represents a function call expression, e.g., sin(x).
type call struct {
fn string // one of "pow", "sin", "sqrt"
args []Expr
fn string // one of "pow", "sin", "sqrt"
args []Expr
}
```
@@ -54,8 +54,8 @@ type Env map[Var]float64
```go
type Expr interface {
// Eval returns the value of this Expr in the environment env.
Eval(env Env) float64
// Eval returns the value of this Expr in the environment env.
Eval(env Env) float64
}
```
@@ -63,11 +63,11 @@ type Expr interface {
```go
func (v Var) Eval(env Env) float64 {
return env[v]
return env[v]
}
func (l literal) Eval(_ Env) float64 {
return float64(l)
return float64(l)
}
```
@@ -75,39 +75,39 @@ unary和binary的Eval方法會遞歸的計算它的運算對象然後將運
```go
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))
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.x.Eval(env) + b.y.Eval(env)
case '-':
return b.x.Eval(env) - b.y.Eval(env)
case '*':
return b.x.Eval(env) * b.y.Eval(env)
case '/':
return b.x.Eval(env) / b.y.Eval(env)
}
panic(fmt.Sprintf("unsupported binary operator: %q", b.op))
switch b.op {
case '+':
return b.x.Eval(env) + b.y.Eval(env)
case '-':
return b.x.Eval(env) - b.y.Eval(env)
case '*':
return b.x.Eval(env) * b.y.Eval(env)
case '/':
return b.x.Eval(env) / b.y.Eval(env)
}
panic(fmt.Sprintf("unsupported binary 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 function call: %s", c.fn))
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 function call: %s", c.fn))
}
```
@@ -117,37 +117,37 @@ func (c call) Eval(env Env) float64 {
```go
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": 32}, "0"},
{"5 / 9 * (F - 32)", Env{"F": 212}, "100"},
}
var prevExpr string
for _, test := range tests {
// Print expr only when it changes.
if test.expr != prevExpr {
fmt.Printf("\n%s\n", test.expr)
prevExpr = test.expr
}
expr, err := Parse(test.expr)
if err != nil {
t.Error(err) // parse error
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)
}
}
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": 32}, "0"},
{"5 / 9 * (F - 32)", Env{"F": 212}, "100"},
}
var prevExpr string
for _, test := range tests {
// Print expr only when it changes.
if test.expr != prevExpr {
fmt.Printf("\n%s\n", test.expr)
prevExpr = test.expr
}
expr, err := Parse(test.expr)
if err != nil {
t.Error(err) // parse error
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)
}
}
}
```
@@ -181,9 +181,9 @@ pow(x, 3) + pow(y, 3)
```go
type Expr interface {
Eval(env Env) float64
// Check reports errors in this Expr and adds its Vars to the set.
Check(vars map[Var]bool) error
Eval(env Env) float64
// Check reports errors in this Expr and adds its Vars to the set.
Check(vars map[Var]bool) error
}
```
@@ -191,46 +191,46 @@ type Expr interface {
```go
func (v Var) Check(vars map[Var]bool) error {
vars[v] = true
return nil
vars[v] = true
return nil
}
func (literal) Check(vars map[Var]bool) error {
return nil
return nil
}
func (u unary) Check(vars map[Var]bool) error {
if !strings.ContainsRune("+-", u.op) {
return fmt.Errorf("unexpected unary op %q", u.op)
}
return u.x.Check(vars)
if !strings.ContainsRune("+-", u.op) {
return fmt.Errorf("unexpected 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("unexpected binary op %q", b.op)
}
if err := b.x.Check(vars); err != nil {
return err
}
return b.y.Check(vars)
if !strings.ContainsRune("+-*/", b.op) {
return fmt.Errorf("unexpected binary op %q", b.op)
}
if err := b.x.Check(vars); err != nil {
return err
}
return b.y.Check(vars)
}
func (c call) Check(vars map[Var]bool) error {
arity, ok := numParams[c.fn]
if !ok {
return fmt.Errorf("unknown 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
arity, ok := numParams[c.fn]
if !ok {
return fmt.Errorf("unknown 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
}
var numParams = map[string]int{"pow": 2, "sin": 1, "sqrt": 1}
@@ -255,27 +255,27 @@ Check方法的參數是一個Var類型的集合這個集合聚集從表達式
這個ParseAndCheck函數混合了解析和檢査步驟的過程
```go
// gopl.io/ch7/surface
gopl.io/ch7/surface
import "gopl.io/ch7/eval"
func parseAndCheck(s string) (eval.Expr, error) {
if s == "" {
return nil, fmt.Errorf("empty expression")
}
expr, err := eval.Parse(s)
if err != nil {
return nil, err
}
vars := make(map[eval.Var]bool)
if err := expr.Check(vars); err != nil {
return nil, err
}
for v := range vars {
if v != "x" && v != "y" && v != "r" {
return nil, fmt.Errorf("undefined variable: %s", v)
}
}
return expr, nil
if s == "" {
return nil, fmt.Errorf("empty expression")
}
expr, err := eval.Parse(s)
if err != nil {
return nil, err
}
vars := make(map[eval.Var]bool)
if err := expr.Check(vars); err != nil {
return nil, err
}
for v := range vars {
if v != "x" && v != "y" && v != "r" {
return nil, fmt.Errorf("undefined variable: %s", v)
}
}
return expr, nil
}
```
@@ -283,17 +283,17 @@ func parseAndCheck(s string) (eval.Expr, error) {
```go
func plot(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
expr, err := parseAndCheck(r.Form.Get("expr"))
if err != nil {
http.Error(w, "bad expr: "+err.Error(), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "image/svg+xml")
surface(w, func(x, y float64) float64 {
r := math.Hypot(x, y) // distance from (0,0)
return expr.Eval(eval.Env{"x": x, "y": y, "r": r})
})
r.ParseForm()
expr, err := parseAndCheck(r.Form.Get("expr"))
if err != nil {
http.Error(w, "bad expr: "+err.Error(), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "image/svg+xml")
surface(w, func(x, y float64) float64 {
r := math.Hypot(x, y) // distance from (0,0)
return expr.Eval(eval.Env{"x": x, "y": y, "r": r})
})
}
```
@@ -301,10 +301,10 @@ func plot(w http.ResponseWriter, r *http.Request) {
這個plot函數解析和檢査在HTTP請求中指定的表達式併且用它來創建一個兩個變量的匿名函數這個匿名函數和來自原來surface-plotting程序中的固定函數f有相同的籤名但是它計算一個用戶提供的表達式環境變量中定義了xy和半徑r最後plot調用surface函數它就是gopl.io/ch3/surface中的主要函數脩改後它可以接受plot中的函數和輸出io.Writer作爲參數而不是使用固定的函數f和os.Stdout圖7.7中顯示了通過程序産生的3個麴面
練習7.13爲Expr增加一個String方法來打印美觀的語法樹當再一次解析的時候檢査它的結果是否生成相同的語法樹
**練習 7.13** 爲Expr增加一個String方法來打印美觀的語法樹當再一次解析的時候檢査它的結果是否生成相同的語法樹
練習7.14定義一個新的滿足Expr接口的具體類型併且提供一個新的操作例如對它運算單元中的最小值的計算因爲Parse函數不會創建這個新類型的實例爲了使用它你可能需要直接構造一個語法樹或者繼承parser接口)。
**練習 7.14** 定義一個新的滿足Expr接口的具體類型併且提供一個新的操作例如對它運算單元中的最小值的計算因爲Parse函數不會創建這個新類型的實例爲了使用它你可能需要直接構造一個語法樹或者繼承parser接口)。
練習7.15編寫一個從標準輸入中讀取一個單一表達式的程序用戶及時地提供對於任意變量的值然後在結果環境變量中計算表達式的值優雅的處理所有遇到的錯誤
**練習 7.15** 編寫一個從標準輸入中讀取一個單一表達式的程序用戶及時地提供對於任意變量的值然後在結果環境變量中計算表達式的值優雅的處理所有遇到的錯誤
練習7.16編寫一個基於web的計算器程序
**練習 7.16** 編寫一個基於web的計算器程序