mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-11-17 17:12:59 +00:00
第7章,部分字词修订,语序调整。少量错误修订。
This commit is contained in:
@@ -50,7 +50,7 @@ type call struct {
|
||||
type Env map[Var]float64
|
||||
```
|
||||
|
||||
我们也需要每个表示式去定义一个Eval方法,这个方法会根据给定的environment变量返回表达式的值。因为每个表达式都必须提供这个方法,我们将它加入到Expr接口中。这个包只会对外公开Expr,Env,和Var类型。调用方不需要获取其它的表达式类型就可以使用这个求值器。
|
||||
我们也需要每个表达式去定义一个Eval方法,这个方法会根据给定的environment变量返回表达式的值。因为每个表达式都必须提供这个方法,我们将它加入到Expr接口中。这个包只会对外公开Expr,Env,和Var类型。调用方不需要获取其它的表达式类型就可以使用这个求值器。
|
||||
|
||||
```go
|
||||
type Expr interface {
|
||||
@@ -71,7 +71,7 @@ func (l literal) Eval(_ Env) float64 {
|
||||
}
|
||||
```
|
||||
|
||||
unary和binary的Eval方法会递归的计算它的运算对象,然后将运算符op作用到它们上。我们不将被零或无穷数除作为一个错误,因为它们都会产生一个固定的结果无限。最后,call的这个方法会计算对于pow,sin,或者sqrt函数的参数值,然后调用对应在math包中的函数。
|
||||
unary和binary的Eval方法会递归的计算它的运算对象,然后将运算符op作用到它们上。我们不将被零或无穷数除作为一个错误,因为它们都会产生一个固定的结果——无限。最后,call的这个方法会计算对于pow,sin,或者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函数混合了解析和检查步骤的过程:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user