Экземпляр Enum для кортежей в Haskell - PullRequest
1 голос
/ 01 апреля 2012

Я бы хотел определить кортеж (x, y) как экземпляр класса Enum, зная, что и x, и y являются экземплярами Enum. Следующая попытка:

instance (Enum x, Enum y) => Enum (x, y) where
    toEnum = y
    enumFrom x = (x, x)

только приводит к ошибке (y не входит в объем). Я новичок в Haskell, может кто-нибудь объяснить, как объявить такой экземпляр?

Ответы [ 2 ]

5 голосов
/ 02 апреля 2012
instance (Enum x, Enum y) => Enum (x, y) where

В приведенной выше строке x и y оба являются типами (переменные типа).

    toEnum = y
    enumFrom x = (x, x)

В приведенных выше двух строках x и y являются значениями ((значениями) переменных). y -as-a-value нигде не определено, это означает, что оно не находится в области видимости.

Что касается того, как объявить такой экземпляр, я не уверен, например, как бы вы хотели, чтобы fromEnum и toEnum вели себя.

2 голосов
/ 02 апреля 2012

Не очень хорошая идея, если вы спросите меня, но в любом случае -

Чтобы создать экземпляр класса типов, вам нужно посмотреть на сигнатуры.

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]

Так что в вашемcase

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 ]

Чтение комментария Дитриха Эппа

На самом деле невозможно создать полезные Enum (x, y) из Enum x и Enum 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 все остальные функции довольно тривиальны.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...