12.3.3.2 直接使用平面映射

来源:互联网 发布:淘宝搜商品价格 编辑:程序博客网 时间:2024/06/02 13:01

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# 中的相同操作。

0 0
原创粉丝点击