我是Haskell的新手(更常见的是函数式编程),所以请原谅我这是非常基本的东西.为了获得更多的品味,我尝试在Haskell中实现一些我正在研究的算法.我有一个简单的模块Interval,在线上实现间隔.它包含类型
data Interval t = Interval t t
Run Code Online (Sandbox Code Playgroud)
辅助功能
makeInterval :: (Ord t) => t -> t -> Interval t
makeInterval l r | l <= r = Interval l r
| otherwise = error "bad interval"
Run Code Online (Sandbox Code Playgroud)
和一些关于间隔的效用函数.
在这里,我的兴趣在于多维间隔(d间隔),那些由d间隔组成的对象.我想分别考虑d-区间,它是线上d个不相交区间(多个区间)与d个单独线条(轨道区间)上d区间的并集的联合.考虑到不同的算法处理,我认为有两种不同的类型(即使两者都是这里的间隔列表)会很好,例如
import qualified Interval as I
-- Multilple interval
newtype MInterval t = MInterval [I.Interval t]
-- Track interval
newtype TInterval t = TInterval [I.Interval t]
Run Code Online (Sandbox Code Playgroud)
允许进行不同的健全性检查,例如
makeMInterval :: (Ord t) => [I.Interval t] -> MInterval t
makeMInterval is = if foldr (&&) True [I.precedes i i' | (i, i') <- zip is (tail is)]
then (MInterval is)
else error "bad multiple interval"
makeTInterval :: (Ord t) => [I.Interval t] -> TInterval t
makeTInterval = TInterval
Run Code Online (Sandbox Code Playgroud)
我现在终于明白了!但是一些功能自然与多个间隔和轨道间隔有关.例如,函数order将返回多个间隔或轨道间隔中的间隔数.我能做什么?添加
-- Dimensional interval
data DInterval t = MIntervalStuff (MInterval t) | TIntervalStuff (TInterval t)
Run Code Online (Sandbox Code Playgroud)
并没有多大帮助,因为,如果我理解得很好(如果我错了,请纠正我),我必须写
order :: DInterval t -> Int
order (MIntervalStuff (MInterval is)) = length is
order (TIntervalStuff (TInterval is)) = length is
Run Code Online (Sandbox Code Playgroud)
并呼吁order作为order (MIntervalStuff is)或order (TIntervalStuff is)当is是一个MInterval或一个TInterval.不是很好,看起来很奇怪.我都不想复制该函数(我有许多与多个和轨道间隔有关的函数,以及一些其他d区间定义,例如等长多个和轨道间隔).
我感觉我完全错了,并且错过了关于Haskell类型的一些重要观点(和/或在这里不能忘记OO编程).所以,这是一个新手问题,Haskell处理这种情况的最佳方法是什么?我是否必须忘记介绍MInterval并TInterval仅使用一种类型?
非常感谢你的帮助,
Garulfo
编辑:这与sclv的回答是一样的; 他的链接提供了有关此技术的更多信息.
这种方法怎么样?
data MInterval = MInterval --multiple interval
data TInterval = TInterval --track interval
data DInterval s t = DInterval [I.Interval t]
makeMInterval :: (Ord t) => [I.Interval t] -> Maybe (DInterval MInterval t)
makeMInterval is = if foldr (&&) True [I.precedes i i' | (i, i') <- zip is (tail is)]
then Just (DInterval is)
else Nothing
order :: DInterval s t -> Int
order (DInterval is) = length is
equalOrder :: DInterval s1 t -> DInterval s2 t -> Bool
equalOrder i1 i2 = order i1 == order i2
addToMInterval :: DInterval MInterval t -> Interval t -> Maybe (DInterval MInterval t)
addToMInterval = ..
Run Code Online (Sandbox Code Playgroud)
这里的类型DInterval表示多维间隔,但它需要一个额外的类型参数作为幻像类型.这种额外的类型信息允许类型检查器区分不同类型的间隔,即使它们具有完全相同的表示.
您可以获得原始设计的类型安全性,但所有结构都共享相同的实现.至关重要的是,当间隔的类型无关紧要时,您可以不指定它.
我还改变了makeMInterval函数的实现; Maybe为这样的函数返回一个比调用错误更惯用的东西.
关于Maybe的更多解释:
让我们检查一下你的功能makeInterval.该函数应该采用Interval列表,如果符合条件,则返回多个间隔,否则返回轨道间隔.这种解释导致类型:
makeInterval :: (Ord t) =>
[I.Interval t] ->
Either (DInterval TInterval t) (DInterval MInterval t)
Run Code Online (Sandbox Code Playgroud)
现在我们有了类型,我们如何实现呢?我们想重新使用我们的makeMInterval函数.
makeInterval is = maybe
(Left $ DInterval TInterval is)
Right
(makeMInterval is)
Run Code Online (Sandbox Code Playgroud)
该函数maybe有三个参数:默认的b使用,如果可能就是Nothing,一个函数a -> b如果也许就是Just a和Maybe a.它返回默认值或将函数应用于Maybe值的结果.
我们的默认值是轨道间隔,因此我们为第一个参数创建一个左轨道间隔.如果可能是Just (DInterval MInterval t),则多重间隔已经存在,所以所有必要的是将它粘在两者的右侧.最后,makeMInterval用于创建多个间隔.
考虑使用类型类来显示不同类型之间的相似性。有关类和重载的帮助,请参阅 haskell 教程 [1]。
[1]:http://www.haskell.org/tutorial/classes.html haskell教程