第7章,部分字词修订,语序调整。少量错误修订。

This commit is contained in:
zhliner
2017-08-24 22:29:24 +08:00
parent 9a9b9a0594
commit 17919273e1
14 changed files with 87 additions and 87 deletions

View File

@@ -50,7 +50,7 @@ type call struct {
type Env map[Var]float64
```
我们也需要每个表式去定义一个Eval方法这个方法会根据给定的environment变量返回表达式的值。因为每个表达式都必须提供这个方法我们将它加入到Expr接口中。这个包只会对外公开ExprEnv和Var类型。调用方不需要获取其它的表达式类型就可以使用这个求值器。
我们也需要每个表式去定义一个Eval方法这个方法会根据给定的environment变量返回表达式的值。因为每个表达式都必须提供这个方法我们将它加入到Expr接口中。这个包只会对外公开ExprEnv和Var类型。调用方不需要获取其它的表达式类型就可以使用这个求值器。
```go
type Expr interface {
@@ -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,7 +111,7 @@ 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转换成摄氏度
@@ -159,7 +159,7 @@ go test(§11.1) 命令会运行一个包的测试用例:
$ go test -v gopl.io/ch7/eval
```
这个-v标识可以让我们看到测试用例打印的输出正常情况下像这个一样成功的测试用例会阻止打印结果的输出这里是测试用例里fmt.Printf语句的输出
这个-v标识可以让我们看到测试用例打印的输出正常情况下像这样一个成功的测试用例会阻止打印结果的输出这里是测试用例里fmt.Printf语句的输出
```
sqrt(A / pi)
@@ -177,7 +177,7 @@ pow(x, 3) + pow(y, 3)
幸运的是目前为止所有的输入都是适合的格式但是我们的运气不可能一直都有甚至在解释型语言中为了静态错误检查语法是非常常见的静态错误就是不用运行程序就可以检测出来的错误通过将静态检查和动态的部分分开我们可以快速的检查错误并且对于多次检查只执行一次而不是每次表达式计算的时候都进行检查
让我们往Expr接口中增加另一个方法Check方法一个表达式语义树检查出静态错误我们马上会说明它的vars参数
让我们往Expr接口中增加另一个方法Check方法一个表达式语义树检查出静态错误我们马上会说明它的vars参数
```go
type Expr interface {
@@ -248,9 +248,9 @@ 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函数混合了解析和检查步骤的过程