回到简体

This commit is contained in:
chai2010
2016-02-15 11:06:34 +08:00
parent 9e878f9944
commit 2b37b23285
177 changed files with 2354 additions and 2354 deletions

View File

@@ -1,13 +1,13 @@
## 7.9. 示例: 表式求值
## 7.9. 示例: 表式求值
在本中,我們會構建一個簡單算術表達式的求值器。我們將使用一接口Expr表示Go言中任意的表式。現在這個接口不需要有方法,但是我們後面會爲它增加一些。
在本中,我们会构建一个简单算术表达式的求值器。我们将使用一接口Expr表示Go言中任意的表式。现在这个接口不需要有方法,但是我们后面会为它增加一些。
```go
// An Expr is an arithmetic expression.
type Expr interface{}
```
的表達式語言由浮點數符號(小數點);二元操作符+-\* 和/;一元操作符-x和+x調用pow(x,y)sin(x)和sqrt(x)的函例如x和pi的量;然也有括號和標準的優先級運算符。所有的值都是float64型。下面是一些表式的例子:
的表达式语言由浮点数符号(小数点);二元操作符+-\* 和/;一元操作符-x和+x用pow(x,y)sin(x)和sqrt(x)的函例如x和pi的量;然也有括号和标准的优先级运算符。所有的值都是float64型。下面是一些表式的例子:
```go
sqrt(A / pi)
@@ -15,7 +15,7 @@ pow(x, 3) + pow(y, 3)
(F - 32) * 5 / 9
```
下面的五個具體類型表示了具的表達式類型。Var型表示對一個變量的引用。(我很快知道爲什麽它可以被出。literal型表示一個浮點型常量。unary和binary型表示有一到兩個運算對象的算符表式,些操作可以是任意的Expr型。call型表示對一個函數的調用;我們限製它的fn字段能是powsin或者sqrt。
下面的五个具体类型表示了具的表达式类型。Var型表示对一个变量的引用。(我很快知道为什么它可以被出。literal型表示一个浮点型常量。unary和binary型表示有一到两个运算对象的算符表式,些操作可以是任意的Expr型。call型表示对一个函数的调用;我们限制它的fn字段能是powsin或者sqrt。
<u><i>gopl.io/ch7/eval</i></u>
```go
@@ -44,13 +44,13 @@ type call struct {
}
```
爲了計算一包含量的表式,我需要一environment變量將變量的名字映射成對應的值:
为了计算一包含量的表式,我需要一environment变量将变量的名字映射成对应的值:
```go
type Env map[Var]float64
```
也需要每表示式去定義一個Eval方法這個方法會根據給定的environment量返迴表達式的值。因爲每個表達式都必提供這個方法,我們將它加入到Expr接口中。這個包隻會對外公ExprEnv和Var型。調用方不需要取其它的表達式類型就可以使用這個求值器。
也需要每表示式去定义一个Eval方法这个方法会根据给定的environment量返回表达式的值。因为每个表达式都必提供这个方法,我们将它加入到Expr接口中。这个包只会对外公ExprEnv和Var型。用方不需要取其它的表达式类型就可以使用这个求值器。
```go
type Expr interface {
@@ -59,7 +59,7 @@ type Expr interface {
}
```
下面大家展示一個具體的Eval方法。Var型的這個方法對一個environment變量進行査找,如果這個變量沒有在environment中定義過這個方法會返迴一個零值literal型的這個方法簡單的返迴它眞實的值。
下面大家展示一个具体的Eval方法。Var型的这个方法对一个environment变量进行查找,如果这个变量没有在environment中定义过这个方法会返回一个零值literal型的这个方法简单的返回它真实的值。
```go
func (v Var) Eval(env Env) float64 {
@@ -71,7 +71,7 @@ func (l literal) Eval(_ Env) float64 {
}
```
unary和binary的Eval方法會遞歸的計算它的運算對象,然後將運算符op作用到它上。我們不將被零或無窮數除作爲一個錯誤,因爲它們都會産生一固定的結果無限。最call的這個方法會計算對於powsin或者sqrt函數的參數值,然後調用對應在math包中的函
unary和binary的Eval方法会递归的计算它的运算对象,然后将运算符op作用到它上。我们不将被零或无穷数除作为一个错误,因为它们都会产生一固定的结果无限。最call的这个方法会计算对于powsin或者sqrt函数的参数值,然后调用对应在math包中的函
```go
func (u unary) Eval(env Env) float64 {
@@ -111,9 +111,9 @@ func (c call) Eval(env Env) float64 {
}
```
一些方法會失敗。例如,一call表式可能未知的函或者錯誤的參數個數。用一個無效的算符如!或者<建一unary或者binary表式也是可能會發生的管下面提到的Parse函數不會這樣)。這些錯誤會讓Eval方法panic其它的錯誤算一個沒有在environment量中出現過的Var隻會讓Eval方法返迴一個錯誤的結所有的這些錯誤都可以通過在計算前檢査Expr來發現是我接下來要講的Check方法的工作但是讓我們先測試Eval方法
一些方法会失败。例如,一call表式可能未知的函或者错误的参数个数。用一个无效的算符如!或者<建一unary或者binary表式也是可能会发生的管下面提到的Parse函数不会这样)。这些错误会让Eval方法panic其它的错误算一个没有在environment量中出现过的Var只会让Eval方法返回一个错误的结所有的这些错误都可以通过在计算前检查Expr来发现是我接下来要讲的Check方法的工作但是让我们先测试Eval方法
下面的TestEval函數是對evaluator的一個測試它使用了我們會在第11章解的testing包但是在知道調用t.Errof會報告一個錯誤就足這個函數循環遍歷一個表格中的這個表格中定了三個表達式和針對每個表達式不同的環境變第一個表達式根據給定圓的面積A計算它的半第二個表達式通過兩個變量x和y計算兩個立方體的體積之和第三個表達式將華氏溫度F轉換成攝氏度
下面的TestEval函数是对evaluator的一个测试它使用了我们会在第11章解的testing包但是在知道用t.Errof会报告一个错误就足这个函数循环遍历一个表格中的这个表格中定了三个表达式和针对每个表达式不同的环境变第一个表达式根据给定圆的面积A计算它的半第二个表达式通过两个变量x和y计算两个立方体的体积之和第三个表达式将华氏温度F转换成摄氏度
```go
func TestEval(t *testing.T) {
@@ -151,15 +151,15 @@ func TestEval(t *testing.T) {
}
```
對於表格中的每一條記録這個測試會解析它的表式然後在環境變量中算它輸出結里我們沒有空間來展示Parse函但是如果你使用go get下載這個包你就可以看到這個函數
对于表格中的每一条记录这个测试会解析它的表式然后在环境变量中算它输出结里我们没有空间来展示Parse函但是如果你使用go get下载这个包你就可以看到这个函数
go test11.1) 命令會運行一包的測試用例
go test11.1) 命令会运行一包的测试用例
```
$ go test -v gopl.io/ch7/eval
```
這個-v標識可以讓我們看到測試用例打印的正常情下像這個一樣成功的測試用例阻止打印果的里是測試用例里fmt.Printf句的
这个-v标识可以让我们看到测试用例打印的正常情下像这个一样成功的测试用例阻止打印果的里是测试用例里fmt.Printf句的
```
sqrt(A / pi)
@@ -175,9 +175,9 @@ pow(x, 3) + pow(y, 3)
map[F:212] => 100
```
的是目前止所有的入都是合的格式但是我們的運氣不可能一直都有甚至在解釋型語言中爲了靜態錯誤檢査語法是非常常靜態錯誤就是不用行程序就可以檢測出來的錯誤通過將靜態檢査和動態的部分分可以快速的檢査錯誤併且對於多次檢査隻執行一次而不是每次表達式計算的候都進行檢査
的是目前止所有的入都是合的格式但是我们的运气不可能一直都有甚至在解释型语言中为了静态错误检查语法是非常常静态错误就是不用行程序就可以检测出来的错误通过将静态检查和动态的部分分可以快速的检查错误并且对于多次检查只执行一次而不是每次表达式计算的候都进行检查
讓我們往Expr接口中增加另一方法Check方法在一個表達式語義樹檢査出靜態錯誤們馬上會説明它的vars參數
让我们往Expr接口中增加另一方法Check方法在一个表达式语义树检查出静态错误们马上会说明它的vars参数
```go
type Expr interface {
@@ -187,7 +187,7 @@ type Expr interface {
}
```
的Check方法展示在下面literal和Var型的算不可能失所以這些類型的Check方法會返迴一個nil值對於unary和binary的Check方法首先檢査操作符是否有效後遞歸的檢査運算單相似地對於call的這個方法首先檢査調用的函是否已知且有有正確個數的參數然後遞歸的檢査每一個參數
的Check方法展示在下面literal和Var型的算不可能失所以这些类型的Check方法会返回一个nil值对于unary和binary的Check方法首先检查操作符是否有效后递归的检查运算单相似地对于call的这个方法首先检查调用的函是否已知且有有正确个数的参数然后递归的检查每一个参数
```go
func (v Var) Check(vars map[Var]bool) error {
@@ -236,7 +236,7 @@ func (c call) Check(vars map[Var]bool) error {
var numParams = map[string]int{"pow": 2, "sin": 1, "sqrt": 1}
```
們在兩個組中有選擇地列出有問題的輸入和它得出的錯誤Parse函這里沒有出會報出一個語法錯誤和Check函數會報出語義錯誤
们在两个组中有选择地列出有问题的输入和它得出的错误Parse函这里没有出会报出一个语法错误和Check函数会报出语义错误
```
x % 2 unexpected '%'
@@ -248,11 +248,11 @@ log(10) unknown function "log"
sqrt(1, 2) call to sqrt has 2 args, want 1
```
Check方法的參數是一Var型的集合這個集合聚集從表達式中找到的量名了保成功的這些變量中的每一都必須出現在環境變量中從邏輯上講這個集合就是調用Check方法返迴的結但是因爲這個方法是遞歸調用的所以對於Check方法填充果到一個作爲參數傳入的集合中更加的方便調用方在初始調用時必須提供一空的集合
Check方法的参数是一Var型的集合这个集合聚集从表达式中找到的量名了保成功的这些变量中的每一都必须出现在环境变量中从逻辑上讲这个集合就是用Check方法返回的结但是因为这个方法是递归调用的所以对于Check方法填充果到一个作为参数传入的集合中更加的方便用方在初始调用时必须提供一空的集合
在第3.2們繪製了一個在編譯器才定的函f(x,y)。在我可以解析檢査和計算在字符串中的表可以建一個在運行時從客戶端接收表式的web應用併且它會繪製這個函數的表示的可以使用集合vars來檢査表達式是否是一個隻有兩個變,x和y的函——實際上是3爲我們爲了方便提供半大小r且我們會在計算前使用Check方法拒有格式問題的表這樣我們就不在下面函的40000個計算過100x100個柵每一有4複這些檢査
在第3.2们绘制了一个在编译器才定的函f(x,y)。在我可以解析检查和计算在字符串中的表可以建一个在运行时从客户端接收表式的web应用并且它会绘制这个函数的表示的可以使用集合vars来检查表达式是否是一个只有两个变,x和y的函——实际上是3为我们为了方便提供半大小r且我们会在计算前使用Check方法拒有格式问题的表这样我们就不在下面函的40000个计算过100x100个栅每一有4复这些检查
這個ParseAndCheck函混合了解析和檢査步驟的過
这个ParseAndCheck函混合了解析和检查步骤的过
<u><i>gopl.io/ch7/surface</i></u>
```go
@@ -279,7 +279,7 @@ func parseAndCheck(s string) (eval.Expr, error) {
}
```
爲了編寫這個web用,所有我需要做的就是下面這個plot函數,這個函數有和http.HandlerFunc相似的名:
为了编写这个web用,所有我需要做的就是下面这个plot函数,这个函数有和http.HandlerFunc相似的名:
```go
func plot(w http.ResponseWriter, r *http.Request) {
@@ -299,12 +299,12 @@ func plot(w http.ResponseWriter, r *http.Request) {
![](../images/ch7-07.png)
這個plot函解析和檢査在HTTP求中指定的表達式併且用它來創建一個兩個變量的匿名函數。這個匿名函數和來自原surface-plotting程序中的固定函f有相同的名,但是它算一個用戶提供的表式。環境變量中定了xy和半r。最plot調用surface函它就是gopl.io/ch3/surface中的主要函數,脩改後它可以接受plot中的函數和輸出io.Writer作爲參數,而不是使用固定的函f和os.Stdout。7.7中示了通程序生的3個麴面。
这个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的算器程序。