这是我的问题here的具体版本。我有一个算法,该算法可以产生一些输出,并且可以产生一些辅助信息,在给定的情况下,我可能会或可能不会关心这些信息(我在下文中将此辅助信息称为“注释”)。我正在使用Euclid算法进行说明:
class AnnotatedGcd m where
actually :: (Integral a) => a -> m a
update :: (Integral a) => a -> m a -> m a
newtype GcdOnly a = Gcd a deriving Show
instance AnnotatedGcd GcdOnly where
actually = Gcd
update q = id
newtype GcdWithSteps a = GcdS (a, Int) deriving Show
instance AnnotatedGcd GcdWithSteps where
actually x = GcdS (x, 0)
update q (GcdS (x, s)) = GcdS (x, s+1)
newtype GcdExtended a = GcdE (a, a, a) deriving Show
instance AnnotatedGcd GcdExtended where
actually x = GcdE (x, 1, 0)
update q (GcdE (x, k, l)) = (GcdE (x, l, k - q*l))
gcd :: (Integral a, AnnotatedGcd m) => a -> a -> m a
gcd a b = if b == 0 then actually a else update q ((Main.gcd) b r)
where q = a `div` b
r = a `rem` b
函数gcd a b
返回一个AnnotatedGcd
,在示例中可以是GcdOnly
,它仅仅是gcd g,或者可以是GcdWithSteps
,另外还要计算多少步花费了,或者也可能GcdExtended
还提供了系数k,l,使得g = k * a + l * b:
*Main> (Main.gcd 253 83) :: (GcdOnly Int)
Gcd 1
*Main> (Main.gcd 253 83) :: (GcdWithSteps Int)
GcdS (1,4)
*Main> (Main.gcd 253 83) :: (GcdExtended Int)
GcdE (1,21,-64)
现在,如果我想计算步数并获得系数怎么办?与其继续写越来越多的AnnotatedGcd
实例,不如我想有一个注释的概念,并说注释的元组再次是注释:
class GcdAnnotation a where
emptyAnnotation :: a
annotate :: Integral b => b -> a -> a
instance (GcdAnnotation a, GcdAnnotation b) => GcdAnnotation (a,b) where
emptyAnnotation = (emptyAnnotation, emptyAnnotation)
annotate q (x, y) = (annotate q x, annotate q y)
instance (GcdAnnotation a, GcdAnnotation b, GcdAnnotation c) => GcdAnnotation (a,b,c) where
emptyAnnotation = (emptyAnnotation, emptyAnnotation, emptyAnnotation)
annotate q (x, y, z) = (annotate q x, annotate q y, annotate q z)
instance GcdAnnotation b => AnnotatedGcd (,b) where
actually x = (x, emptyAnnotation)
update q (g, x) = (g, annotate q x)
这是有效的Haskell代码,但最后一个实例声明除外。问题是:一个人怎么才能真正发表这样的声明?
通用版本为:给定两个参数类Class a
和Class b
,如何将元组(a,b)
变成两个类的实例(一个关于第一个参数,一个关于尊重第二个。