6.5.1 函数组合

6.5.1 函数组合

 

    处理函数最重要的操作,就是组合。让我们首先看一个示例,这是非常有用的。我们将使用这个示例,用元组存储(城市的)名字和人口。在清单 6.16 中,我们创建一个函数,根据人口的规模,确定是城市、镇,还是村。还通过确定存储在列表中的几个地方的状态测试它。

 

Listing 6.16 Working with city information (F# Interactive)

 

> let places = [ ("Grantchester", 552);
                       ("Cambridge", 117900);
                       ("Prague", 1188126); ];;
val places : (string * int) list

> let statusByPopulation(population) =
     match population with
     | n when n > 1000000 -> "City"
     | n when n > 5000 -> "Town"
     | _ -> "Village";;
val statusByPopulation : int �C> string

> places |> List.map (fun (_, population) �C>
     statusByPopulation(population));;
val it : string list = ["Village"; "Town"; "City"]

 

    清单 6.16 的第一部分(创建一个测试数据的列表,声明 statusByPopulation 函数)非常简单,有趣的是在最后几行。我们想使用 List.map ,来获取每个位置的状态。要做到,我们把一个 lambda 函数作为参数值传递。这个 lambda 函数首先使用模式匹配,从元组中提取第二个元素,然后,调用 statusByPopulation 函数。

    代码运行良好,但可以写得更优雅。关键的概念是,我们必须依次执行两个操作。首先要访问元组中的第二个元素,然后,使用返回的值进行计算。由于第一个操作可以使用 snd 函数完成,我们需要组合这两个函数。在 F# 中,这可以使用函数组合运算符(>>),写成这样:

 

snd >> statusByPopulation

 

    这个操作的结果是一个函数,取一个元组,读取其第二个元素(必须是一个整数),并基于此数计算状态。我们可以了解这些函数是如何组合的,通过看表 6.1,它显示了函数的类型签名。

表 6.1 类型签名:snd,statusByPopulation,和通过使用 >> 使用运算符合这两个函数获得的函数,

函数值 类型

snd
snd (after specification)
statusByPopulation
snd >> statusByPopulation

('a * 'b) -> 'b
('a * int) -> int
int -> string
('a * int) �C> string

 

    表格的第二行,显示了 snd 函数的特定类型,在编译器推断出元组的第二个元素必须是整数之后。我们可以得到这个类型,如果用 int 类型替换第一行中的类型参数 ' b。现在,我们有两个可以组合的函数,因为,第二行的返回类型与第三行的输入类型相同。使用组成,把函数连接到一起,就获得一个函数,它调用第一个函数,并把这个调用的结果作为第二个函数的输入。这个最终的函数,其输入的类型与第二行的函数相同,返回类型与第三行的函数相同。清单 6.17 显示了我们如何使用函数组合重写原来的代码。

 

Listing 6.17 Using the function composition operator (F# Interactive)

 

> places |> List.map (fun x -> (snd >> statusByPopulation) x);;
val it : string list = ["Village"; "Town"; "City"]

> places |> List.map (snd >> statusByPopulation);;
val it : string list = ["Village"; "Town"; "City"]

 

    在第一行,我们显式调用组合函数,把包含城市名和人口的元组作为参数值。这是为了演示,组成的结果是一个函数,可以使用普通的语法来调用。然而,使用函数组合的原因是,可以使用组合的函数作为给其他函数的参数值。这里,这个组合函数取一个元组,并返回一个字符串,因此,我们可以立即使用它,作为给 List.map 的参数值,来获取该示例地方的状态列表。

    函数组合运算符的实现非常简单。下面就是我们如何定义它,假设 F# 库中不存在:

 

> let (>>) f g x = g(f(x))
val (>>) : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c

 

    在这个声明中,该运算符具有三个参数。当我们早先处理它时,只需要指定前两个参数(函数被组合)。更深入了解组合函数是如何工作的,可以看图 6.2 中对这个类型签名的两种可能的解释。

 

image

图 6-2 函数组合运算符的签名。如果指定三个参数值(注释在上面),依次返回调用的结果;如果只指定两个参数值(注释在下面),返回一个组合函数。

 

    这个运算符可用于组合函数,是由于偏应用。如果我们只指定前两个参数值,其结果是组合函数。当这个运算符接收第三个参数值时,它使用该参数值调用第一个函数,然后,使用这个结果调用第二个函数。很明显,指定所有三个参数值,通常没有多大用处, 我们只是能够不使用运算符,而直接调用函数 !

    现在,我们看到在 F# 中函数组合的工作方式,让我们看看在 C# 中它看起来可能如何。

 

 

 

你可能感兴趣的:(职场,笔记,休闲,名字,hadstj)