你可以嵌套“双重利弊”模式匹配吗?

时间:2009-01-07 15:53:29

标签: f# pattern-matching

我想加强一个模式,只匹配通过额外验证功能的数字。

let (|IsValid|_|) n = ...

let (|Nil|One|Two|) (l : int list) =
    match l with 
    | a :: b :: t -> Two(a + b)
    | a :: t      -> One(a)
    | _           -> Nil

“一”案很容易:

    | IsValid(a) :: t -> One(a)

“两个”案件对我来说并不明显。它需要验证数字的总和。我可以不使用守卫吗?

...

编辑:我可以像这样使用一个守卫(带有bool返回的isValid函数):

    | a :: b :: t when isValid a + b -> Two(a + b)

这不仅仅是匹配模式的优雅;更糟糕的是,a + b被施加两次。

另请注意,这是我实际代码的简化版本(例如,我不是要简单地匹配不同长度的列表) - 问题是关于双重缺点模式的嵌套匹配。

2 个答案:

答案 0 :(得分:2)

当你这样做时:

| a :: b :: t -> ... 

您不一定要匹配列表中的两个元素。最好使用[]代替t来完全匹配两个元素 - t可以是更多元素的列表。

 | a :: b :: [] -> Two (a+b)

这将确保您匹配两个且仅两个元素 - 免费检查错误!我建议这样做,即使你希望函数只接受0,1或2个元素的列表。所以,

编辑:

let (|MatchTwo|_|) = function
    | a :: b :: t -> Some(a + b :: t)
    | _ -> None
let (|Nil|One|Two|) (l : int list) = match l with 
    | MatchTwo(IsValid(a) :: t) -> Two(a)
    | IsValid(a) :: t  -> One(a)
    | _ -> Nil

是的,请使用when。这是一团糟。模式匹配就是这样,在匹配中应用函数确实没有意义。但考虑到我之前提到的内容。根据您的示例:

match l with
| a :: b :: t when isValid (a+b) -> Two (a+b)
| a :: t when isValid (a) -> One a
| _ -> Nil

如果第一个模式上的isValid为false,则第二个模式将匹配长度超过1的列表 - 发出警告。在你的模式中尽可能具体,如果你想匹配一个元素,那就去做吧。

如果用于组合a和b(在这种情况下为+)的任何操作都是计算上昂贵的,那么在测试isValid并返回变体类型之前,您将不得不删除when并使用let语句。

答案 1 :(得分:2)

我的解决方案:添加一个“帮助器”识别器,其返回值设计为在父模式中使用:

let (|MatchTwo|_|) = function
    | a :: b :: t -> Some(a + b :: t)
    | _ -> None

像这样使用它:

let (|Nil|One|Two|) (l : int list) =
    match l with 
    | MatchTwo(IsValid(a) :: t) -> Two(a)
    |          IsValid(a) :: t  -> One(a)
    | _                         -> Nil