2015-09-15 21 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

相關問題