12.3.3.2 直接使用平面映射

首先,我们要看看平面映射到底是什么样子。通常,理解函数如何运行的第一步,就是研究类型签名。图 12.2 比较了 Seq.map(普通映射)和 Seq.collect(平面映射)的类型签名。

图 12.2 对于每个输入元素,普通映射返回一个元素,而平面映射,可以返回元素的任意集合。

提醒一下,类型签名中的 # 号,描述映射函数,传递给 collect,表示函数的返回类型不必一定是 seq <‘b> 类型。在前一章,我们讨论过使用 # 号的类型声明,#seq<‘b> 位置上可以用实现了 seq<‘b> 接口的任何实际类型代替。这就是说,我们可以返回序列,或者 F# 列表、数组,或者甚至是自定义的集合类型。

现在,让我们看一下如何使用 Seq.collect 函数,重写前面的示例。通常,在序列表达式内部,可以用一个 Seq.collect 调用,替换每个 for 循环。这正是早期版本的 F# 编译器在编译序列表达式时所做的。在我们的示例中有两个嵌套的循环,因此要做两步转换。在清单 12.11 中,我们先替换外部循环。

清单12.11 用平面映射替换外层循环 (F# Interactive)

> entered |> Seq.collect (fun name–>     [1]

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

if (n = name) then

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

val it : seq<string> =

seq[ "London (UK)"; "Cambridge (UK)"; "Cambridge (USA)"]

我们用平面映射替换了外层循环,在清单 12.11 中是 Seq.collect,用户输入的城市列表作为输入参数传递给它[1]。我们提供的 Lambda 函数,有一个城市的名字作为参数,然后,遍历所有已知城市的集合,找到包含该城市的国家[2]。搜索使用清单 12.10 中的序列表达式实现,但是删除了外层的循环。我们使用的 lambda 函数返回有关城市的序列,有指定的名字,Seq.collect 函数将所有这些连接在一起,返回一个结果序列。

现在,我们已经组合了函数调用和序列表达式,那么,再来看一下如何替换内层 for 循环,以完成转换。我们可以使用嵌套的 Seq.filter 和 Seq.map,或者,甚至更好,用 Seq.choose,它可以把两个操作组合成一个。我们将展示编译器所做的,因此,用平面映射替换每一个 for 循环,自然符合这样的规则。清单 12.12 显示的还是同样的处理代码,只使用了 Seq.collect 调用。

清单12.12 用两个平面映射替换循环 (F# Interactive)

> entered |> Seq.collect (fun name–>

cities |> Seq.collect (fun (n, c) –>     [1]

if (n = name) then        |

[ sprintf "%s (%s)" n c ]  | [2]

else [] ));;               |

val it : seq<string> =

seq[ "London (UK)"; "Cambridge (UK)"; "Cambridge(USA)" ]

外层调用与清单 12.11 中的相同,但是,在 lambda 函数内部,我们现在执行另一个 Seq.collect 调用[1]。嵌套的调用遍历所有的城市,对于每个城市,如果城市与输入的名称不匹配,则返回空列表;如果匹配,则返回包含一个元素的列表。可以发现,我们已经用返回包含一个元素列表的代码,替换了 yield 的使用。如果代码包含多个 yield,可能会返回更长的列表。还有一点值得注意,必须添加else 子句返回空列表;在序列表达式的内部,这是隐式的。

虽然 Seq.collect 函数有时是有用的,比如,用高阶函数写处理序列的代码,但是,真正的重要性在于,它能将任意序列表达式转换成函数调用。我们很快会看到,序列表达式是更通用的 F# 结构的特例,平面映射是定义序列表达式如何运行的原始基本操作;我们还会看到,在这一节所演示的转换,与那些可以自定义值的其他计算,运行方式是类似的。

我们前面提到过,可以利用映射和筛选实现嵌套循环[2],但是,正如我们所知道的,序列表达式中的 for 循环,足以表达实现映射、筛选和联接,我们在这一节已经看到。现在,我们看一下在 C# 中的相同操作。

时间: 2024-12-26 10:34:48

12.3.3.2 直接使用平面映射的相关文章

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

假设我们有一个关于城市的元组列表,每个元组包含城市的名字和它所在的国家,另外,我们还有一个列表,是用户所选的城市.因此,我们可以这样表示样本数据: let cities = [ ("New York","USA"); ("London", "UK"); ("Cambridge","UK"); ("Cambridge", "USA") ] let e

12.3.3 平面映射(flattening projection)

12.3.3 平面映射(flatteningprojection) 平面映射,可以为源集合中的每个元素,生成元素序列,合并所有返回序列.我们很快就会看到,这是定义其他处理操作,包括映射和筛选,的基本操作.平面映射所独有的特征,就是能够为每个输入元素生成多个输出元素. 注意 在 LINQ 库中,这个操作称为 SelectMany.在查询表达式中,用多个 from 子句表示.从名字可以看出,它类似于 Select 操作,但是能够为源中的每个项目返回多个元素.F# 库中对应的函数是 Seq.colle

12.3.3.3 在 C# 中使用平面映射

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

有用函数编程

<序> 感谢 关于本书 关于封面 第一部分 学习函数式思维 第一章 不同的思维 1.1 什么是函数式编程? 1.2 通往有用函数编程之路 1.3 用函数式编程提高生产力 1.3.1 函数范式 1.3.2 声明式编程风格 1.3.3 了解程序的执行 1.3.4 设计并发友好的应用程序 1.3.5 函数风格怎样形成代码 1.4 函数式编程演示样例 1.4.1 用声明式风格表达意图 1.4.1.1 用 LINQ 处理数据 1.4.1.2 用 XAML 描写叙述用户界面 1.4.1.3 声明式函数动画

12.3.2.2 使用查询和序列表达式

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

Hibernate4.x之映射关系--单向一对多

在领域模型中,类与类之间最普遍的关系就是关联关系在UML中,关联是有方向的 以Customer和Order为例:一个用户能发出多个订单,而一个订单只能属于一个客户.从Order到Customer的关联是多对一关联:而从Customer到Order是一对多关联 单向n-1 单向n-1关联只需从n的一端可以访问到1的一端 域模型:从Order到Customer的多对一单向关联需要在Order类中定义一个Customer属性,而在Customer类中无需定义存放Order对象的集合属性 关系数据模型:

Hibernate4.x之映射关系--双向1-n

双向1-n与双向n-1是完全相同的两种情形 双向1-n需要在1的一端可以访问n的一端,反之亦然. 域模型:从Order到Customer的多对一双向关联需要在Order类中定义一个Customer属性,而在Customer类中需定义存放Order对象的集合属性 关系数据模型:ODDERS表中的CUSTOMER_ID参照CUSTOMER表的主键 当Session从数据库中加载Java集合时,创建的是Hibernate内置集合类的实例,因此在持久化类中定义集合属性时必须把属性声明为Java接口类型

web应用程序servlet的映射名称的规则及请求过程

首先用MyEclipse创建一个web Project(工程名起为TestServletProject),新建一个Servlet(这里servlet的名字起TestServlet),将请求的servlet映射名称设为/TestServlet,(具体步骤可以查看tomcat上servlet程序的配置与处理servlet请求过程).并在TestServlet的doGet方法中在控制台打印一句“this is TestServlet” jxf.servlet.TestServlet.java 1 pa

Hibernate之关联关系映射(一对一主键映射和一对一外键映射)

1:Hibernate的关联关系映射的一对一外键映射: 1.1:第一首先引包,省略 1.2:第二创建实体类: 这里使用用户信息和身份证信息的关系,用户的主键编号既可以做身份证信息的主键又可以做身份证信息的外键,这里先做外键. 创建User.java: 用户和身份证一对一的关联关系映射       private IdCart idCart; IdCart.java: 身份证和用户,一对一的关系       private User user; 1 package com.bie.bean; 2