我对Haskell完全不熟悉(更常见的是函数式编程),所以请原谅我这是非常基本的东西。为了获得更多的品味,我尝试在Haskell中实现一些我正在研究的算法。我有一个简单的模块Interval
,可以在线上实现间隔。它包含类型
data Interval t = Interval t t
辅助函数
makeInterval :: (Ord t) => t -> t -> Interval t
makeInterval l r | l <= r = Interval l r
| otherwise = error "bad interval"
和一些关于间隔的效用函数。
在这里,我的兴趣在于多维间隔(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]
允许进行不同的健全性检查,例如
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
我现在终于明白了!但是一些功能自然与多个间隔和轨道间隔有关。例如,函数order
将返回多个间隔或轨道间隔中的间隔数。我能做什么?添加
-- Dimensional interval
data DInterval t = MIntervalStuff (MInterval t) | TIntervalStuff (TInterval t)
没有多大帮助,因为,如果我理解得很好(如果我错了就纠正我),我将不得不写
order :: DInterval t -> Int
order (MIntervalStuff (MInterval is)) = length is
order (TIntervalStuff (TInterval is)) = length is
并在order
为order (MIntervalStuff is)
或order (TIntervalStuff is)
时将is
称为MInterval
或TInterval
。不是很好,看起来很奇怪。我都不想复制该函数(我有许多与多个和轨道间隔有关的函数,以及一些其他d区间定义,例如等长多个和轨道间隔)。
我感觉我完全错了,并且错过了关于Haskell类型的一些重要观点(和/或在这里不能忘记OO编程)。所以,这是一个新手问题,Haskell处理这种情况的最佳方法是什么?我是否必须忘记介绍MInterval
和TInterval
并仅使用一种类型?
非常感谢你的帮助,
Garulfo
答案 0 :(得分:5)
编辑:这与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 = ..
此处类型DInterval表示多维间隔,但它将一个额外的类型参数作为幻像类型。这种额外的类型信息允许类型检查器区分不同类型的区间,即使它们具有完全相同的表示。
您可以获得原始设计的类型安全性,但所有结构都共享相同的实现。至关重要的是,当间隔的类型无关紧要时,您可以不加指定。
我还改变了makeMInterval函数的实现;为这样的函数返回Maybe
比调用错误更加惯用。
关于Maybe的更多解释:
让我们检查你的函数makeInterval
。该函数应该采用Interval列表,如果符合条件,则返回多个间隔,否则返回轨道间隔。这种解释导致类型:
makeInterval :: (Ord t) =>
[I.Interval t] ->
Either (DInterval TInterval t) (DInterval MInterval t)
现在我们有了类型,我们如何实现它?我们想重新使用我们的makeMInterval函数。
makeInterval is = maybe
(Left $ DInterval TInterval is)
Right
(makeMInterval is)
函数maybe
有三个参数:如果Maybe是b
则使用默认Nothing
,如果Maybe是a -> b
则使用函数Just a
,和Maybe a
。它返回默认值或将函数应用于Maybe值的结果。
我们的默认值是轨道间隔,因此我们为第一个参数创建一个左轨道间隔。如果可能是Just (DInterval MInterval t)
,则多重间隔已经存在,所以所有必要的是将它粘在两者的右侧。最后,makeMInterval
用于创建多个间隔。
答案 1 :(得分:3)
您想要的是具有相同结构和常用操作但可以区分的类型,并且某些操作仅对一种或另一种类型有意义。 Haskell中的这个成语是Phantom Types。
答案 2 :(得分:3)
你想要的是一个类型类。类型类是在Haskell中完成重载的方式。类型类是由许多类型共享的公共结构。在您的情况下,您想要创建一个具有order
函数的所有类型的类:
class Dimensional a where
order :: a -> Int
这就是说有一类名为a
的{{1}}类型,对于属于这个类的每种类型,都有一个名为Dimensional
的函数a -> Int
。现在,您需要将所有类型声明为属于该类:
order
等等。您还可以通过以下方式声明对来自给定类的内容起作用的函数:
instance Dimensional MInterval where
order (MInterval is) = length is
对于属于类型 isOneDimensional :: (Dimensional a) => a -> Bool
isOneDimensional interval = (order interval == 1)
的所有类型a
,可以读取类型声明,此函数需要Dimensional
并返回a
“。
我喜欢把类看作数学中的代数结构(它与你不能强制执行某些逻辑约束的东西不完全相同,但是......):你说明一个类型属于某些类型的要求是什么class(在类比中,必须在一个集合上定义操作以使其属于某个代数结构),然后,对于每个特定类型,您说明所需函数的具体实现是什么(在类比中,什么是每个特定集合的具体功能)。
以一个小组为例。组必须具有标识,每个元素的反转和乘法:
Bool
和整数组成了一个小组:
class Group a where:
identity :: a
inverse :: a -> a
(<*>) :: a -> a -> a
请注意,这不会解决重复问题,因为您必须实例化每种类型。
答案 3 :(得分:0)
考虑使用类型类来显示不同类型之间的相似性。有关类和重载的信息,请参阅haskell教程[1]以获取帮助。
[1]:http://www.haskell.org/tutorial/classes.html haskell教程