8.1.4 在 F# 中使用函数列表

首先,我们声明一个表示有关客户信息的类型;客户有很多属性,因此,用F# 的记录类型表示最自然的选择,我们在前一章已经看过。清单 8.4 显示了类型声明,和所创建样本客户的代码。

清单 8.4 Client 记录类型和样本值 (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) 。

清单 8.5 执行测试 (F# Interactive)

> let tests =    <-- 创建测试列表

[ (fun cl ->cl.CriminalRecord = true);

(funcl -> cl.Income < 30000);

(funcl -> cl.UsesCreditCard = false);

(funcl -> cl.YearsInJob < 2) ];;

val tests : (Client -> bool) list    [1]

> let testClient(client) =

let issues = tests|> List.filter (fun f -> f (client))   [2]

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 –> unit

> testClient(john);;

Client: John Doe

Offer a loan: YES (issues = 1)

这是使用lambda 函数写初始化测试创建列表的常规语法,不必写出任何类型批注,F# 仍然能够正确推断出列表的类型[1]。F# 的类型推断非常智能,使用访问成员的名字就能推断出我们想要使用的记录类型。

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

在本节,我们学习了如何设计和使用基本的面向行为的数据结构,函数列表(a list of functions),在 C# 和 F# 中都有。在补充材料“无点式编程(Point-freeprogramming style)”中,我们会看到清单8.5 中用到的重要的函数技术。在下一节,我们会继续有关常见做法的讨论,就像我们讨论两个面向对象的设计模式和相关函数式结构一样。

无点式编程(Point-freeprogramming style)

我们见过很多例子,调用高阶函数时,不必显式写出 lambda 函数,那么,在清单 8.5 中这样做也行吗?程序的这种写法称为无点(point-free),因为我们在使用包含值(比如列表)的数据结构时,从来没有给结构中的值指定任何名字(特定的“点”)。我们用示例来演示已经见过的概念:

[1 .. 10] |> List.map ((+) 100)

places |> List.map (snd >>statusByPopulation)

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

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

现在,我们看一下如何重写清单8.5 的示例。首先,我们要用管道运算符来重写 lambda 函数。

把:(fun f ->f client)

重写成:(fun f-> client |> f)

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

tests |> List.filter ((|>) client)

无点风格编程应始终用在刀刃上。虽然它使代码更简洁、典雅,但是,有时很难阅读和推理,我们在这里的演示就很重要。无点风格对于某些领域的函数编程是重要的,在第十五章,我们将会看到它在开发特定域语言时的用途。

时间: 2024-10-12 06:15:43

8.1.4 在 F# 中使用函数列表的相关文章

10.1 优化函数 在前面的章节中,我们已经知道,递归是 F# 中处理函数的主要控制流机制。我们第一次是使用它写一些进行计算的简单函数,例如,计算指定范围内的数字的和或阶乘。后来,我们发现它在处理递

10.1 优化函数 在前面的章节中,我们已经知道,递归是 F# 中处理函数的主要控制流机制.我们第一次是使用它写一些进行计算的简单函数,例如,计算指定范围内的数字的和或阶乘.后来,我们发现它在处理递归数据结构,最重要的列表是时,是无价的. 我们知道,递归也有一些局限性,堆栈溢出的可能性是最明显的一个:我们将会看到,某些递归计算非常低效.在命令式语言中,通常使用非递归函数,以避免出现问题:函数语言已经有方法解决这些问题,并可以高效地使用递归.首先要集中关注于正确性:如果一个额外的字节吹动堆栈,真正

C++ 链式继承下的虚函数列表

目录 1.虚函数列表的位置 2.虚函数列表的内容 3.链式继承中虚函数列表的内容 ? 注: 虚函数列表 又称为虚表, vtbl , 指向它的指针称为vptr, vs2019中称为__vfptr 操作系统: windows 10 专业版 64位 编译器: Visual Studio 2019 Community ? 1.虚函数列表的位置 结论 编译器一般会保证指向虚函数列表的指针存在于对象实例中最前面的位置 而虚函数列表中的内容, 就是多个函数指针 代码验证: 首先声明一个基类Base和一个派生类

“虐心”的获取C++函数列表

今天在这里和大家分享一下如何获取c++文件中的函数列表,问题和灵感来自于同事小W自开发的C++代码覆盖率工具,原理是通过给现有的代码进行打桩,在运行到该函数的时候对该函数进行标记,最终统计得到代码的函数覆盖度. 如图:程序的源代码 经过打桩后的代码 原理大致如下,这里就不给大家详细介绍了. 那么问题的重点来了,如何解析并且获取cpp文件中的函数呢? 首先想到的方法是的利用编译原理的思想通过添加规则的方法去获取函数,如:对"{","}","(",&

F#中使用box和unbox函数进行装箱和拆箱操作

很多应用都会在界面中使用某种列表控件:用户可以选中.删除或重新排列列表中的项目.这些控件其实都是UITableView 对象,可以用来显示一组对象,例如,用户地址薄中的一组人名. 我们都知道ADO.NET提供了对数据库或外部数据源的数据访问接口,它本身实现了面向连接与面向无连接的数据访问方式.面向连接是以数据库连接为基础的,在打开数据库连接后,将数据访问指令送入数据库内执行后,利用游标来存取结果集的访问模式,优点是访问速度快,缺点则是必须建立连接,且会有锁定问题:面向无连接则是先以面向连接的方式

JavaScript中的函数表达式

在JavaScript中,函数是个非常重要的对象,函数通常有三种表现形式:函数声明,函数表达式和函数构造器创建的函数. 本文中主要看看函数表达式及其相关的知识点. 函数表达式 首先,看看函数表达式的表现形式,函数表达式(Function Expression, FE)有下面四个特点: 在代码中须出现在表达式的位置 有可选的函数名称 不会影响变量对象(VO) 在代码执行阶段创建 下面就通过一些例子来看看函数表达式的这四个特点. FE特点分析 例子一:在下面代码中,"add"是一个函数对象

Python3中map函数的问题

在Python2中map函数会返回一个list列表,如代码: >>> def f(x, y): return (x, y) >>> l1 = [ 0, 1, 2, 3, 4, 5, 6 ] >>> l2 = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ] 返回结果如下: >>> map(f, l1, l2) [(0, 'Sun'), (1, 'Mon'), (2, 'Tue'),

CString中Format函数与格式输入与输出

CString中Format函数与格式输入与输出 Format是一个很常用,却又似乎很烦的方法,以下是它的完整概貌,以供大家查询之用: 格式化字符串forma("%d",12)意思是将一个整形的格式化的字符(我认为是保持其形状不变) 1).格式说明总是以%字符开始,以下是不同类型数据的格式方式%号后的说明: d输出带符号十进制数 o输出无符号八进制数 x输出无符号十六进制数 u输出无符号数 c输出单个字符 s输出一串字符 f输出实数(6位小数) e以指数形式输出实数 g选用f与e格式中

JQuery中trim函数的具体实现代码

由于Javascript 1.8.1 之前的版本,没有内置 trim 函数,所以 JQuery 对它有自己的实现.不同的JQuery版本,trim函数的实现也不尽相同. 阅读本文需要掌握正则表达式用法,如果不是很了解,建议阅读这个.鉴于正则表达式的强大用途(在各种语言如JS,Python,Ruby,Java中都会用到),建议重点学习并掌握. JQuery 1.7.2版本 // 截取的部分源码,不是完整语句,旨在说明实现过程 trimLeft = /^\s+/, trimRight = /\s+$

Linux系统下C语言如何调用scalapack中的函数

在并行计算中经常需要调用scalapck(并行化的lapack)函数库里面的函数进行编程,这里简单介绍在C语言如何调用scalapck中的矩阵向量乘的函数. 注意:scalapack中的函数是用fortran写的,矩阵是按列进行存储的. scalapack的链接需要用到blas,因此确保本机上安装好了blas.gfortran 下面是一个矩阵向量乘法的例子(为了简单计算,该程序中设定进程数为4): #include <stdio.h> #include <string.h> #in