8.1.4 在 F# 中使用函数列表

8.1.4 在 F# 中使用函数列表

 

    首先,我们声明一个表示有关客户信息的类型。客户端有相当多属性,所以,最自然的表示将是 F# 的记录类型,我们在前一章中已经看过。清单 8.4 显示了这个类型声明,和所创建的示例客户的代码。

 

Listing 8.4 Client record type and sample value (F# Interactive)

 

> type Client =
  { Name : string; Income : int; YearsInJob : int
    UsesCreditCard : bool; CriminalRecord : bool };;
type Client = (...)

> let john =

  { Name = "John Doe"; Income = 40000; YearsInJob = 1
    UsesCreditCard = true; CriminalRecord = false };;
val john : Client

 

    这里没有什么新东西,我们声明一个类型,并创建它的实例。为使这个清单更短,我们没有为每个属性使用单独的行,不论是在声明类型时,还是在创新值时。这是有效的 F#,但我们必须在属性之间加上分号。在轻量级的语法中,编译器会自动在行尾将加上(如果分号是需要的),但是,当换行不能帮助编译器时,必须要明确写上分号。

    清单 8.5 完成这个示例。首先,它创建测试列表,然后,决定建议提供是否贷款给前面清单中的示例客户(John Doe) 。

 

Listing 8.5 Executing tests (F# Interactive)

 

> let tests =
     [ (fun cl -> cl.CriminalRecord = true);
       (fun cl -&gt; cl.Income < 30000);
       (fun cl -> cl.UsesCreditCard = false);
       (fun cl -&gt; cl.YearsInJob < 2) ];;
val tests : (Client -> bool) list

&gt; let testClient(client) =
     let issues = tests |&gt; List.filter (fun f -&gt; f (client))
     let suitable = issues.Length <= 1
     printfn "Client: %s\nOffer a loan: %s (issues = %d)" client.Name
               (if (suitable) then "YES" else "NO") issues.Length;;
val testClient : Client �C> unit

&gt; testClient(john);;
Client: John Doe
Offer a loan: YES (issues = 1)

 

    这使用了创建列表常规语法,来初始化测试,使用 lambda 函数的语法写的。我们不必写任何类型注释,F# 仍然正确推断出列表的类型。F# 的类型推断足够聪明,能使用访问成员的名字推断出我们想要使用的记录类型。

    在 C# 版本中,我们使用 Count 方法统计测试失败数。F# 没有等效的函数,我们既可以实现它,也可以组合其他标准的函数,获得相同的结果。这里,我们采用了第二种方法。首先,我们得到被认为是不安全的客户测试列表,这些通过使用 List.filter 返回测试,然后,我们使用 Length 属性,得到问题的数量。

    在本节中,我们学习了如何设计和处理基本的面向行为的数据结构――函数列表――在 C# 和 F# 中。在侧边栏"无点式编程“中,我们会看到清单 8.5 中用到的重要的函数技术。在下一节中,我们会继续有关常见做法的讨论,就像我们讨论两个面向对象的设计模式和相关的函数式结构一样。

 

无点式编程

 

    我们已经见过很多例子,当调用高阶函数时,不必显式写出 lambda 函数,那么,这在清单 8.5 中可能吗?这种写程序的方式被称为无点(point-free),因为,我们处理的数据结构,包含值(比如列表),但是,我们从来没有给这个结构中的值指定任何名字(特定的"点") 。让我们用示例来演示这一概念,我们已经看过了:

 

[1 .. 10] |&gt; List.map ((+) 100)
places |&gt; List.map (snd &gt;&gt; statusByPopulation)

 

    在第一种情况下,我们处理一个数字集合,但是,没有任何符号表示列表中的值。第二种情况有的类似,除了我们处理的列表是元组以外,而且,没有任何的符号来表示元组或元组中的任何元素。

    这种无点式之所以可能,是由于几个编程技术。第一个是使用偏函数应用,是基于有大量参数的函数,创建有必需数量参数的函数的一种方法。在我们的示例中,我们把中缀运算符 (+) 也看做一个普通函数,第二个使用函数组合,这是另一项重要的构建函数技术,不必显式引用这个函数处理的值。

    现在,让我们看看如何重写清单 8.5 的示例。首先,我们要用流运算符来重写这个 lambda 函数。

 

把:

(fun f -&gt; f client)

重写成:

(fun f -&gt; client |&gt; f)

 

    这两个函数意思相同。我们几乎完成,因为,流运算符取这个 client 作为第一个参数值,一个函数作为第二个参数值。如果我们使用偏应用来只指定第一个参数值(client),我们会得到一个函数,它取函数 (f) 作为参数值,并将它应用到 client:

 

tests |&gt; List.filter ((|&gt;) client)

 

    无点式编程应始终用在刀刃上。虽然它使代码更简洁、典雅,但是,有时很难阅读和推理,我们在这里已经表明是不平凡的。无点式对于某些领域的编程是重要的,在第 15 章,我们将会看到它在开发特定域语言时的用途。

你可能感兴趣的:(F#,职场,客户端,休闲)