3

我在Haskell中實現了一個二進制到十進制的函數,目前我正在開發一個將十進制轉換爲二進制值的函數。 (我知道這些功能在某些地方是可用的,雖然它們不是Prelude.hs的一部分)如何在Haskell中實現十進制到二進制函數

我想出了下面的C語言程序代碼,但是我很難適應它的功能範例。

while (n > 0) 
{ 
    if (n % 2 == 1) 
     str = str + "1"; 
    else 
     str = str + "0"; 
    n = n/2; 
} 

我剛剛冒險進入Haskell的函數式編程,所以我對功能思維方式很陌生。我試圖使用遞歸和列表理解,但我不確定如何正確放置守衛和邏輯,因爲這涉及多個條件。我使用一個Int列表來保存單獨的二進制位。

--Decimal to binary 
toBin:: Int -> [Int] 
toBin 0 = [0] 
toBin n | (n % 2 == 1) = 
     |(n % 2 == 0) = 

我明白了,上面的模式可以讓程序選擇保護和結束評估函數。我在這裏錯了嗎?

下面是我想出原始遞歸來將任何基數(小於10,代替2)轉換爲小數。

toDecimal :: [Int] -> Int 
toDecimal [] = 0 
toDecimal (x:xs) = (x * 2 ^(length xs)) + bin xs 

謝謝先進。

回答

14

有沒有%操作;你可能會尋找替代`mod`

toBin 0 = [0] 
toBin n | n `mod` 2 == 1 = ... 
     | n `mod` 2 == 0 = ... 

逆天讓你的函數的多個分支之間進行選擇。在這種情況下,如果其相應條件爲真,則每個...將是toBin n的結果。爲了兩個列表追加在一起,你可以使用++運營商,並`div`對應於整數除法:

toBin 0 = [0] 
toBin n | n `mod` 2 == 1 = toBin (n `div` 2) ++ [1] 
     | n `mod` 2 == 0 = toBin (n `div` 2) ++ [0] 

然而,這有幾個問題。首先,它始終以0開始,這是多餘的;另外,使用++ [1]的速度很慢,因爲它必須通過整個列表來添加一個元素到最後;最好是預先每個元素,因爲我們去,然後反向結果在最後。

要解決這兩個東西,我們會達到分裂toBin爲主要功能和輔助功能:

toBin 0 = [0] 
toBin n = reverse (helper n) 

helper 0 = [] 
helper n | n `mod` 2 == 1 = 1 : helper (n `div` 2) 
     | n `mod` 2 == 0 = 0 : helper (n `div` 2) 

在這個版本中,我們使用:運營商,需要一個值和一個列表,並返回帶有預置在開頭的值的列表。我們還會在我們的幫助器中返回0的空結果,並在toBin中處理0的情況,以便結果中不再有0。

我們可以完全跳過警衛,因爲我們只寫一遍的n `mod` 2結果的右側簡化helper的代碼:

helper 0 = [] 
helper n = (n `mod` 2) : helper (n `div` 2) 

最後,還有,做了div和功能一個在一個mod去,從而可以更高效:

helper 0 = [] 
helper n = let (q,r) = n `divMod` 2 in r : helper q 

作爲附加的註釋,這並不能真正十進制轉換爲二進制,將其轉換爲二進制的整數; Haskell實現不太可能以十進制格式存儲整數,儘管它們是以該格式書寫和打印的。要編寫十進制到二進制的完整轉換,需要一個將十進制字符串解析爲整數的函數。

+1

「需要將一個十進制字符串解析爲一個整數的函數」 - 就像,erm,'read'? – 2012-02-06 20:02:06

+0

@DanielFischer:是的,但大概是OP試圖從頭開始實現這一點:)畢竟,'showIntAtBase'也存在。 – ehird 2012-02-06 20:04:22

2
toBin :: Int -> [Int] 
toBin 0 = [0] 
toBin 1 = [1] 
toBin n 
    | n `mod` 2 == 0 = toBin (n `div` 2) ++ [0] 
    | otherwise = toBin (n `div` 2) ++ [1] 

0和1是微不足道的情況。 如果n是偶數,那麼你應該追加零到最後,否則你追加一個。 這是很好的做法在你的最後守衛表達式捕捉一切(這就是爲什麼我使用,否則這是一樣的真)

4
toBinary :: Int -> [ Int ] 

toBinary 0 = [ 0 ] 

toBinary n = toBinary (n `quot` 2) ++ [ n `rem` 2 ] 
0

您可以從Data.Char使用unfoldr函數從Data.List模塊和intToDigit

dec2bin :: Int -> [Char] 
dec2bin = reverse . map intToDigit . unfoldr (\x -> if x==0 then Nothing else Just(rem x 2, div x 2)) 

這裏發生的是unfoldr功能過程與所提供的匿名函數的輸入,直到它返回Nothing ,同時將第一個值Just彙總在一個列表中。此列表包含Int s,因此需要使用intToDigit將其轉換爲Char,然後進行反轉,因爲它們是按相反順序收集的。一個列表Char s是Haskell中的一個字符串,所以你完成了。

相關問題