如果你问我,这不是一个好主意,但无论如何——
要创建类型类的实例,您需要查看签名。
class Enum a where
succ :: a -> a
pred :: a -> a
toEnum :: Int -> a
fromEnum :: a -> Int
enumFrom :: a -> [a]
enumFromThen :: a -> a -> [a]
enumFromTo :: a -> a -> [a]
enumFromThenTo :: a -> a -> a -> [a]
所以你的情况
toEnum :: Int -> (x, y)
但是toEnum = y 甚至没有被定义,因为y 只是一个类型,而不是一个值或构造函数。可能性是
toEnum n = (toEnum 0, toEnum n)
或
toEnum n = (toEnum n, toEnum n)
或
toEnum n = (toEnum $ n`div`2, toEnum $ (n+1)`div`2)
至于enumFrom,你的版本有签名
enumFrom :: a -> (a,a)
但我们需要
enumFrom :: (x,y) -> [(x,y)]
什么定义合适取决于toEnum是如何定义的;我的第一个建议是
enumFrom (x,y) = [ (x,y') | y' <- enumFrom y ]
阅读 Dietrich Epp 的评论
实际上不可能从Enum x 和Enum y 创建一个有用的Enum (x, y)。您需要额外的上下文,例如 Bounded x, Bounded y, Enum x, Enum y => Enum (x, y)。
我想了可以真正有意义地完成它的方式。似乎确实有可能存在双射ℤ → ℤ2。我的建议:
[ ...
, (-3,-3), (-3,-2), (-2,-3), (-3,-1), (-1,-3), (-3,0), (0,-3), (-3,1), (1,-3), (-3,2), (2,-3), (-3,3), (3,-3)
, (-2,3), (3,-2), (-1,3), (3,-1)
, (-2,-2), (-2,-1), (-1,-2), (-2,0), (0,-2), (-2,1), (1,-2), (-2,2), (2,-2)
, (-1,2), (2,-1)
, (-1,-1), (-1,0), (0,-1), (-1,1), (1,-1)
, (0,0)
, (1,0), (0,1), (1,1)
, (2,0), (0,2), (2,1), (1,2), (2,2)
, (3,0), (0,3), (3,1), (1,3), (3,2), (2,3), (3,3)
, ... ]
请注意,这也可以简化为双射 ℕ → ℕ2,这很重要,因为一些 Enum 实例不会进入负范围,而其他实例会。
实施:
让我们创建一个普通的(Int,Int) 实例;很容易将其概括为您想要的。另外,我只会处理阳性病例。
观察(0,0) 和(不包括)(k,0) 之间存在k^2 元组。所有其他元组(x,y) 和max x y == k 直接跟在它后面。这样,我们就可以定义fromEnum:
fromEnum (x,y) = k^2 + 2*j + if permuted then 1 else 0
where k = max x y
j = min x y
permuted = y>x
对于toEnum,我们需要找到这个函数的逆函数,即知道fromEnum -> n,我们想知道参数。 k 很容易计算为floor . sqrt $ fromIntegral n。 j 的获得方式类似,只是用余数的div 2。
toEnum n = let k = floor . sqrt $ fromIntegral n
(j, permdAdd) = (n-k^2) `divMod` 2
permute (x,y) | permdAdd>0 = (y,x)
| otherwise = (x,y)
in permute (k,j)
使用fromEnum 和toEnum,所有其他功能都相当简单。