在Haskell中有使用自然数的实用方法吗?
我正在学习Haskell,希望在一些构造函数中强制使用正整数(1,2,3,…),但我似乎只找到“Int”和“Integer”数据类型 我可以用经典的在Haskell中有使用自然数的实用方法吗?,haskell,types,integer,unsigned,Haskell,Types,Integer,Unsigned,我正在学习Haskell,希望在一些构造函数中强制使用正整数(1,2,3,…),但我似乎只找到“Int”和“Integer”数据类型 我可以用经典的 data Nat = Zero | Succ Nat 但是我不能用1,4。。。表示它们 所以我问,有没有办法做到这一点?(这类似于在C中使用“unsigned”) 提前谢谢 编辑:正如C.a.McCann所解释的,我将把它隐藏在一个模块中。此外,我还必须添加以下链接:以获取有关该主题的摘要。感谢您抽出时间回答 您可以从中使用Word32,它对应于
data Nat = Zero | Succ Nat
但是我不能用1,4。。。表示它们
所以我问,有没有办法做到这一点?(这类似于在C中使用“unsigned”)
提前谢谢
编辑:正如C.a.McCann所解释的,我将把它隐藏在一个模块中。此外,我还必须添加以下链接:以获取有关该主题的摘要。感谢您抽出时间回答 您可以从中使用Word32,它对应于C中的uint32\t
使用Word32,您会遇到与C中的无符号类型相同的问题,尤其是上溢和下溢。如果您想确保不会发生这种情况,您需要将其包装为一个newtype,并且只导出一个智能构造函数。因此,不可能进行加法、减法等运算,也不存在过流或下流的风险。例如,如果您想支持加法,您可以添加和导出一个用于添加无符号整数的函数,但要检查溢出情况(以及性能损失)。然后它可能看起来像这样:
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))
module Nat (Nat() {- etc. -} ) where
newtype Nat = Nat { unNat :: Integer }
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
通常有两种方法:您给出的归纳定义,或使用其他内容作为内部表示的抽象数据类型 请注意,归纳表示法对于大数不是非常有效;但是,它可能是懒惰的,这让您可以做一些事情,比如查看两个NAT中的哪一个更大,而无需进一步评估较小NAT的大小 抽象数据类型是在单独的模块中定义的,不导出其构造函数,例如
IO
或data.Set.Set
。您可以这样定义:
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))
module Nat (Nat() {- etc. -} ) where
newtype Nat = Nat { unNat :: Integer }
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
…在这里,您可以导出对Nat
的各种操作,这样,即使内部表示形式只是Integer
,也可以确保构造的Nat
类型的值中没有包含负值
在这两种情况下,如果您想使用数字文字,您将需要一个fromInteger
的定义,它附加到Num
类型类,这对于自然数来说是完全错误的,但是很好
如果您不介意创建一个断开的实例来获得语法细节,您可以这样做:
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))
module Nat (Nat() {- etc. -} ) where
newtype Nat = Nat { unNat :: Integer }
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
…等等,用于其他功能。对于抽象数据类型方法也可以这样做,只是小心不要使用
派生
来获得一个自动的Num
实例,因为它会很高兴地打破您的非负约束。我不记得它是否解决了您的特定问题,但您可能会喜欢Colin Runciman的论文。如果你不能越过付费墙,那似乎有一个问题。我还是喜欢你的方式。虽然你不能使用这种甜言蜜语的语法,但你仍然可以使用常规的旧函数来生成列表。我不喜欢这样,但是我猜没有其他选择了,对吗?映射-1
到sz
的fromInteger实现不是更有用吗?