Подтвердить что ты не робот

Существует ли практический способ использования натуральных чисел в Haskell?

Я изучаю Haskell и хотел бы навязывать использование положительных целых чисел (1,2,3,...) в некоторых конструкторах, но я только, кажется, нахожу типы данных "Int" и "Integer".

Я мог бы использовать канонический

data Nat = Zero | Succ Nat

но тогда я не мог использовать 1, 4,... для их обозначения.

Итак, я спрашиваю, есть ли способ сделать это? (что похоже на использование "unsigned" в C)

Спасибо заранее.

EDIT: Я собираюсь спрятать его внутри модуля, как объяснил К. А. Макканн. Кроме того, я должен добавить следующую ссылку: http://haskell.org/haskellwiki/Smart_constructors для резюме по этому вопросу. Спасибо, что нашли время ответить!

4b9b3361

Ответ 1

В целом для этого существуют два подхода: индуктивное определение, которое вы дали, или абстрактный тип данных с использованием чего-то другого для внутреннего представления.

Заметим, что индуктивное представление не очень эффективно для больших чисел; однако он может быть ленивым, что позволяет вам делать такие вещи, как видеть, какая из двух nats больше, не оценивая больше, чем размер меньшего.

Абстрактный тип данных - это тот, который определен в отдельном модуле и не экспортирует его конструкторы, примеры IO или Data.Set.Set. Вы можете определить что-то вроде этого:

module Nat (Nat() {- etc. -} ) where

newtype Nat = Nat { unNat :: Integer }

... где вы экспортируете различные операции на Nat, так что, хотя внутреннее представление просто Integer, вы гарантируете, что значение типа Nat не построено с отрицательным значением.

В обоих случаях, если вы хотите использовать числовые литералы, вам понадобится определение fromInteger, которое привязано к классу типа Num, что совершенно неверно для натуральных чисел, но хорошо.

Если вы не возражаете сделать сломанный экземпляр, чтобы получить синтаксические тонкости, вы можете сделать что-то вроде этого:

instance Num Nat where
    Zero + n = n
    n + Zero = n
    (Succ n1) + (Succ n2) = Succ . Succ $ n1 + n2

    fromInteger 0 = Zero
    fromInteger i | i > 0 = Succ . fromInteger $ i - 1

... и т.д. для других функций. То же самое можно сделать для подхода абстрактного типа данных, просто будьте осторожны, чтобы не использовать deriving для получения автоматического экземпляра Num, потому что он с радостью нарушит ваше неотрицательное ограничение.

Ответ 2

Вы можете использовать Word32 из Data.Word, что соответствует uint32_t в C.

С Word32 вы получаете те же проблемы, что и с неподписанными типами в C, особенно over- и underflow. Если вы хотите убедиться, что этого не произойдет, вам нужно привязать его к новому типу и только экспортировать интеллектуальный конструктор. Таким образом, никакое сложение, вычитание и т.д. Не было бы возможным, и не было бы риска чрезмерного или недостаточного. Если вы хотите поддержать добавление, например, вы можете добавить и экспортировать функцию для добавления неподписанных ints, но с проверкой на переполнение (и с ограничением производительности). Он может выглядеть следующим образом:

module NT(UInt, addUInts) where

import Data.Word

newtype UInt = UInt Word32
  deriving (Show)

mkUInt :: Word32 -> UInt
mkUInt = UInt

addUInts :: UInt -> UInt -> Maybe UInt
addUInts (UInt u1) (UInt u2) =
  let u64 :: Word64
      u64 = fromIntegral u1 + fromIntegral u2
  in if u64 > fromIntegral (maxBound :: Word32)
       then Nothing
       else Just (UInt (fromIntegral u64))

Ответ 3

Я не могу вспомнить, относится ли это к вашему конкретному вопросу, но вам может понравиться статья Колина Рунсимана А как насчет натуральных чисел?. В случае, если вы не можете преодолеть платный сервер, на Citeseer существует .