4.2.2 计算数据

4.2.2 计算数据

 

    在这个应用程序的第一个版本中,我们将只打印标签,以及图表中每个项目所占比例(百分比)。

    若要计算百分比,就需要知道列表中所有项目数值总和。在清单 4.4 中的函数 calculateSum 来计算此值。

 

Listing 4.4 Calculating a sum of numeric values in the list (F# Interactive)

> let rec calculateSum(rows) = match rows with | [] -> 0 | (_, value)::tail -> let remainingSum = calculateSum(tail) value + remainingSum ;; val calculateSum : ('a * int) list -> int > let sum = calculateSum(testData);; val sum : int = 579 > 123.0 / float(sum) * 100.0;; val it : float = 21.24352332

 

    这外函数再次用递归模式处理列表。遵循同样的模式写的代码可能暗示,我们有些地方伏特 错了(重复很少有乐趣,也无聊)。理想情况下,我们只应该写代码的每个版本的独特部分,而不需要自己重复。对前面的示例,这个异议是有效的,我们可使用更优雅的方式,在下一章将学习如何实现。在许多函数程序中,仍然需要递归与模式匹配,所以,看看另外一个示例并熟悉这些概念是很有用的。

    对于一个空列表,函数 calculateSum 只返回 0。对于 cons cell,它以递归方式对尾进行求和(原始列表减去第一个元素),加的结果赋给来自头(列表中的第一项)的一个值。这个代码中的模式配,演示了一个有趣的现象,是值得讨论的。在第二个分支中,我们需要分解 cons cell,以便我们按照 head::tail 模式,匹配列表。代码是更复杂一点,因为在同时,还要根据另一个模式匹配头,以分解元组,写作 (first, second).。是因为,这个列表包含的元组,存储了标题作为第一个参数值,数值作为第二个参数值。在我们的示例中,想要读取这个数值,而忽略标题,这样,我们就可以使用下划线模式去忽略元组的第一个成员。如果我们要组合所有这些模式在一起,就得到 (_, value)::tail,这就是我们在代码中要用的。

    如果我们看看由 F# Interactive 打印出的函数签名,就可以看到,该函数取一个元组列表中作为输入,返回一个整数。输入元组的类型是 'a * int,表示该函数是泛型,说明列表包含任意的元组,第二个元素是一个整数。第一个类型是无关的,因为值在模式匹配中被忽略。F# 编译器自动使代码通用,使用的这一功能被称为自动范型化(automatic generalization)。在第 5 和 6 章,我们将学习更多有关范函数与自动范型化的内容。

    清单 4.3 中的最后一个命令,为在清单 4.4 中的测试铺平了道路,那为什么不止一次输入测试数据呢?为了测试这个函数,先计算总和,最后,计算值为 123 的记录所占的百分比。因为我们想要获得精确结果(21.24%),要将整数转换为浮点数,调用函数 float。

 

转换和解析数字

    F# 是一种 .NET 语言,因此它使用这个平台上可用的数值类型的标准集。下面的清单显示了我们将要用到的最有用的类型。您可以看到,.NET 的类名以斜体表示,F# 使用的短名在括号中:
 

    ■ Int32,UInt32 (int,uint32): 标准的 32 位整数类型;F# 中,文字写作:42 (有符号)或 42u (无符号);也有 16 位和 64 位的变体,对于 16 位 (int16, uint16),写作 42s 和 42us,64 位 (int64,uint64)写作 1 L 或 64 位的 1UL。
    ■ Double, Single (float, float32):表示双精度和单精度浮点数;文字分别写作 3.14 和 3.14f。注意,这里 F# 和 C# 之间的区别: 在 C# 中的 double,在 F# 中叫 float;在 C# 中的 tfloat 在 F# 中叫 float32。

    ■ SByte, Byte (sbyte, byte):有符号和无符号的 8 位整数;文字写作 1y (有符号) 和 1uy (无符号。
    ■ Decimal (decimal):适合财务计算的十进制浮点类型,需要很多的整数和小数位数。文字写作 3.14M。
    ■ BigInteger (bigint):代示任意大小的整数类型。这是 .NET 4.0 中的新类型,由 System.Numerics 命名空间提供;F# 的早期版本包含了这个类型的自己实现,以便你可以使用,针对在 Visual Studio 2008  中早期版本的 .NET 框架 。在 F# 中,这种类型的文字写作 1I。

    与 C# 不同,F# 编译器在不同的数字类型之间,当精度不会丢失的情况下,也不插入自动转换。F # 也不能使用类型转换(type-cast)语法进行显式转换,所以,我们必须把所有的转换写成函数调用。F# 库包含一组转换函数,通常,与 F# 目标类型的名称相同。下面的列表显示了几个最有用的转换函数:

    ■ int :将任意数字值转换为一个整数;该函数是多态的,这意味着,它适用于不同的参数值类型。例如,我们可以写 (int 3.14),用于将浮点值转换为整数,或 (int 42uy),将一个字节值转换为整数。

    ■ float, float32 :将一个数字值转换为双精度或单精度浮点数;它是有时令人困惑的,float 对应于.NET 的 Double 类型,float32 对应于 .NET 的 Single 类型。

    此外可以使用这些函数,将字符串转换为数字。如果需要更多地控制转换,例如,指定的区域信息,可以使用 Parse 方法。此方法在一个 .NET 类中,对应于数值类型,可以在 System 命名空间中找到。例如,要转换一个字符串到整数,可以写成 Int32.Parse("42")。此方法在失败时会将抛出异常,因此,还有一种方法 TryParse。使用此方法,我们可以很容易测试转换是否成功,该方法返回布尔值标记,通过一个输出参数,我们可以得到解析好的数值,但在 F# 中,还有更简单的方法可以访问它。我们将在第 5 章中详细讨论。但是,正如你所看到的,用法很简单:

 

let (succ, num) = Int32.TryParse (str)
if succ then Console.Write("Succeeded: {0}", num)
else Console.Write("Failed")
 

    这决不是在 F# 和 .NET 中使用数字的全面参考。我们只讨论了最常用数值类型和函数。要详细了解,请参阅标准的 .NET 参考或 F# 在线参考 [F# 网站]。

    在清单 4.4 中,我们以计算一个项占测试数据集的百分比的等式结束。这是在 F# 中迭代开发的另一个例子,因为,我们在下一节正需要这个等。我们尝试写出了这个计算中较难的部分,以确保我们可以在隔离情况下做到:这样,我们可以在下一节中使用它。我们将首先编写代码来从文件中读取数据,然后使用此等式作为代码基础,打印数据集到控制台。

你可能感兴趣的:(F#,职场,C#,休闲,函数编程)