【形式化方法:VDM++系列】4.VDM实战1——铁路费用计算
又有将近2个月没更新博客了啊!winter holiday简直玩儿疯了的说!结果假期前学习的形式化方法已经忘了大半!面对期末作业,大脑一片空白。于是,赶快复习了一下之前学习的姿势!
这次的主要任务是完成一个费用计算程序。
1.问题
Make a model to calculate train fare from a station to another station by using functions and the following table.
使用函数计算两个车站之前的铁路费用。基础数据如下:
values
vFareSet = {
mk_FareRecord("Tokyo", "Shinagawa", ),
mk_FareRecord("Tokyo", "Shinjuku", ),
mk_FareRecord("Shinjuku", "Shinagawa", )
}
函数开头如下:
static public Calculate_fare : set of FareRecord * Station * Station -> nat
Calculate_fare(aSetOfFare, aDeparture, anArrival) == is not yet specified;
2.解法
首先必须明确问题要求,仔细观察函数的输入输出,Calculate_fare函数有3个输入参数,分别是费用记录集合(set of FareRecord),始发车站,终点车站;输出为一个整数,即2个车站间的费用;函数内容尚未定义,现在需要我们定义函数体完成费用的计算。
计算费用的业务逻辑是:根据始发站和终点站查找费用记录集合,如果在费用纪录集合中找到始发站和终点站都相同的纪录,就返回该记录的费用。用SQL语言可以很简单的解释该逻辑:
select fare_num from fareRecord
where fareRecord.aDeparture=aDeparture and fareRecord.anArrival=anArrival
明确了业务逻辑就可以打开Overture工具开始编码了!

上图展示了Overture工具默认建立的vdm类,它包类定义,类型定义,值定义,初始化变量,操作定义,函数定义,测试用例定义。这里我们需要定义type,value和functions
(1)定义类型
需要定义的类型有:fareRecord类型,Station类型。

定义Station类型与String等价,定义FareRecord类型为结构体类型,包含3个子元素
(2)定义值

(3)定义函数

注意:红线部分所展示的函数体与之前的SQL语句基本相同,let .. in set .. be st .. in .. 是VDM++独特的语法所在,需要仔细体会才能明白。
(4)定义函数的前置条件和后置条件

函数的前置条件是:对于要求的始发站和终点站应该包含于费用记录集合当中,对于费用记录集合中的2条记录,如果始发站和终点站相同,那么费用也应该相同。
函数的后置条件是:存在唯一一条记录,该记录的始发站,终点站与传入的始发站,终点站相同,该记录的结果与执行函数体得到的result相同。
至此,我们的需求定义完成。
代码如下:
class fareCaculate
types
public Station = seq of char
inv s == s<>""; public FareRecord ::
fDeparture : Station
fArrival : Station
fFare : nat
inv fr == fr.fDeparture <> fr.fArrival functions
static public Calculate_fare : set of FareRecord * Station * Station -> nat
Calculate_fare(aSetOfFare, aDeparture, anArrival) ==
let wFareRecord in set aSetOfFare be st
{aDeparture, anArrival} = {wFareRecord.fDeparture, wFareRecord.fArrival}
in wFareRecord.fFare
pre
{aDeparture, anArrival} in set
{{e.fDeparture, e.fArrival} | e in set aSetOfFare} and
forall wFareRecord1, wFareRecord2 in set aSetOfFare &
wFareRecord1.fDeparture = wFareRecord2.fDeparture and
wFareRecord1.fArrival = wFareRecord2.fArrival =>
wFareRecord1.fFare = wFareRecord2.fFare
post
exists1 wFareRecord in set aSetOfFare &
{aDeparture, anArrival} = {wFareRecord.fDeparture, wFareRecord.fArrival} and
RESULT = wFareRecord.fFare; end fareCaculate
3.测试
现在编写测试程序对上述需求进行测试。测试程序如下所示:
class Test is subclass of fareCaculate
values vFareSet = {
mk_FareRecord("Tokyo","Shinagawa",),
mk_FareRecord("Tokyo","Shinjuku",),
mk_FareRecord("Shinjuku","Shinagawa",)
};
functions
static public makeOrderMap : seq of bool +> map nat to bool
makeOrderMap(s) == {i |-> s(i) | i in set inds s};
public run : () -> seq of char * bool * map nat to bool
run() ==
let testcases = [t1(), t2(), t3()],
testResults = makeOrderMap(testcases)
in
mk_("The result of regression test = ",
forall i in set inds testcases & testcases(i), testResults);
public t1 : () -> bool
t1() ==
Calculate_fare(vFareSet, "Tokyo", "Shinagawa") = ;
public t2 : () -> bool
t2() ==
Calculate_fare(vFareSet, "Tokyo", "Shinjuku") = ;
public t3 : () -> bool
t3() ==
Calculate_fare(vFareSet, "Shinjuku", "Shinagawa") = ;
end Test
测试结果如图所示:

测试全部返回true,表明需求定义正确。
至此,第一版的的铁路票价计算需求定义程序完成。
【形式化方法:VDM++系列】4.VDM实战1——铁路费用计算的更多相关文章
- 【形式化方法:VDM++系列】3.基于VDM++的图书管理系统需求定义
接前文:http://www.cnblogs.com/Kassadin/p/4091040.html 1.Before We Start: 在开始图书管理系统需求定义之前,需要先进行一些说明. 1.1 ...
- 【形式化方法:VDM++系列】2.VDMTools环境的搭建
接前文:http://www.cnblogs.com/Kassadin/p/3975853.html 上次讲了软件需求分析的演化过程,本次进入正题——VDM开发环境的搭建 (自从发现能打游戏以来,居然 ...
- 【形式化方法:VDM++系列】1.前言
1.前言 今天开始上课学习软件需求分析与VDM++,经过一节课的学习,我又增长了见识. 软件需求工程在软件工程中处于十分核心的地位:需求分析的好坏直接决定软件工程的成败.这一点是我之前对需求工程的理解 ...
- 【asp.net core 系列】10 实战之ActionFilter
0.前言 在上一篇中,我们提到了如何创建一个UnitOfWork并通过ActionFilter设置启用.这一篇我们将简单介绍一下ActionFilter以及如何利用ActionFilter,顺便补齐一 ...
- Chapter 5 软件工程中的形式化方法
从广义上讲,形式化方法是指将离散数学的方法用于解决软件工程领域的问题,主要包括建立精确的数学模型以及对模型的分析活动.狭义的讲,形式化方法是运用形式化语言,进行形式化的规格描述.模型推理和验证的方法. ...
- 手牵手,使用uni-app从零开发一款视频小程序 (系列下 开发实战篇)
系列文章 手牵手,使用uni-app从零开发一款视频小程序 (系列上 准备工作篇) 手牵手,使用uni-app从零开发一款视频小程序 (系列下 开发实战篇) 扫码体验,先睹为快 可以扫描下微信小程序的 ...
- 从零开始搭建django前后端分离项目 系列六(实战之聚类分析)
项目需求 本项目从impala获取到的数据为用户地理位置数据,每小时的数据量大概在8000万条,数据格式如下: 公司要求对这些用户按照聚集程度进行划分,将300米范围内用户数大于200的用户划分为一个 ...
- 【asp.net core 系列】8 实战之 利用 EF Core 完成数据操作层的实现
0. 前言 通过前两篇,我们创建了一个项目,并规定了一个基本的数据层访问接口.这一篇,我们将以EF Core为例演示一下数据层访问接口如何实现,以及实现中需要注意的地方. 1. 添加EF Core 先 ...
- Spark RDD/Core 编程 API入门系列之动手实战和调试Spark文件操作、动手实战操作搜狗日志文件、搜狗日志文件深入实战(二)
1.动手实战和调试Spark文件操作 这里,我以指定executor-memory参数的方式,启动spark-shell. 启动hadoop集群 spark@SparkSingleNode:/usr/ ...
随机推荐
- Android EditText不弹出输入法焦点问题的总结
转自:http://mobile.51cto.com/aprogram-403138.htm 看一个manifest中Activity的配置,如果这个页面有EditText,并且我们想要进入这个页面的 ...
- 找不到System.Runtime.Serialization.Json的解决方案
System.ServiceModel System.ServiceModel.Web System.Runtime.Serialization 三者均要添加引用
- ASP.NET MVC 之控制器与视图之间的数据传递
今天,我们来谈谈控制器与视图之间的数据传递. 数据传递,指的是视图与控制器之间的交互,包括两个方向上的数据交互,一个是把控制器的数据传到视图中,在视图中如何显示数据,一个是把视图数据传递到控制器中, ...
- SublimeText更换皮肤
SublimeText是一款非常好用的文本编辑工具,官方网址http://www.sublimetext.com/. 这里介绍一下手动安装SublimeText皮肤的方法. (1)首先找一款你喜欢的皮 ...
- 构造函数继承关键apply call
主要我是要解决一下几个问题: 1. apply和call的区别在哪里 2. apply的其他巧妙用法(一般在什么情况下可以使用apply) 我首先从网上查到关于apply和 ...
- 小米2s换了屏幕后不能近距离对焦,拆过后无法对焦?
主要问题就是小米2/2S手机中壳套后摄像头的位置,里面还有一个正方形的黑色塑胶垫片,一般拆机后这个垫片是不会掉出来的,所以一般上盖时也是直接把中壳合上后上螺丝. 这样安装基本会导致塑胶垫片把摄像头顶住 ...
- 删除_desktop.ini病毒文件
del h:\_desktop.ini /f/s/q/a/f 强制删除只读文件/s 从当前目录及其所有子目录栓出指定文件.显示正在删除的文件名/q 制定清音状态.不提示确认删除/a 按照属性来删除
- PID控制器的数字实现及C语法讲解
PID控制器的数字实现及C语法讲解 概述 为方便学习与交流,根据自己的理解与经验写了这份教程,有错误之处请各位读者予以指出,具体包含以下三部分内容: (1) PID数字化的推导过程(实质:微积分的近 ...
- 使用泛型 类型“System.Collections.Generic.IEnumerator<T>”需要 1 个类型参数
解决办法:添加 using System.Collections:命名空间
- Java学习之路:2、Mysql 链接与查询
1.事实上我发现有了php的功力在里面,学习java起来还是不是很费劲,只是java就没有面向过程这一说 package second; import java.sql.*;//导入 public c ...