3

昨天写一些代码的时候,我遇到了两个奇怪的问题,我和我的函数式编程朋友都搞不明白。我们看了很长时间,在网上研究了它,但我们无法在任何地方找到任何答案,所以这里是:

问题是在这段代码中:

第一个奇怪的问题:

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.Lengthmatch. 但是通过这样做,我看到了一个新的奇怪之处:

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,警告就会消失。

4

2 回答 2

8

您的两个问题都源于模式匹配允许互换使用值和文字的期望。不,不是的。MSDN 上的模式匹配 (F#)主题很好地概述了支持的模式类型及其应用程序的优先规则。简化冗长描述的主要原则是:除非该值是文字标识符(可区分联合的案例值、异常标签或活动模式案例),否则您无法匹配一个值。

在您的第一个问题点中,编译器不像您期望的那样将其视为数组bs.Length的属性,而是将其视为来自不存在的模块或命名空间的文字或标识符;正如John Palmer在他的回答中指出的那样,您可以通过使用带有保护语句的变量模式来实现预期的行为。合法使用类似于您的模式匹配表达式的示例是:LengthbsLengthbs

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而不是被比较的值;第二个匹配规则没有机会启动。

于 2013-06-24T11:26:39.410 回答
6

match 语句不像你想象的那样工作 - 正确的语法是

match i with
| t when t = bs.Length

在第二种情况下,您实际上创建了一个名为的新变量bsLength,它隐藏了先前的定义bsLength并匹配所有整数,因此您会收到规则永远不匹配的警告。

于 2013-06-24T10:10:40.033 回答