mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2026-02-08 10:52:46 +08:00
fmt code
This commit is contained in:
266
ch7/ch7-09.md
266
ch7/ch7-09.md
@@ -18,7 +18,7 @@ pow(x, 3) + pow(y, 3)
|
||||
下面的五個具體類型表示了具體的表達式類型。Var類型表示對一個變量的引用。(我們很快會知道爲什麽它可以被輸出。)literal類型表示一個浮點型常量。unary和binary類型表示有一到兩個運算對象的運算符表達式,這些操作數可以是任意的Expr類型。call類型表示對一個函數的調用;我們限製它的fn字段隻能是pow,sin或者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有相同的籤名,但是它計算一個用戶提供的表達式。環境變量中定義了x,y和半徑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的計算器程序。
|
||||
|
||||
Reference in New Issue
Block a user