如何构造匹配表达式

时间:2017-02-22 18:17:26

标签: parsing command-line f# pattern-matching

我允许使用像--10GB这样的命令行参数,其中--GB是常量,但是可以在之间替换为1,10或100之类的数字常数值,例如--5GB

我可以使用substr轻松解析字符串的开头和结尾,或者编写命令行解析器,但是想要使用match。我只是不确定如何构造匹配表达式。

let GB1     = cvt_bytes_to_gb(int64(DiskFreeLevels.GB1))
let arg = argv.[0]

let match_head = "--"
let match_tail  = "GB"

let parse_min_gb_arg arg =
    match arg with
    | match_head & match_tail -> cvt_gb_arg_to_int arg
    | _ -> volLib.GB1

我收到警告说_ This rule will never be matched.如何构建AND表达式是什么?

2 个答案:

答案 0 :(得分:4)

我能想到的最好的是使用部分活动模式:

let (|GbFormat|_|) (x:string) =
    let prefix = "--"
    let suffix  = "GB"
    if x.StartsWith(prefix) && x.EndsWith(suffix) then
        let len = x.Length - prefix.Length - suffix.Length
        Some(x.Substring(prefix.Length, len))
    else
        None

let parse_min_gb_arg arg =
    match arg with
    | GbFormat gb -> gb
    | _ -> volLib.GB1

parse_min_gb_arg "--42GB"

答案 1 :(得分:4)

您不能匹配字符串,除了匹配整个值,例如match s with | "1" -> 1 | "2" -> 2 ...

解析开始和结束将是最有效的方法,没有必要变得聪明(顺便说一下,这是一个普遍真实的陈述)。

但如果确实想要使用模式匹配,那么肯定可以这样做,但是你必须自己制作一些自定义匹配器(也称为"活动模式& #34;。)

首先,制作一个自定义匹配器来解析"中间"由前缀和后缀包围的字符串的一部分:

let (|StrBetween|_|) starts ends (str: string) =
  if str.StartsWith starts && str.EndsWith ends then 
    Some (str.Substring(starts.Length, str.Length - ends.Length - starts.Length))
  else 
    None

用法:

let x = match "abcd" with 
        | StrBetween "a" "d" s -> s
        | _ -> "nope"
// x = "bc"

然后创建一个解析整数的自定义匹配器:

let (|Int|_|) (s: string) = 
    match System.Int32.TryParse s with 
    | true, i -> Some i 
    | _ -> None

用法:

let x = match "15" with 
        | Int i -> i
        | _ -> 0
// x = 15

现在,结合两者:

let x = match "--10GB" with 
        | StrBetween "--" "GB" (Int i) -> i 
        | _ -> volLib.GB1
// x = 10

这种组合和嵌套模式的能力是它们的主要力量:你可以用小的,易于理解的部分构建一个复杂的模式,并让编译器将它与输入匹配。这基本上就是为什么它被称为" 模式匹配"。 : - )