【问题标题】:How to pattern match this efficiently?如何有效地进行模式匹配?
【发布时间】:2012-12-02 11:03:16
【问题描述】:

我想将整数数组与以下伪模式进行匹配: 其中a 表示这些数字等于或 0(即假设任何数字“等于”0)

[| a; a; a; a; a; |]          // matches [| 1; 1; 0; 0; 1 |]
[| a; a; a; not a; a; |]      // matches [| 3; 3; 0; 2; 3 |]
[| a; a; not a; a; a; |]      // matches [| 4; 4; 2; 0; 4 |]
[| a; not a; a; a; not a; |]  // matches [| 1; 2; 0; 0; 3 |]
[| a; a; a; not a; a; |]      // matches [| 1; 1; 0; 4; 1 |]
[| not a; a; a; a; a; |]      // matches [| 5; 1; 0; 1; 1 |]

我该怎么做?

【问题讨论】:

  • 我不太明白anot a 在这里的含义。你能说得更清楚一点吗? a 表示“0 或 1”,not a 表示“任何其他数字”吗?
  • 最终链式 if 语句可能是最快的。有没有真正的理由不放弃匹配语句?

标签: f# pattern-matching


【解决方案1】:

在您的示例中,数组的第一个元素始终是 a,您可以在布尔数组上进行匹配:

match Array.map (fun x -> x = Array.get arr 0 || x = 0) arr with
| [| true; true; true; true; true; |] -> ... // matches [| 1; 1; 0; 0; 1 |]
| [| true; true; true; false; true; |] -> ... // matches [| 3; 3; 0; 2; 3 |]
| [| true; true; false; true; true; |] -> ... // matches [| 4; 4; 2; 0; 4 |]
| [| true; false; true; true; false; |] -> ... // matches [| 1; 2; 0; 0; 3 |]
| [| true; true; true; false; true; |] -> ... // matches [| 1; 1; 0; 4; 1 |]
| _ -> ...

这将创建一个临时数组,但对于小尺寸的数组来说没什么大不了的。

更新:

如果第一个 a 在另一列中,您只需在该列上投影并进行另一次模式匹配(5 列最多有 5 个匹配项)。

match Array.map (fun x -> x = Array.get arr 1 || x = 0) arr with
| [| false; true; true; true; true; |] -> ... // matches [| 5; 1; 0; 1; 1 |]
| ...

也就是说,当伪模式的数量和复杂性增加时,最好使用函数而不是模式匹配。

【讨论】:

  • 您可以按列对伪模式进行分组并执行多个match。但您似乎对模式匹配期望过高。当伪模式太复杂时,是时候创建函数来识别它们了。
  • 当我再看一遍时,我发现它是错误的(实际上我的例子是错误的)。作为 [|真的;真的;真的;真的;真的; |] -> ... // 匹配 [| 0; 1个; 2; 3; 4 |] 在你的方法中
【解决方案2】:

您可以定义一个由您要检测的模式参数化的活动模式。在您的示例中,您使用包含anot a 的数组的伪语法编写这些模式,但您可以很容易地将它们编写为包含相同值的实际数组在输入中有匹配的值。例如,类似:

[| 'a'; 'a'; 'b'; 'a'; 'a'; |]

它甚至可以是@bytebuster 解决方案中的字符串(您可以使用ToCharArray 方法将string 转换为char[])。然后你可以定义活动模式:

let inline (|ArrayPattern|_|) pattern input =
  let matches =
    Array.zip pattern input
    |> Seq.groupBy fst 
    |> Seq.forall (fun (_, values) -> 
        let cases = values |> Seq.map snd |> set
        Set.count (cases - Set.singleton 0) <= 1)
  if matches then Some() else None  

我想这与@bytebuster 实现的基本相同。在这里,我将输入数组的元素按模式中对应的键进行分组,然后使用集合操作检查每个组最多有一个非零值。

要使用它,您现在可以将 int[]ArrayPattern [| ... |] 匹配,其中数组参数指定您要查找的特定模式:

match [| 1; 1; 2; 0; 1 |] with 
| ArrayPattern [| 'a'; 'a'; 'b'; 'a'; 'a'; |] -> printfn "match"
| _ -> printfn "not"

【讨论】:

    【解决方案3】:

    我认为,这只是一个计算任务,所以它看起来不太非常漂亮。不过,这是我的尝试。请注意,为了更好的可读性,我使用 string 表示“a”而不是“not a”。

    // Define possible return values
    type TriState<'T> = | Yes of 'T | No of 'T | Unknown with 
        override this.ToString() =
            match this with
            | Unknown   -> "Unknown"
            | Yes value -> sprintf "Array matches, common value %A" value
            | No value  -> sprintf "Array mismatches, common value %A" value
    
    // a boilerplate function
    let matchA (pat: string) xs =
        let mustBeA, mustNotBeA =
            List.zip
                (xs |> Array.toList)
                (pat.ToCharArray() |> Array.toList)
            |> List.partition (snd >> ( (=) 'A'))
        // these values must be all equal
        let mustBeA' =
            mustBeA |> List.map fst |> List.filter ( (<>) 0)
        // these values must NOT be equal to the value taken from above
        let mustNotBeA' =
            mustNotBeA |> List.map fst
        match mustBeA' with
        | []            -> Unknown   // can't find the "must" value
                                     // due all "must" values are zero
        | mustValue::_  ->           // we have bootstrap value to compare against
            if (List.forall ( (=) mustValue) mustBeA')
                && (List.forall ( (<>) mustValue) mustNotBeA')
            then Yes mustValue
            else No mustValue
    

    现在,测试:

    [| 1; 2; 0; 0; 3 |] |> matchA "ABAAB" |> printf "%A\n" // Yes 1
    [| 4; 4; 2; 0; 4 |] |> matchA "AABAA" |> printf "%A\n" // Yes 4
    [| 5; 1; 0; 1; 1 |] |> matchA "BAAAA" |> printf "%A\n" // Yes 1
    

    当然,你可以把它包装成一个参数化的活动模式:

    let (|MatchesA|_|) pattern arr =
        matchA pattern arr |> function | Yes x -> Some x | _ -> None
    
    match [| 1; 2; 0; 0; 3 |] with
    | MatchesA ("AAAAB") x -> sprintf "Pattern1 %d" x
    | MatchesA ("ABAAB") x -> sprintf "Pattern2 %d" x
    | _                    -> "Unknown"
    |> printf "%A\n"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多