我想加强一个模式,只匹配通过额外验证功能的数字。
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被施加两次。
另请注意,这是我实际代码的简化版本(例如,我不是要简单地匹配不同长度的列表) - 问题是关于双重缺点模式的嵌套匹配。
答案 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