12.3.3.1 序列表达式中的平面映射

假设我们有一个关于城市的元组列表,每个元组包含城市的名字和它所在的国家,另外,我们还有一个列表,是用户所选的城市。因此,我们可以这样表示样本数据:

let cities = [ ("New York","USA"); ("London", "UK");

("Cambridge","UK"); ("Cambridge", "USA") ]

let entered = [ "London";"Cambridge" ]

现在,假设我们要找出选定城市的国家。我们可以在 cities 列表中遍历所选的城市,找到国家。你可能可能已经看出了这种方法的问题了:有一个名为 Cambridge 的城市,在英国和美国都有,因此,对于一个城市,需要能够返回多条记录。在清单 12.10 中可以看到,我们在序列表达式中使用两个嵌套的 for 循环。

清单12.10 使用序列表达式联接集合 (F# Interactive)

> seq { for name in entered do     [1]

for (n, c) in cities do     [2]

if (n = name) then            | [3]

yield sprintf "%s(%s)" n c };;  |

val it : seq<string> =                                   | 返回剑桥的

seq [ "London (UK)";"Cambridge (UK)"; "Cambridge (USA)" ]  | 两个国家

外层的 for 循环遍历 entered 中的名字[1],嵌套的循环遍历 cities 列表[2]。这样,在嵌套的循环体内部,我们就可以比较每个输入城市的名字,是否与每个已知城市的名字相等。如果名字相同的话,嵌套在两个循环内的代码[3]使用 yield 语句,生成一个项目;如果名字不一样,就不产生任何元素。

用数据库术语来说,这个操作可以用联接(jion)来解释。输入名字的列表与包含城市信息的列表,使用城市名字作为键,进行联接。使用序列表达式来写这个代码是很容易的,它是 F# 中联接编程的首选方式。

我们提到过,任何序列表达式可以写成使用平面映射的操作,因此,我们可以看一下如何显式使用 Seq.collect 重写前面的示例。实际你不会这样做,但是,当我们研究类似于可选工作流,定义自己的、序列表达式时,这将是非常宝贵的。

时间: 2024-12-24 19:45:25

12.3.3.1 序列表达式中的平面映射的相关文章

12.3.3.3 在 C# 中使用平面映射

类似于 collect 函数的LINQ 运算符,是 SelectMany,但两者之间也有差异,因为 LINQ 有不同的要求.而 F# 序列表达式只能使用 collect 函数表示,LINQ 查询可以使用许多其它运算符,所以,对于序列操作,它们需要不同的方式. 我们再先看一下普通语法,然后,再考虑转换成使用显式扩展方法的语法,我们还使用前面的 F# 示例的数据.有关国家信息的城市列表中包含了 CityInfo 类的实例,有两个属性,输入名字的列表只包含字符串.清单 12.13 展示的是我们写的 L

12.2.1 递归的序列表达式

函数式编程中主要的控制流结构是递归.我们已经在很多例子中,写的普通函数就使用过递归,它能够解决命令式编程中的循环问题,而不需依赖可变状态.当我们想写一个简单的递归函数时,要使用 let rec 关键字,这样,就能函数以递归方式调用自身. 用于组合序列的 yield! 结构,也可以在序列表达式中执行递归调用,所以,我们同样可以使用函数编程的方法,生成序列.清单 12.4 生成所有的小于 1 百万的阶乘数,与清单 12.1 的 C# 示例一样. 清单 12.4 使用序列表达式生成序列数 (F# In

12.4.1 自定义查询表达式

原则上,我们可以使用查询处理任何类型,只要它提供了绑定操作.这是函数式编程中这类函数的标准名称,像上一节类型签名所展示的.从技术角度来讲,我们需要实现一些方法,在把查询表达式转换为标准的函数调用,由 C# 编译器所使用.我们将为 12.6 节中的 Option<T> 的类型实现这些方法,该类型没有实现 IEnumerable<T>,所以,不能使用标准查询运算符. 我们首先考虑一下,查询应用到选项类型,是什么意思.清单 12.15 有两个查询,左边的处理列表,右边的处理选项类型.我们

12.3.3.2 直接使用平面映射

首先,我们要看看平面映射到底是什么样子.通常,理解函数如何运行的第一步,就是研究类型签名.图 12.2 比较了 Seq.map(普通映射)和 Seq.collect(平面映射)的类型签名. 图 12.2 对于每个输入元素,普通映射返回一个元素,而平面映射,可以返回元素的任意集合. 提醒一下,类型签名中的 # 号,描述映射函数,传递给 collect,表示函数的返回类型不必一定是 seq <'b> 类型.在前一章,我们讨论过使用 # 号的类型声明,#seq<'b> 位置上可以用实现了

12.1.3 使用 F# 序列表达式 在 C# 中的迭代器非常方便(comfortable),能够在普通的 C# 方法中写复杂的代码 (实现 IEnumerable&lt;T&gt;/IEnumerator

12.1.3 使用 F# 序列表达式 在 C# 中的迭代器非常方便(comfortable),能够在普通的 C# 方法中写复杂的代码(实现 IEnumerable<T>/IEnumerator<T> 接口的类型).开发人员写的代码使用标准的C# 功能,比如环,唯一的改变只是我们可以使用一种新的语句,来做一些非标准的事情,这个新语句用 yield return 表示(或者 yield break 表示终止序列),非标准的行为返回序列中下一个元素的值.在以后需要访问序列的时候(最后,计

12.3.2.2 使用查询和序列表达式

在 C# 3.0 中,我们可以使用新的查询表达式语法,写有关映射和筛选数据的操作.查询表达式还支持许多其他操作,但我们会只关注映射和筛选,来演示函数技术和 F# 功能. F# 虽然没有专门提供的查询表达式,但是,使用序列表达式,仍可以轻松地写出映射和筛选数据的查询.这是因为序列表达式在 F# 中所有地方都可以使用,而不仅仅是返回序列的函数.清单 12.9 显示了使用 C# 中的查询和 F# 中的序列表达式,实现我们前面的示例. 清单 12.9 在 C# 和 F# 中的筛选和映射序列 C# F#

12.1.3.1 写序列表达式

在 C# 中,当我们实现返回 IEnumerable<T>.IEnumerator<T>,或对应的非泛型方法时,可以自动使用迭代器.F# 序列表达式使用 seq 标识符显式标记,而且不必要使用方法体或函数体.正如其名字所暗示的,序列表达式是表达式的不同类型,我们可以在代码中的任意位置使用.清单 12.2 演示了使用此语法,创建简单的序列. 清单 12.2 介绍序列表达式的语法 (F# Interactive) > let nums = seq { let n = 10   

12.2.2 无穷序列

在前一章,我们简单演示过使用延迟值,实现延迟列表.这种数据结构可以用来创建无穷数据结构,比如,从零开始的整数列表.这之所以可能,是因为每个元素的计算被推迟了:只在访问元素时,才计算它的值,并且,每次只关注一个元素的计算. 使用seq<'a> 表示序列是相似的.该接口有一个方法MoveNext,计算出下一个的元素.序列可能是无穷的,即,MoveNext 方法始终能够计算出下一个元素,并永远不会返回false (表示序列结束).无穷序列听起来可能有点奇怪,但我们将看到,它可能很有价值,把算法划分成

hdu1710-Binary Tree Traversals (由二叉树的先序序列和中序序列求后序序列)

http://acm.hdu.edu.cn/showproblem.php?pid=1710 Binary Tree Traversals Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 4210    Accepted Submission(s): 1908 Problem Description A binary tree is a