13.4.2 用计量单位格式化数据
13.4.2 用计量单位格式化数据 XML 数据中的许多指标,都只能转换为浮点数(float);这当然是对的,因为面积和森林覆盖率都是数字,但是,这种数据没有更多的意义。把从非类型化的 XML 数据转换成 F# 类型化数据结构,其目的是通过类型注解,更好地了解这些值含义。为了使类型更具体,我们可以使用计量单位(units of measure),这在第二章提到过。使用这个功能,面积按平方公里计,森林覆盖面积按占总地面积的百分比计。我们首先通过几个示例来了解一下计量单位。 13.4.2.1 使用计量单位 在 F# 中,使用计量单位很容易,因此,我们在这里一带而过。声明计量单位使用 type 关键字,加上专有的特性([])。严格说来,计量单位并不是类型,但我们可以把它当作为另一种类型使用。我们首先定义两个简单的计量单位,表示公里和小时: [<Measure>] type km
[<Measure>] type h
可以发现,Measure 特性说明类型是计量单位。这是一个专有特性,F# 编译器能够理解。我们不一定要自己定义计量单位,因为 FSharp.PowerPack.dll 库中有标准集,但是现在,我们还是自己声明。有了单位 km 和 h 以后,就可以创建表示公里或小时的值了。清单 13.15 显示了创建带单位的值,和使用这些值参与计算的函数。 清单13.15 使用计量单位的计算 (F#) > let length = 9.0<km>;; [1]
val length : float<km> = 9.0
> length * length;; [2]
val it : float<km^2> = 81.0
> let distanceInTwoHours(speed:float<km/h>) = [3]
speed * 2.0<h>;;
val distanceInTwoHours : float<km/h> -> float<km> [4]
> distanceInTwoHours(30.0<km/h>);;
val it : float<km> = 60.0
描述数值常量的单位,把单位括在尖括号角中,加在值的后面[1]。我们首先定义了一个表示在长度的值,以公里计。如果计算有单位的值,F# 能自动推断出结果的单位,因此,距离乘了两次,得出的面积以平方公里计[2]。表示单位,可用常规符号,即,用 ^ 表示乘方,用 / 表示除法,并列单位用乘法。 [<Measure>] type percent
let coef = 33.0<percent>
这段代码创建表示百分比的单位,然后,定义了常量 coef,值为 33%。严格来讲,以百分比计的值没有单位,因为它仅是一个比例,但是,把它定义成单位很有用。为了演示,我们计算 50 公里距离的 33%。由于 coef 表示比例,可以简单地把两个值乘起来: > 50.0<km> * coef;;
val it : float<km percent> = 1650.0
很明显这是错误的。我们期望的结果是以公里计,但是,从推导出的类型可以发现,结果是以公里乘以新的单位 percent。另外,从交互运行的代码还可以看到,数值太大,而计量单位的重要性就在于,可以在类型检查时就发现错误,而不必等到程序实际运行。那么,哪里出问题了呢?总是出在百分比值表示的比例已经乘以 100,要得出正确的结果,需要把值再除以 100 percent: > 50.0<km> * coef / 100.0<percent>;;
val it : float<km> = 16.5
可以发现,现在好多了。我们把结果除以 100 percent,这样,在结果中就没有 percent单位了。F# 自动化简单位,知道 km percent/percent 等于 km。这个示例演示了使用计量单位的重要原因:与使用类型一样,能够有帮于尽早捕获大量的错误。 注意 计量单位还有很多其他重要的功能,在这个简介中我们并没有涉及到。例如,可以定义派生单位,比如 N(表示力,以牛顿计),这实际上就是 kg m/s^2;在函数或类型中,泛型参数也可以使用单位。有关计量单位的详细信息,请参阅 F# 联机文档,和架构师 Andrew Kennedy 关于该功能的博客(http://blogs.msdn.com/andrewkennedy)。 现在,还是回到我们的主示例,把下载的数据转换成带单位的类型。我们将使用原子单位 percent 表示地区森林覆盖率,用单位 km^2 表示面积。 13.4.2.2 格式化世界银行的数据 我们声明的 readValues 函数,从 XML 文档中读取值,最后一个参数是解析函数,用于将每个数据点转换到适当类型的值。我们下载的数组包含了三组面积,以平方公里计,和三组森林覆盖率,以百分比计。清单 13.16 演示了把原始文档转换成数据结构,从中可以方便提取重要信息。 清单 13.16 把原始数据转换成类型化的数据结构 (F#) let areas =
Seq.concat(data.[0..2]) [1]
|> readValues (fun a -> float(a) * 1.0<km^2>) [2]
|> Map.ofSeq [3]
let forests =
Seq.concat(data.[3..5])
|> readValues (fun a -> float(a) * 1.0<percent>)
|> Map.ofSeq
在进行管道处理之前,先把表示第一个指标的所有页面中的数据连接起来[1],再把每个值从字符串转换成数值,以平方公里计[2],然后,用数据生成映射(Map)[3]。第二个命令处理森林覆盖率,与此类似。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |