data Foo = Bar1
| Bar2 Foo Foo
| Bar3 Foo
| Bar4 Foo Foo Foo
现在,假设有人建立了一个
Foo
树,我想检查Foo值的参数是否有效。构造函数参数的规则是:Bar2 Bar1 Foo
Bar3 (Bar2|Bar3|Bar4)
Bar4 Bar1 Bar1 (Bar1|Bar4)
我知道值的构造函数,只想检查立即参数,没有递归的方法。像这样:
bar2 Bar1 Bar1 = True
bar2 Bar1 (Bar2 {}) = True
bar2 Bar1 (Bar3 _) = True
bar2 Bar1 (Bar4 {}) = True
bar2 _ _ = False
和例如对于Bar4同样:
bar4 Bar1 Bar1 Bar1 = True
bar4 Bar1 Bar1 (Bar4 {}) = True
bar4 _ _ _ = False
如何最简洁地表达这些条件?列出所有组合在某些情况下有点过多。据我所知,不存在用于模式匹配的“ OR”语法。
更新
我改编了Daniel的解决方案,并得出以下结论:
data Foo = Bar1
| Bar2 Foo Foo
| Bar3 Foo
| Bar4 Foo Foo Foo
deriving (Data, Typeable)
bar2 a b = a `isOf` [Bar1] && b `isOf` [Bar1,Bar2{},Bar3{},Bar4{}]
bar4 a b c = [a,b] `areOf` [Bar1] && c `isOf` [Bar1,Bar4{}]
isOf l r = toConstr l `elem` map toConstr r
areOf l r = all (`isOf` r) l
我喜欢的是,除了添加派生子句外,我不必更改数据类型,并且它是可读的。当然,缺点是这些是动态检查。就我而言,这很好,因为它仅用于类似断言的检查以发现编程错误。
最佳答案
有一个很好的静态检查解决方案;这是一个动态检查解决方案的建议。关键思想是避免使用模式匹配,而使用我们拥有的所有工具来编写紧凑的代码。这样做有两种选择。我将讨论两个。首先是为每个构造函数编写isBarX
函数:
isBar1 (Bar1 {}) = True
isBar1 _ = False
-- ...
isBar4 (Bar4 {}) = True
isBar4 _ = False
valid (Bar1) = True
valid (Bar2 a b) = isBar1 a
valid (Bar3 a) = not (isBar1 a)
valid (Bar4 a b c) = isBar1 a && isBar1 b && (isBar1 c || isBar4 c)
另一个选择是编写一个函数,该函数返回一些数据,以告诉使用了哪个构造函数-例如,像
data Constructor = CBar1 | CBar2 | CBar3 | CBar4
这样的自定义类型,或者像我下面将要介绍的那样,像Int
这样的小技巧。constr (Bar1 {}) = 1
constr (Bar2 {}) = 2
constr (Bar3 {}) = 3
constr (Bar4 {}) = 4
valid (Bar1) = True
valid (Bar2 a b) = constr a == 1
valid (Bar3 a) = constr a /= 1
valid (Bar4 a b c) = constr a == 1 && constr b == 1 && constr c `elem` [1,4]