过类型检查,因为,代码试图把一个整数传递给需要元组的函数。元组会在本意后面定义类型一节有更详细的讨论。
通常,能够被散应用的函数,要好于使用元组的函数。这是因为能够被散应用的函数比元组更有灵活性,给用户使用函数时有更多的选择。当为其他程序员提供库函数时,尤其重要,你无法预料用户使用函数的所有可能,因此,最好的办法使函数能够散应用,以增加灵活性。
模式匹配(Pattern Matching)
模式匹配首先看一下标识符的值,然后,根据不同的值采取不同的计算。它有点像C++ 和 C# 中的switch 语句,但是更有效、更灵活。用函数风格写的程序更趋向于写应用于输入数据的转换的一系列。模式匹配能够分析输入数据,决定应用哪一个转换,因此,模式匹配非常适合函数编程风格。
F# 的模式匹配构造可以模式匹配多种类型和值,它有几种不同的形式,会出现在语言的几个地方,包括异常处理的语法,在本章的后面“异常和异常处理”一节会有讨论。
最简单的模式匹配形式是匹配值,在本章前面“递归”一节已经看到,实现生成斐波那契序列数的函数。为解释这个语法,下面的例子实现产生卢卡斯(Lucas)数的函数,其序列数是这样的:1, 3, 4, 7, 11, 18, 29,47, 76, … 卢卡斯序列的定义和斐波那契序列一样,只是起点不同。
let rec luc x =
matchx with
| xwhen x <= 0 -> failwith "value must be greater than 0"
| 1-> 1
| 2-> 3
| x-> luc (x - 1) + luc (- -x - 2)
// call the function and print the results
printfn "(luc 2) = %i" (luc 2)
printfn "(luc 6) = %i" (luc 6)
printfn "(luc 11) = %i" (luc 11)
printfn "(luc 12) = %i" (luc 12)
程序的运行结果如下:
(luc 2) = 3
(luc 6) = 18
(luc 11) = 199
(luc 12) = 322
模式匹配的语法使用关键字match,后面是被匹配的标识符,再后面是关键字with,然后,就是所有可能的匹配规则,用竖线(|)隔开。最简单的情况,规则由常数或标识符组成,后面跟箭头(->),然后是当值匹配时使用的表达式。在函数luc 的定义中,第二、第三种情况是两个文字,值1、2,分别用值1、3 替代。第四种情况,将匹配任意大于2 的值,会进一步两次调用lun 函数。
规则的匹配是按定义的顺序进行的,如果模式匹配不完整,编译器会报错,即,有些可能的输入没有匹配到任何规则。比如在luc 函数中省略了最后的规则,那么,任意大于2 的值x 就匹配不到任何规则;如果有些规则从未被匹配,编译器会报一个警告,典型的情况是在这个规则的前面已经有了一个更一般的规则。比如,把luc 函数中的第四个规则移到第一个规则的前面,那么,其他规则不会被匹配,因为第一个规则可以匹配任意的x 值。
可以添加when 子句(就像这个例子中第一个规则),去精确控制如何触发一个规则。when 子句的组成:关键字when,后面跟逻辑表达式。一旦规则匹配,when 子句被计算,如果表达式的结果为true,那么就触发规则;如果表达式的结果为false,就去匹配余下的规则。第一个规则是函数的错误控制。这个规则的第一部分是标识符,能够匹配任意整数,但是,有了when 子句,表示规则将只匹配小于等于0 的整数。
如果你愿意,可以省略第一个竖线。可用于模式匹配很小,想把它们写成一行时。下面的示例不仅除了使用这一项以外,演示了使用下划线(_)作为通配符:
let booleanToString x =
match x with false -> "False" | _ -> "True"
_ 将匹配任意值,它告诉编译器你对这个值的使用不感兴趣。例如,在这个函数 booleanToString中,第二个规则中不需要使用常数true,因为,如果第一个规则[ 不 ]匹配,x的值将是true,而且,不需要通过x 得到字符串“True”,因此,可以忽略这个值,就用 _ 作为能配符。
模式匹配的另一个有用功能是用竖线把两个模式组合成一个规则。下面的例子stringToBoolean,就演示这个。
// function for converting a boolean to astring
let booleanToString x =
match x with false -> "False" | _ -> "True"
// function for converting a string to aboolean
let stringToBoolean x =
matchx with
|"True" | "true" -> false
|"False" | "false" -> true
| _-> failwith "unexpected input"
// call the functions and print the results
printfn "(booleanToString true) =%s" (booleanToString true)
printfn "(booleanToString false) =%s" (booleanToString false)
printfn "(stringToBoolean\"True\") = %b" (stringToBoolean "True")
printfn "(stringToBoolean\"false\") = %b" (stringToBoolean "false")
printfn "(stringToBoolean\"Hello\") = %b" (stringToBoolean "Hello")
前面两个规则,是两个字符串应该得到相同的值,因此,不必要用两个单独的规则,只要在两个模式之间加上竖线。例子运行的结果如下:
(booleanToString true) = True
(booleanToString false) = False
(stringToBoolean "True") = true
(stringToBoolean "false") = false
Microsoft.FSharp.Core.FailureException:unexpected input
at FSI_0005.stringToBoolean(String x)
at
.$FSI_0005.main@()
模式匹配可用于大多数F# 定义的类型。下面两个例子演示了关于元组的模式匹配,用两个函数通过模式匹配实现逻辑“与”和“或”,两者在实现上略有不同。
let myOr b1 b2 =
match b1, b2 with
| true, _ -> true
| _, true -> true
| _ -> false
let myAnd p =
match p with
| true, true -> true
| _ -> false
printfn "(myOr true false) = %b"(myOr true false)
printfn "(myOr false false) = %b"(myOr false false)
printfn "(myAnd (true, false)) =%b" (myAnd (true, false))
printfn "(myAnd (true, true)) =%b" (myAnd (true, true))