我在Haskell玩弄玩具项目。我之前实现了一些我用其他语言构建的数据结构,以熟悉它们是如何在Haskell中构建的。这不是我的第一个函数式语言,我已经在OCaml中构建了一些像Scheme解释器这样的项目,但我认为我的OCaml经验正在着色我如何处理这个问题。它'不是非常重要,但可能对上下文有用,要知道我实施的数据结构是PR-Quadtree。
我想要做的是匹配和解构一个守卫内部的类型,一个与OCaml的匹配声明。
data Waypoint = WayPoint {
lat :: Float,
lon :: Float,
radius :: Float,
speed :: Float,
accel :: Float
} deriving (Show)
data Region = Region {
x :: Float,
y :: Float,
width :: Float
} deriving (Show)
data PRQuadtree = WhiteNode Region
| BlackNode Region Waypoint
| GreyNode {
topLeft :: PRQuadtree,
topRight :: PRQuadtree,
botLeft :: PRQuadtree,
botRight :: PRQuadtree,
region :: Region
} deriving (Show)
getRegion node
| BlackNode(r, _) = r
| WhiteNode(r) = r
| GreyNode = region node
getRegion
函数是我遇到问题的函数。如果我试图做的事情不清楚:我想简单地提取参数的一个元素,但这取决于参数的代数数据类型的哪个成员。在OCaml中,我可以这样做:
let getRegion node = match node with
| BlackNode(r, _) = r
| WhiteNode(r) = r
| GreyNode = region(node)
(或者类似的东西,我的OCaml现在有点生疏了。)
然而,在Haskell中,这似乎不会在保护分支的RHS范围内绑定r
。我试图查找Pattern Guards,因为它们听起来和我想做的一样,但我真的不知道这里发生了什么。真的,我只是希望得到从LHS的LHS到等于的RHS的绑定(取决于我们已经下降的后卫的哪个分支)。
什么是惯用的Haskell做我想做的事情?
答案 0 :(得分:6)
可以通过以下方式实现:
getRegion :: PRQuadtree -> Region
getRegion (BlackNode r _) = r
getRegion (WhiteNode r) = r
getRegion GreyNode{region=r} = r
或甚至
getRegion :: PRQuadtree -> Region
getRegion x = case x of
BlackNode r _ -> r
WhiteNode r -> r
GreyNode{} -> region x
在Haskell中,预先设置类型签名是非常惯用的。
另一种选择是将region
字段扩展到其他情况:
data PRQuadtree = WhiteNode { region :: Region }
| BlackNode { region :: Region , waypoint :: Waypoint }
| GreyNode {
topLeft :: PRQuadtree,
topRight :: PRQuadtree,
botLeft :: PRQuadtree,
botRight :: PRQuadtree,
region :: Region
} deriving (Show)
现在,region
将适用于所有PRQuadtree
值。
Haskell在定义代数数据类型时使用|
来分隔不同的构造函数,但不使用它来分隔case
分支,而是使用语法
case .. of { pat1 -> e1 ; pat2 -> e2 ; ... }
可以用缩进替换
case .. of
pat1 -> e1
pat2 -> e2
...
另请注意,不鼓励使用部分字段选择器:
data A = A1 { foo :: Int } | A2
上面,foo A2
类型检查但崩溃。另一方面,当所有构造函数中都存在字段时,我们不会面临这样的风险。
答案 1 :(得分:3)
你也可以写:
getRegion x
| BlackNode y <- x -> ....
| Greynode{} <- x -> ....
但在这个简单的案例中它是非常单一的。
但是,在更复杂的程序中,这种防护模式匹配非常有用。您可以使用多个方程式或大小写来区分一般情况,如@chi所示。但是,您可以检测特殊情况,例如以下组成的示例:
getRegion x = case x of
BlackNode{region}
| [(0,_)] <- filter (inRegion region) interestingPoints
-> -- region encloses exactly 1 interesting point on x axis
....
| otherwise = ....
where
interestingPoints = .....
inRegion :: Region -> Point -> Bool
GreyNode{} -> ....