我想写一个函数来判断一个给定的整数是否为素数,我应该使用类型签名?
isPrime :: Int -> Bool
或
isPrime :: (Integral a) => a -> Bool
区别是什么?是否有特别的理由选择其中一个?
如果是这样,我应该在哪种情况下分别使用这两种?
答案 0 :(得分:11)
类型Int -> Bool
表示您的函数对Int
类型的值进行操作,这些值是大小限制的整数(我相信,最大大小取决于机器)。
类型(Integral a) => a -> Bool
表示您的函数对任何类型的值进行操作,该类型具有Integral
类型类的实例 - 即表现的类型喜欢以特定方式整数。在具体类型上选择它的主要原因是创建一个更通用的功能。
当您需要在其他上下文中使用类似整数的类型时,使用Integral
的通用表单最有用 - 一个很好的示例是标准库失败的地方所以,例如像replicate :: Int -> a -> [a]
这样的函数。对于某些特定类似整数类型的代码,为了自己的目的,希望将该类型与replicate
一起使用,因此需要首先转换为Int
,或者从{{1}导入genericReplicate
}。
在您的情况下,您可能需要考虑的是类型Data.List
,它表示任意大小的整数。由于您的主要目标是计算,因此支持任意整数类型的价值较小。
如果内存为我服务,标准库中Integer
的唯一实例无论如何都是Integral
和Int
。 (编辑:正如Hammar在评论中提醒我的那样,Integer
和Data.Int
中也存在固定大小类型的实例。还有{{1}等外来类型但我故意无视这些。)
答案 1 :(得分:4)
我会推荐签名
isPrime :: Integer -> Bool
签名isPrime :: Int -> Bool
将排除对大数字的快速测试,因为那些测试通常会溢出(技术上也是Integer
也是如此,至少在integer-gmp
提供的版本中,但在重要之前你很可能会记忆 long ,所以我们可以保持无限Integer
类型的虚构。
类型isPrime :: Integral a => a -> Bool
将是任何可行实现的谎言。对于类型建模Integral
,可以有一个Z[sqrt(2)]
的实例(虽然对于这样的类型toInteger
会不忠实),对于这样的类型,2不会是素数,怎么会一个用通用测试检测到它?
或者考虑对因子环Z/(n)
建模的有限类型。此类型的Ord
实例与算术不兼容,但我们已经为Int
等提供了此类实例。例如,在Z(6) = {0,1,2,3,4,5}
中,primes将为2, 3和4(注意,它们都不是irreducible,2 = 4 * 2,3 = 3 * 3,4 = 2 * 2)。
所以唯一有意义且可行的测试是,#34;它是一个理性(或自然)素数,它的值恰好在类型的范围内?"。在isPrime :: Integer -> Bool
类型中捕获(尽可能好,而不牺牲太多速度),在适当的时候与toInteger
组合。
答案 2 :(得分:1)
许多素性测试都是概率性的并且需要随机数,所以至少在最低级别他们会有这样的类型签名:
seemsPrime :: (Integral a) => a -> [a] -> Bool
Integral约束似乎是合理的,因为通常你不需要具体类型,只需要rem
之类的操作。