7.4.2 使用聚合操作进行计算
7.4.2 使用聚合操作进行计算 ? ??? 聚合背后的概念在于我们保持一些状态,将在整个操作过程中传递。我们用一个初始状态开始,用给定的处理函数,为文档中的每个部件,计算一个新的状态。这种概念被反映在函数的签名中: ? val aggregateDocument : ? ??? 我们使用的广义概念"一些状态"的原因是,这种状态可能是任何东西。在这个函数签名中的状态类型是一个类型参数 'a,所以,它取决于这个函数的用户。函数的后两个参数值指定要处理的文档部件(这也表示整个文档)和状态的初始值。AggregateDocument 的第一个参数值是用于处理文档部件的函数,它基于旧的状态和单个文档部件计算新状态。清单 7.17 显示了完整的实现(或许是令人惊讶的简短)。 ? Listing 7.17 Aggregation of document parts (F#) ? let rec aggregateDocument f state docPart = ? ??? 代码需要遍历文档中的所有部件,它在当前部件上调用这个函数,然后,以递归方式处理所有子部件。与此相关的顺序是:我们可以设计出函数,首先处理所有的子部件,然后是当前的部件。不同之处在清单 7.17 中,该函数会在树的"根"节点上调用,而在其他情况下,可能会在"叶"节点上首先调用。对我们的目的而言,这两个选项都工作正常,但对于一些高级的处理,我们可能必须考虑,想要哪一种遍历方法。 ??? 当我们对当前部件调用这个聚合函数时,对要保存新的状态的值,使用相同的名字。新值隐藏旧值,在这里,这是一项有用的安全措施: 它意味着,在我们已经计算出新的状态以后,不会由于过失而意外使用原来的状态。接下来,我们处理可能包含子部件的部件。对于标题部件,我们以递归方式处理正文。当我们获得有子部件列表的拆分部件时,用列表上通常的聚合函数 List.fold 对它进行聚合。 ??? 聚合对于各种东西可能是可用的。下面的代码片断展示了如何使用这个操作,来统计整个文档中的单词数量: ? let totalWords = ? ??? 我们使用作为参数值的函数只关心包含文本的部件。我们有两个这样的部件,都包含 TextContent 类型值的文本。F# 的模式匹配,使我们能够只使用有一种模式,处理两种情况。这种语法称为或模式(or-pattern),只能够用在两种模式绑定到值有相同类型的标识符的情况。在我们的例子中,只需要一个 TextContent 类型的标识符(tx)。在模式匹配的正文中,我们把这个文本拆分成使用空格分隔的单词,并加上返回数组的长度作为计数。 ? 注意? ??? 这里有几个建议,你可以在本书的网站上找到,http://www.functional-programming.net 或 http://www.manning.com/Real-WorldFunctionalProgramming。 ??? ■ 可以使用 mapDocument 来把超过500多个字符的文本部件拆分为两列。 ??? ■ 可以使用聚合来收集一组用于文档的图像。 ??? ■ 可以实现类似筛选的操作,取一个类型为 (DocumentPart -> bool)的函数,并创建文档,只包含该函数返回 true 的部件。使用这个函数,能够从文档中删除所有的图像。 ? ??? 我们已经看到,第二种表示便于对文档进行各种操作,特别是,如果我们首先实现了有用的高阶函数。现在,我们要回到 C#,将讨论我们刚才看到的适用于C# 编程的思想,以及它们如何与面向对象方法中众所周知的概念相关。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |