2015-09-15 26 views
6

所以我注意到,在n = 20之后,由于Int类型的有限工作范围,在LearnYouAHaskell(下面)中给出的阶乘函数变成了胡扯。使函数的类型取决于输入

factorial :: Int -> Int 
factorial 0 = 1 
factorial n * factorial (n-1) 

使用factorial :: Integer -> Integer修复很好的问题,但它使我想起这个问题。据推测Integer比Int要慢一些,所以理想情况下(我知道我在这里捏一分钱)我希望我的阶乘函数只在输入大于20时使用Integer,并保留较小数字的Int->Int类型。似乎应该有一个优雅的解决方案,使用if-then-else或守卫,但仍然运行到语法胡椒(错误消息)

回答

11

你可以不依赖类型通过使用一个总和类型并在需要时或延迟投给Integer,直到在某些情况下,最终生长hackish的解决方案。我不认为任何一种解决方案都会比使用Integer更好 - 不要担心Integer,gmp和mpir是相当不错的。

铸造的解决方案是这样的:

selectiveFactorial :: Integer -> Integer 
selectiveFactorial i 
    | i < 20 = fromIntegral $ factorial (fromIntegral i :: Int) 
    | otherwise = factorial i 

factorial :: Integral a => a -> a 
factorial 0 = 1 
factorial n = n * factorial (n - 1) 
+9

在GHC中,我们有['Integer'](http://hackage.haskell.org/package/integer-gmp-1.0.0.0/docs/ GHC-Integer-GMP-Internals.html#t:Integer)恰好是一个和类型,小值使用更高效的表示形式。因此,在上面再增加一笔钱可能会让事情变得更糟。 – chi

+0

我宁愿写'selectiveFactorial :: Int - > Integer; selectiveFactorial i |我<20 = fromIntegral(阶乘i)|否则=阶乘(来自整体i)'。 – user3237465

1

使函数的类型取决于它的值正是什么dependent types是。不幸的是,Haskell没有依赖类型,因此无法完成。

+0

值得一提的是* do *支持依赖类型的语言,如[idris](http://www.idris-lang.org/),它与Haskell非常相似。 – AJFarmar

5

它不能完成。有一些事情可以做,但是:

  1. 使类型更通用:factorial :: Num a => a -> a; 这允许你的函数的用户决定他想要发生什么样的运行时惩罚,以及允许的数字范围。

  2. 使用和类型像data PossiblyBig = Small Int | Big Integer,然后有编码不适合作为Big东西融入IntSmall,事情的实现instance Num PossiblyBig; AFAIK Integer已经可以像这样工作了(如果你想知道的话,请查看GMP实施),所以这是一个通常的例子,而不是建议你在这种特殊情况下应该做什么。

+5

是的,你所描述的是Integer是如何实现的。 “Int”范围内的任何值都以与“Int”相同的方式存储,该范围之外的值将其数据存储在“ByteArray”字段中。在顶部添加另一个相同类型的图层可能不会有帮助:) –

7

由于Rein Henrichs said你可以与相关的类型语言,这Haskell没有(还没有,挺)有做这些事情。在伊德里斯说,它看起来像

factorial : (n : Int) -> if n <= 20 then Int else Integer 
factorial n with (n <= 20) 
    factorial n | True = thisfactorial n 
    factorial n | False = thatfactorial n 

但你怎么会使用这个结果?那么,你需要进行比较以找出期望的类型,并且当所有事情都说完之后,我不明白你是如何赢得任何东西的。为了完整起见,使用网站可能是这个样子:

use : Int -> Integer 
use n with (factorial n) 
    use n | fn with (n <= 20) 
    use n | fn | False = fn 
    use n | fn | True = cast fn 

注意,with条款的顺序是显著! fn绑定获取类型if n <= 20 then Int else Integer;由于我不完全理解的原因,n <= 20测试必须在右侧,以便模式匹配影响其类型。

+0

Haskell中的[same](http://lpaste.net/141109)with singletons(仅在我使用':=='和'%:'替换':<='和'%:<='时适用于我: ==')。 – user3237465

+0

你可能会返回'Int Integer',但我猜'Integer'已经是'Int Int BigInt'了...... – mb14

相关问题