在昨天编写一些代码时,我遇到了两个奇怪的问题,我和我的函数式编程导向的朋友都不知道。我们已经看了很长时间,并在网上研究过它,但我们无法在任何地方找到任何答案,所以这里有:
问题在于此代码:
第一个奇怪的问题:
let outer1 (bs : byte array) =
let rec inner (bs : byte array) (bacc : byte array) (i : int) =
match i with
| bs.Length -> bacc // <--- Error: bs is not recognized. Why?
| _ -> bacc.[i] <- bs.[i]
inner bs bacc (i + 1)
inner bs (Array.zeroCreate bs.Length) 0
这里的问题是:FS0039: The namespace or module 'bs' is not defined.
怎么会这样?毕竟bs在函数签名中。此外,使用let bsLength = bs.Length
定义新值的工作正好在match
之前。但通过这样做,我看到了一个新的奇怪之处:</ p>
let outer2 (bs : byte array) =
let rec inner (bs : byte array) (bacc : byte array) (i : int) =
let bsLength = bs.Length
match i with
| bsLength -> bacc
| _ -> bacc.[i] <- bs.[i] // <--- Warning: Rule never matched. Why?
inner bs bacc (i + 1)
inner bs (Array.zeroCreate bs.Length) 0
此处的问题是警告:warning FS0026: This rule will never be matched
。
我根本不明白。 i
并且数组的长度彼此无关。如果我写一个整数(例如10
)而不是bsLength
,警告就会消失。
答案 0 :(得分:8)
您的问题都来自于期望模式匹配允许交替使用值和文字。不,不是的。 MSDN上的Pattern Matching (F#)主题很好地概述了其应用程序支持的模式类型和优先级规则。简化冗长描述的主要原则是:除非此值为文字或标识符(区分联合的案例值,异常标签),否则您无法匹配值,或活动模式案例。)
在您的第一个问题点中,编译器不会将bs.Length
视为数组Length
的属性bs
,而是将其视为不存在的文字或标识Length
模块或命名空间bs
;正如John Palmer在他的回答中指出的那样,通过使用带有guard statement的变量模式,您可以实现预期的行为。合法使用类似于你的模式匹配表达式的样本将是:
module bs =
[<Literal>]
let Length = 100
//.............................
let v = 100;
let s = match v with
| bs.Length -> "matched"
| _ -> "not matched";;
val s : string = "matched"
编译器将第二个问题点视为变量模式,并为bsLength
分配值i
,而不是像您预期的那样比较值;第二个匹配规则没有机会参与。
答案 1 :(得分:6)
匹配语句不像您认为的那样工作 - 正确的语法是
match i with
| t when t = bs.Length
在第二种情况下,您实际创建了一个名为bsLength
的新变量,该变量隐藏了早期bsLength
的定义并匹配所有整数,因此您可以获得规则永不匹配的警告。