首页 > 代码库 > 8.3.1 函数的记录

8.3.1 函数的记录

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 函数。

当前版本的代码有一个问题,即,在创建检查时,必须写一些相似的函数。现在,我们就来解决这个问题,减少不必要的代码重复。

8.3.1 函数的记录