[英]How to pattern match this efficiently?
我想将一个整数数组与下面的伪模式匹配:其中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 |]
我该怎么做呢?
在您的示例中,数组的第一个元素始终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 |]
| ...
也就是说,当伪模式的数量和复杂性增加时,最好使用函数而不是模式匹配。
您可以定义由要检测的模式参数化的活动模式。 在您的示例中,您使用包含a
或not a
包含a
数组的伪语法编写这些模式 ,但是当您希望在输入中具有匹配值时,可以非常轻松地将它们写为包含相同值的实际数组。 例如,类似于:
[| 'a'; 'a'; 'b'; 'a'; 'a'; |]
它甚至可以是@ bytebuster解决方案中的string
(您可以使用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实现的基本相同。 在这里,我通过模式中的相应键对输入数组的元素进行分组,然后使用set操作来检查每个组最多只有一个非零值。
要使用它,您现在可以匹配一个int[]
与ArrayPattern [| ... |]
ArrayPattern [| ... |]
其中array参数指定您要查找的特定模式:
match [| 1; 1; 2; 0; 1 |] with
| ArrayPattern [| 'a'; 'a'; 'b'; 'a'; 'a'; |] -> printfn "match"
| _ -> printfn "not"
我认为,这只是一项计算任务,所以看起来不是很漂亮。 不过,这是我的尝试。 注意我使用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"
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.