F# odd pattern matching issues

拥有回忆 提交于 2019-12-10 20:12:38

问题


While writing some code yesterday, I ran into two odd problems, which neither me nor my functional programming oriented friend could figure out. We have looked at it for quite some time, and researched it on the net, but we were not able to find any answers anywhere, so here goes:

The issue is that in this code:

First weird problem:

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

The problem here is: FS0039: The namespace or module 'bs' is not defined. How can this be? bs is in the function signature after all. Moreover, defining a new value with let bsLength = bs.Length works right before the match. But by doing so I see a new oddity:

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

Here the problem is a warning that says: warning FS0026: This rule will never be matched. I don't get that at all. i and the length of the array has no relation to each other. If I write an integer (for instance 10) instead of bsLength, the warning disappears.


回答1:


Both your problems stem from the expectation that pattern matching allows using values and literals interchangeably. No, it does not. Pattern Matching (F#) topic on MSDN gives a good overview of supported pattern types and precedence rules of their application. The major principle simplifying a lengthy description there is: you cannot match a value unless this value is a literal, or identifier (a case value of a discriminated union, an exception label, or an active pattern case).

In your first problem point compiler treats bs.Length not as a property Length of array bs as you expect, but as a literal or identifier Length from non-existing module or namespace bs; as John Palmer pointed in his answer you may achieve the expected behavior by using variable pattern with a guard statement. A sample of legitimate use of the pattern matching expression resembling yours would be:

module bs =
    [<Literal>]
    let Length = 100
//.............................
let v = 100;
let s = match v with
    | bs.Length -> "matched"
    | _ -> "not matched";;

val s : string = "matched"

The second problem point is treated by compiler as variable pattern, and bsLength is assigned a value of i instead of values being compared, as you expected; second matching rule does not have chances to kick in.




回答2:


The match statement doesn't work like you think it does - the correct syntax is

match i with
| t when t = bs.Length

In the second case, you actually create a new variable called bsLength which hides the definition of the earlier bsLength and matches all integers, so you get the rule never matched warning.



来源:https://stackoverflow.com/questions/17272899/f-odd-pattern-matching-issues

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!