1

我想加强一种模式以仅匹配通过附加验证功能的数字。

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

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

'One' 的情况很简单:

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

“二”的情况对我来说并不明显。它需要验证数字的总和。我可以在不使用 when-guard 的情况下做到这一点吗?

...

编辑:我可以像这样使用 when-guard(带有返回布尔值的 isValid 函数):

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

这不如仅仅匹配一个模式那么优雅;更糟糕的是,a + b 被应用了两次。

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

4

2 回答 2

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 在第一个模式上为假,则第二个模式将匹配长度大于一个的列表 - 被警告。在你的模式中尽可能具体,如果你想匹配一个元素,那就去做。

如果您用于组合 a 和 b(在本例中为 +)的任何操作在计算上都很昂贵,那么您必须when在测试 isValid 并返回变体类型之前删除并使用 let 语句。

于 2009-01-07T16:02:31.340 回答
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
于 2009-01-09T16:27:52.043 回答