4.3 创建控制台应用程序

4.3 创建控制台应用程序


为我们的应用程序编写一个简单的基于控制台输出,是一个好的开始,因为这做起来相对容易,并且很快看到结果。在本节中,我们将使用的几个技术,在稍后的图形版本中也很重要。即使应用程序不需要基于控制台的输出,仍可以把它作为起点,到后来把它改编成更高级的图形化的版本。在这一章中,我们就要这样做。

在上一节,我们已经完成了大部分程序,通过编写通用的泛函数,由控制台和图形版本共享。有一个函数 processLines,取从 CSV 文件加载的字符串的列表,返回解析为元组的列表,函数 calculateSum ,对数据集中的数值求和。在清单 4.4 中,还写过计算百分比的等式,因此,唯一剩余的任务是从文件中读取数据,打印输出到控制台窗口。在清单 4.5 中可以看到我们如何把一切放到一起。

Listing 4.5 Putting the console-based version together (F# Interactive)

> open System.IO;;


> let lines = List.ofseq(File.ReadAllLines(@"C:/Ch03/data.csv"));;
val lines : string list


> let data = processLines(lines);;
val data : (string * int) list =
[("Asia", 3634); ("Australia/Oceania", 30); ("Africa", 767);
("South America", 511); ("Europe", 729); ("North America", 307)]
> let sum = float(calculateSum(data));;
val sum : float = 5978.0


> for (title, value) in data do

let percentage = int((float(value)) / sum * 100.0)
Console.WriteLine("{0,-18} - {1,8} ({2}%)",
title, value, percentage)
;;
Asia - 3634 (60%)
Australia/Oceania - 30 (0%)
Africa - 767 (12%)
South America - 511 (8%)
Europe - 729 (12%)
North America - 307 (5%)

清单 4.5 首先打开 System.IO 命名空间,其中包含使用文件系统的 .NET 类。接下来,我们将使用此命名空间中的类 File 和 它的方法 ReadAllLines,提供一种简单方式读取一个文件中的文本内容,返回字符串的数组。再次,我们使用 ofseq 函数把这个数组转换成字符串列表。接下来的两步是很容易,因为,它们使用我们在上一节中已经实现并测试了的两个函数,处理行,并对结果求和。

让我们现在看看最后一块代码,它使用 for 循环遍历在分析的数据集中的所有元素。它类似于 C# 中的 foreach 语句,在关键字 for 和 in 之间的表达式并不只是一个变量;它是一种模式。如你所见,在 F# 中,模式匹配比你想象的更常见!这一特殊模式把元组分解成标题 (值 title) 和数值
(value)。在循环体中,我们首先计算百分比,使用在清单 4.4 中测试过的等式,然后,使用我们熟悉的.NET 方法 Console.WriteLine 输出结果。

F# 和 .NET 中格式化字符串


在 F# 中有两种方法来解决字符串格式化的问题。第一个选项是使用 F# 库中包含的函数,这与 F# 的前辈(OCaml 语言)是兼容的,但为很好地使用 F# 而设计。另一种方式是使用 .NET 框架中的函数,它有时比相应的 F# 函数更丰富。Printfn 函数,曾经在前面的示例中用过,代表第一组,而 Console.WriteLine 是标准的 .NET 方法,来自后一个列表。

在 .NET 中格式化字符串,需要指定一个复合格式字符串(composite format string)作为第一个参数值,它包含占位符,由后面的参数值填充。占位符包含参数值的索引,(可选)指定对齐方式和格式。两种最常用的格式化方法是 Console.WriteLine(用于打印到控制台)和 String.Format(它返回带格式的字符串):

> let name, date = "Tomas", DateTime.Now;;
> let s = String.Format("Hello {0}! Today is: {1:D}", name, date);;
val s : string = "Hello Tomas! Today is: Sunday, 15 March 2009"

格式化字符串在冒号之后指定,例如,{0:D} 用长日期格式格式化日期,{0:e},用于科学计数法的浮点数,{0: x} 用于十六进制的整数。在上面的清单中,我们还指定对齐方式和打印值的边距,通过在逗号之后加上一个数字:

> Console.WriteLine("Number with spaces: {0,10}!", 42);;
Number with spaces: 42!
> Console.WriteLine("Number with spaces: {0,-10}!", 42);;
Number with spaces: 42 !

除了对齐方式和边距的规范之外,当格式化 .NET 标准的数据类型时,F# 还经常使用 .NET 库,(例如,DateTime 类型或 DateTimeOffset 类型,这表示与 UTC 时区相关的时间)。下面的示例概括了一些有用的格式化字符串。注意输出是区分地区的,因此,它可能是不同的,取决于你的系统设置:

> let date = DateTimeOffset.Now;;
val date : DateTimeOffset = 03/15/2009 16:37:53 +00:00
> String.Format("{0:D}", date);;
val it : string = "Sunday, 15 March 2009"
> String.Format("{0:T}", date);;
val it : string = "16:36:09"
> String.Format("{0:yyyy-MM-dd}", date);;
val it : string = "2009-03-15"

F#-专有的函数的格式化字符串被编译器特别处理,好处是,它可以检查我们使用的类型是否正确。就像在 .NET 中的格式化一样,格式作为第一个参数值,但在格式中占位符指定参数值的类型。没有索引,因此,占位符必须与作为参数值的顺序相同。在 F# 中,经常会使用 printf 和 printfn 输出字符串到控制台,(printfn 有换行符)和 sprintf,返回带格式的字符串:

printfn "Hello %s! Today is: %A" name date
let s = sprintf "Hello %s! Today is: %A" name date

下面的清单显示最常用的点位符类型:

■ %s :参数值是 string 类型。
■ %d :有符号或无符号的整数类型(例如, byte, int, or ulong)
■ %f :float 或 float32 类型的浮点数
■ %A :输出任意类型的值,使用 F# 反射和 .NET ToString 方法的组合,它打印出任何值的最可读的调试信息。

在 .NET 和 F# 的方法之间进行选择有时是困难的。一般情况下,使用 F# 函数通常更好一些,因为,它正为了很好地处理 F# 而设计的,并且,可以基于格式字符串检查参数值的类型。如果你需要的功能不可用,或使用 F# 函数很难实现,就可以切换到 .NET 格式化方法,因为,这两者在 F# 中都可以轻松地使用。

如果不在 F# Interactive 中运行,我们可以把清单 4.5 的代码转换成标准的控制台应用程序。假如你正在 Visual Studio 中编写代码,通过按 Alt + Enter 在 F# Interactive 中执行,那么,你已经有了这个应用程序完整的源代码。要使它更有用的唯一的改变是,从命令行中读取文件名的能力。在 F# 中,我们可以读取命令行参数值,使用标准的 .NET 的 Environment.GetCommandLineArgs 方法。或者,可以写一个入口点(entry-point)函数,取一个字符串数组作为参数值,并使用 EntryPoint 属性值标记 。数组中的第一个元素是运行的可执行文件的名称,因此,为了读取第一个参数值,可以写成 args.[1]。

在这一节中,给我们的数据处理用程序添加了一个简单的基于控制台的输出。现在,是使用 Windows Forms 库实现图形用户界面的时候了,然后,使用 System.Drawing 命名空间中的类绘制饼图。由于有了前面的实验以及在开发展期间使用了 F# Interactive,我们已经知道代码的重要部分工作正常。如果我们打算从零开始写整个应用程序,将很有可能已经有几个小的版本,很难发现在代码中的 bug。当然,在稍后的阶段的开发过程中,我们应该把互动式的实验转变成单元测试。我们会在第 11 章讨论这个话题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值