我在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中,这似乎不在r
守卫分支的RHS范围内.我试图查找Pattern Guards,因为它们听起来像我想做的那样,但我真的不知道这里发生了什么.真的,我只是希望得到从LHS的LHS到等于RHS的绑定(取决于我们已经下降的后卫的哪个分支).
什么是惯用的Haskell做我想做的事情?
它可以实现如下:
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 |
在定义代数数据类型时使用ML,分隔不同的构造函数,但不使用它来分离case
分支,而是使用语法
case .. of { pat1 -> e1 ; pat2 -> e2 ; ... }
可以用缩进代替
case .. of pat1 -> e1 pat2 -> e2 ...
另请注意,不鼓励使用部分字段选择器:
data A = A1 { foo :: Int } | A2
在上面,foo A2
键入检查但崩溃.另一方面,当所有构造函数中都存在字段时,我们不会面临这样的风险.