8.3.1 函数的记录
我们已经看到一种处理多个函数的方法。在前面的示例中,返回结果为函数元组,同样的技术可以表示有新增报表功能的应用程序。报表函数的参数为客户,在屏幕上输出信息,返回结果为 unit。使用这种表示方法,行为列表的类型将是:
((Client -> bool) * (Client -> unit))list
初看起来有点可怕,很复杂,函数没有名字,使代码不具可读性。在前面的示例中,这并不是大问题,因为函数只在局部使用,但是,这个列表是我们应用程序的一个关键数据结构,所以,它应该尽可能清晰。使代码更具可读性,有一个简单的解决方案,是用记录类型代替元组。我们可以像这样来定义:
type ClientTest =
{ Check : Client –>bool
Report : Client ->unit }
这段代码定义了有两个字段的记录,且两个都是函数。使用函数,可以像使用任何其他类型一样,这又是一个例子。这个声明类似于声明简单对象(或接口),我们将在后面讨论这种相似性。现在,要先看一下清单 8.11,我们使用前面声明的记录类型,创建检查的列表。
清单 8.11 创建有报表的测试 (F#)
let checkCriminal(client) =client.CriminalRecord = true | 检查犯罪记录
let reportCriminal(client) = |
printfn "Checking 'criminalrecord' of '%s' failed!" client.Name |
let checkIncome(client) = client.Income< 30000 | 检查最低收入
let reportIncome(client) = |
printfn "Checking 'income' of'%s' failed (%s)!" |
client.Name"less than 30000" |
let checkJobYears(client) =client.YearsInJob < 2 | 检查当前工作年限
let reportJobYears(client) = |
printfn "Checking 'years in thejob' of '%s' failed (%s)!" |
client.Name "less than 2" |
let testsWithReports = [1] 创建记录列表
[ { Check = checkCriminal; Report =reportCriminal };
{ Check = checkIncome;Report = reportIncome };
{ Check = checkJobYears;Report = reportJobYears };
(* more tests... *) ]
清单 8.11 是一系列的 let 绑定。为使代码更具可读性,我们没有使用 lambda 函数,所有的检查都定义为普通的 F# 函数。每个检查,我们都定义了一个 check 前缀的函数,和一个 report 前缀的函数。如果在 F# Interactive 中输入代码,可以看到函数类型对应到 ClientTest 记录类型。最后一个操作创建了检查列表[1]。我们需要为每个标准创建一个记录,保存两个相关函数,并创建包含这些记录值的列表。
我们还要修改函数,检查特定客户。首先找到失败的检查(使用 Check 字段),然后,让它们输出结果(使用 Report 字段)。清单 8.12 显示修改后的函数,当我们运行时,使用样本客户,一样能够输出。
B Gets list of tests
that failed
清单 8.12 检查客户,输出报表 (F# Interactive)
> let testClientWithReports(client)=
let issues = | [1]
testsWithReports | 得到失败的检查列表
|>List.filter (fun tr -> tr.Check(client)) |
let suitable =issues.Length <= 1 <-- 计算整体结果
for i inissues do | [2]
i.Report(client) | 报告所有发现的问题
printfn"Offer loan: %s" |
(if (suitable) then"YES" else "NO")
;;
val testClientWithReports : Client ->unit
> testClientWithReports(john);;
Checking 'years in the job' of 'John Doe'has failed (less than 2)!
Offer loan: YES
相对于清单 8.5,testClient 函数只略有改变。第一个变化是在选择那些检查失败的行中[1]。列表现在是记录的集合,所以,我们必须使用保存在 Check 字段中的函数来检查客户。第二个改变是,早先,我们感兴趣的只是未能通过检查的数量,现在,我们还需要输出有关失败的详细信息[2],这是通过使用命令式的for 循环实现的,为所有失败的检查调用 Report 函数。
当前版本的代码有一个问题,即,在创建检查时,必须写一些相似的函数。现在,我们就来解决这个问题,减少不必要的代码重复。