返回入门教程首页 主页 | 开发工具 | 应用芯片 | 核心模块  
 
   
 

Forth 初学者指导 (使用Win32Forth)

原著 J.V. Noble

编译 Forth 中文网, ForthChina.com

原文 Win32Forth 系统联机在线文档

有价值的程序示例

为了解释如何构造一个有价值的程序,让我们开发一个二分法计算平方根的程序。我们将使用 FORmula TRANslator ftran201.f 以简化代码的外观(也就是说它隐藏了需要进行数据读取的存储的操作)。

首先,我们需要完整地理解算法:

> 如果我们知道根在 xa 和 xb, 之间,并且 f(xa)*f(xb) < 0 ( 至少中间有一个根 ) ,我们就把一个可能的根定在 xp = (xa+xb)/2 .

> 我们计算函数 xp: fp = f(xp). ,如果 If fa*fp > 0 我们设置 xa = xp, 否则我们设置 xb = xp. 我们重复这个过程,直到根满足要求。

开始编程,我们注意到,将要对三个点保持跟踪 : xa, xb 和 xp ,我们也需要对这三个点上的函数值保持跟踪 Ra, Rb 和 Rp. 我们也需要指定一个精度epsilon, 我们希望在其中确定根。

下面, 我们需要定义用户接口。一但我们有了一个开平方的子程序,应该如何访问它?因为我们需要通过名字来引用函数,同时也需要告诉它根的区间,我们希望有某种办法把名字作为参数传递给求根的过程。

我以前曾经开发了一个适合我自己的接口

use( fn.name xa xb precision )bin_root

比如

use( f1 0e0 2e0 1e-5 )bin_root

根将被留在浮点堆栈上。

把函数名字作为参数传递的代码在你装入 ftran201.f 时读入 — 在这个程序中使用的字是 use( , v: defines。v: 在字典中创建一个哑项,在这个程序中命名为 dummy ,它可以在传递名字后执行实际的功能)bin_root。

这里是数据结构和标识符

MARKER -binroots \ say -binroots to unload

\ Data structures

FVARIABLE Ra \ f(xa)

FVARIABLE Rb \ f(xb)

FVARIABLE Rp \ f(xp)

FVARIABLE xa \ lower end

FVARIABLE xb \ upper end

FVARIABLE xp \ new guess

FVARIABLE epsilon \ precision

v: dummy \ create dummy dictionary entry

实际的求平方根的子程序 :) bin_root 将非常简单、很容易编写:

: )bin_root ( xt --) ( f: Low High Precision -- root)

initialize

BEGIN not_converged? WHILE NewPoint REPEAT

f" (xa+xb)/2" ( f: -- root)

;

注意这个子程序由有意义的名字组成,所以它不需要注释。 )bin_root 自己通过堆栈注释来说明。第一行的注释就是 )bin_root 在数据堆栈上有一个“执行标记“,三个浮点数据在浮点堆栈上。这些是它的参数。( 参看 11d 关于 EXECUTE 的讨论 ) 。

执行标记用来改变哑字典项dummy的行为 :

defines dummy

在字 initialize 中使 dummy 功能变成我们计算根的行为。

最后的注释是 ( f: -- root) 说明 )bin_root 把答案留在浮点堆栈上。

从自顶向下的编程方面来说,因为我们是从程序的最后一个定义开始的,并按我们方式向前进。在 Forth 中,我们经常同时使用两种方法 — 自顶向下和自顶向上。

我们现在必须定义的关键字是 initialize  , NotConverged? NewPoint。我们可以开始初始化,因为概念上很简单:

: initialize ( xt --) ( f: lower upper precision --)

defines dummy \ xt -> DUMMY

f" epsilon=" f" xb=" f" xa=" \ store parameters

f" Ra=dummy(xa)"

f" Rb=dummy(xb)"

f" MoreThan( Ra*Rb, 0)" \ same sign?

ABORT" Even # of roots in interval!"

;

ABORT" 如果在堆栈上遇到了数据堆栈上的 TRUE 标志则打印后面的信息,退出执行。 ABORT ( 没有 " ) 简单地退出执行。所以我们经常在一些像IF...THEN 一类的决策语句中见到它(参看 11d 的两个例子)。

ABORT" 被一个测试处理。为了使用一个测试作为 Fortran 类的表达式的一个函数(这个测试消耗浮点堆栈上的两个参数并在数据栈上留下一个标志), 我们必须定义一个同义词,原因是 ftran201.f 不能识别传统的关系操作符比如 > 或者 < . 定义是

: MoreThan ( f: a b) ( -- true if a>b)

POSTPONE F> ; IMMEDIATE

f" MoreThan( Ra*Rb, 0)" 产生的代码是

RA F@ RB F@ F* flit 0.00000E-1 F>

它就是我们要的。我们已经解释了短语 defines dummy。短语 f" xa=" 等是把浮点栈上内容存入浮点变量操作的缩写。这样 f" xa=" 产生代码 XA F!  . initialize 的其它部分是在 (a,b) 边界的端点处计算函数值。

NotConverged? 是一个对 ( 非 ) 转换的测试。 WHILE 在数据堆栈上有一个标志,就像 10a 中描述的那样,所以我们定义:

: not_converged? ( -- f)

f" MoreThan( ABS( xa - xb ), epsilon )" ;

它产生代码

XB F@ XA F@ F- FABS EPSILON F@ F>

NewPoint   很清楚是这样的:

: NewPoint

f" xp = (xa+xb)/2" \ new point

f" Rp = dummy(xp)"

f" MoreThan( Ra*Rp, 0)" \ xp on same side of root as xa?

IF f" xa=xp" f" Ra=Rp"

ELSE f" xb=xp" f" Rb=Rp" THEN

;

那就是,我们通过对分得到了新的估计值,计算了函数,并确定新的边界。其它的工作就是定义适当的顺序,通过将入 bin_rts.f 来测试结果。

FALSE [IF]

Usage example:

: f1 fdup fexp f* 1e0 f- ; ok

use( f1 0e0 2e0 1e-5 )bin_root f. .567142 ok

[THEN]

最后,如果我们真的希望非常细心、计划程序重用,则加入适当的样板文件头,就像在文件中的那样。

 

   

(C) ForthChina.com 版权所有 2004-2010
Email:forthchina@163.com