设为首页 加入收藏

TOP

第三章函数编程(二)(二)
2015-11-21 02:13:27 来源: 作者: 【 】 浏览:51
Tags:第三章函数 编程
是更优美的函数调用方法。

F# 有两种不同类型的运算符:

前缀(prefix)运算符,它的运算对象(operand)在这个运算符的后面;

中缀(infix)运算符,这个运算符在第一和第二个运算对象之间。

F# 提供了丰富多样的运算符集合,可用于数字、布尔值、字符串和集合类型。在 F# 和它的库函数中定义的运算符数量甚众,限于篇幅,在此不再一一详解,本文将着重介绍如何在 F# 中使用和定义运算符。

就像在C# 一样,F# 的运算符也可以重载(overload),就是说,一个运算符可以用于多种的类型;然而,与C# 不同的是,各个运算对象必须有相同的类型,否则会产生编译错误。F# 也允许用户定义、重定义运算符。

在这一节的最后会有讨论。

F# 的运算符重载规则与 C# 类似,因此,任何 BCL 类、.NET 库函数,在 C# 中支持运算符重载的运算符,在 F# 中也一样支持。例如,可以用+ 运算符去连接字符串,同样,也能对日期时间类型(System.DataTime)和时间段(System.TimeSpan)进行加,因为这些类型都支持+ 运算符的重载。下面的例子就演示了这些重载:

let ryhm = "Jack " + "and" + "Jill"

open System

let oneYearLater =

DateTime.Now+ new TimeSpan(365, 0, 0, 0, 0)

与函数不一样,运算符不是值,因此,它就不能当作参数传递给其他函数。然而,如果真的需要把一个运算符当成值来使用,只要把它用括号括起来就行了,现在,这个运算符的行为就与函数完成一样了。这样,会有两个推论:

1、运算符现在已经是函数了,其函数必须放在运算符的后面:

let result = (+) 1 1

2、由于运算符是值,因此,它可以作为函数的结果返回,传递给其他的函数,或者绑定到标识符。这样定义 add 函数就非常简洁了:

let add = (+)

我们将会这一章的后面看到,当处理[ 由于中文的原因,有时翻成处理,有时翻成使用。]列表时,把运算符当作值是非常有用的。

只要用户愿意,既可以定义运算符,也可重新定义已有的运算符(虽然,这样做并不总是可取的,因为,这样的运算符不再支持重载)。下面的例子故意重把+ 定义为减法。

let (+) a b = a - b

printfn "%i" (1 + 1)

用户定义(custom自定义)运算符必须是非字母、数字,可以是一个字符,也可以是一组字符。可以用下列字符自定义运算符:

!$%&*+-./<=>?@^|~

[

这里有个较大的变化,原来还有一个冒号,但是不能是字符组的首字符;现在,干脆取消了。

]

定义运算符的语法与用关键字let定义函数相同,除了用运算符替换函数名,并用括号括起来,让编译器知道括号中的符号是运算符的名字,而不是运算对象。下面的例子自定义了运算符+:*,完成运算对象先加再乘:

let ( +* ) a b = (a + b) * a * b

printfn "(1 +* 2) = %i" (1 +:* 2)

例子的运行结果如下:

(1 +* 2) = 6

一元运算符总是在运算对象的前面。自定义的二元运算符,如果以感叹号(!)、问号(?)、波浪号(~)开头,就是前缀运算符;其他的则是中缀运算符,放在运算对象的中间。

函数应用(Function application)

函数应用,有时称为函数组合(function composition),或者组合函数(composing functions),简单地说,就是调用带有参数的函数。下面的例子定义了函数 add,然后应用两个参数。注意,参数没有用括号或逗号分隔,只需要用空格分隔。

let add x y = x + y

let result = add 4 5

printfn "(add 4 5) = %i" result

函数的运行结果如下:

(add 4 5) = 9

F# 的函数,如果有固定数量的参数,会直接应用源文件中接下来的值,调用函数时不需要使用括号;有时,使用括号,是为了说明函数应用了哪些参数。看一下这个例子,用 add 函数实现四个数的加法,可以为每一个函数调用绑定一个标识符,但是,针对这样一个简单的计算,这样做有点太?嗦了:

let add x y = x + y

let result1 = add 4 5

let result2 = add 6 7

let finalResult = add result1 result2

相反,更好的办法通常是把一个函数的结果直接传递给下一个函数。要这样做,就要用括号说明哪些参数与该函数相关:

let add x y = x + y

let result =

add(add 4 5) (add 6 7)

这里,add 函数的第二、第三个位置分别用括号把4、5 和 6、7 分了组,第一个位置将根据其他两个函数的结果进行计算。

F# 还有一种组合函数的方法,使用 pipe-forward运算符(|>),它的定义是这样的:

let (|>) x f = f x

简单地说,它取一个参数 x,应用到给定的函数 f,这样,参数就可以放在函数的前面了。下面的例子使用|> 运算符,把参数 0.5 应用到函数 System.Math.Cos:

let result = 0.5 |> System.Math.Cos

在某些情况下这种反转可能是很有用的,特别是打算把许多函数链接到一起时。下面是用 |> 运算符重写的前面 add 函数示例:

let add x y = x + y

let result = add 6 7 |> add 4 |> add5

有些程序员认为这种风格更具可读性,因为,因为它比以从右到左的方式读代码更方便。现在,这段代码可以读作“6 加 7,然后,把结果转交给下一个函数,再加 4,然后,再把结果转交给函数,加 5”。更多有关这种函数应用风格的适用环境我们放到第四章讲解。

这个示例还用到了 F# 的散函数应用,下一节再讨论。

函数的散应用(Partial Application of Functions)

F# 支持函数的散应用(有时也称为散函数,或curried 函数)。即,不必要给函数一次传递所有的参数。注意,前一节最后的示例,只传递一个参数给 add 函数,而它有两个参数。这是与函数就是值的观点相关。

因为函数就是值,如果它没有一次接受所有的参数,那么,它返回的值就是一个新函数,等着接受其余的参数。这样,在这个例子中,给 add 函数只传递 4,结果是一个新函数,我们把它称为 addFour,因为它只取一个参数,并把它加 4 。乍看起来,这个思想是无趣、无益的,但是,它是函数编程中的强大部分,在全书中都有应用。

这种行为不可能总是适当的,例如,如果这个函数有两个浮点参数表示一个点,那么,可能不希望这些数值分别传递给函数,因为,只有它们在一起才能表示点。另外,也可以用括号把函数的参数括起来,用逗号分隔,把它们变成一个元组(tuple)。请看下面的代码:

let sub (a, b) = a - b

let subFour = sub 4

当编译这个例子,会出现下面的错误消息:

prog.fs(15,19): error: FS0001: Thisexpression has type

int

but is here used with type

'a * 'b

这个示例不能编译,因为 sub 函数要求一次给足两个参数。现在 sub只有一个参数,元组(a,b),而不是两个参数。然而,在第二行调用 sub 时只提供了一个参数,且不是元组。因此,程序不能通

首页 上一页 1 2 3 4 下一页 尾页 2/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇hdu 3067 小t的游戏 下一篇稀疏有向图最短路径

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: