2009-12-24 2 views
54

Comment imprimer des littéraux entiers en binaire ou hexadécimal en haskell?Comment imprimer des littéraux entiers en binaire ou hexadécimal en haskell?

printBinary 5 => "0101" 

printHex 5 => "05" 

Quelles bibliothèques/fonctions permettent cela?

Je suis tombé sur le module Numeric et sa fonction showIntAtBase mais j'ai été incapable de l'utiliser correctement.

> :t showIntAtBase 

showIntAtBase :: (Integral a) => a -> (Int -> Char) -> a -> String -> String 

Répondre

69

Le module numérique comprend plusieurs functions for showing an Integral type à diverses bases, y compris showIntAtBase. Voici quelques exemples d'utilisation:

import Numeric (showHex, showIntAtBase) 
import Data.Char (intToDigit) 

putStrLn $ showHex 12 "" -- prints "c" 
putStrLn $ showIntAtBase 2 intToDigit 12 "" -- prints "1100" 
+6

Pour quelqu'un paresseux comme moi qui n'a pas fait défiler vers le bas, l'exemple printf est beaucoup plus concis et flexible, et peut faire d'autres choses utiles comme par ex. donne une chaîne de longueur constante et toutes les autres fonctions printf. Au lieu de ci-dessus, juste: 'printf"% 032b "5' – mozboz

+11

@mozboz,' printf' dans Haskell est plus comme un tour de magie qu'une fonction à utiliser dans le code sérieux. La chaîne de format est analysée lors de l'exécution (ce qui peut générer des erreurs d'exécution) et l'ensemble du mécanisme est un peu lent. – dfeuer

+0

Celui-ci ne fonctionne pas avec des nombres négatifs. – CMCDragonkai

3

Vous pouvez convertir entier en binaire avec quelque chose comme ce qui suit:

decToBin x = reverse $ decToBin' x 
    where 
    decToBin' 0 = [] 
    decToBin' y = let (a,b) = quotRem y 2 in [b] ++ decToBin' a 

utilisation dans GHCi:

Prelude> decToBin 10 
[1,0,1,0] 
+0

Ne serait-il pas préférable d'avoir b: (decToBIn a) à la fin de la dernière ligne? – prince

+0

Ne devrait-il pas rendre 0? 'decToBin '0 = [0]' peut-être? – Jaseem

+0

@SjB, ne fonctionne pas pour les nombres négatifs –

26

Si vous importez les modules Numeric et Data.Char, vous pouvez le faire:

showIntAtBase 2 intToDigit 10 "" => "1010" 
showIntAtBase 16 intToDigit 1023 "" => "3ff" 

Cela fonctionne pour toutes les bases jusqu'à 16, car cela est tout ce qui intToDigit fonctionne pour. La raison de l'argument de chaîne supplémentaire vide dans les exemples ci-dessus est que showIntAtBase renvoie une fonction de type ShowS, qui concaténera la représentation d'affichage sur une chaîne existante.

24

Vous pouvez également utiliser printf du paquet printf pour formater votre sortie avec des descripteurs de format de style c:

import Text.Printf 

main = do 

    let i = 65535 :: Int 

    putStrLn $ printf "The value of %d in hex is: 0x%08x" i i 
    putStrLn $ printf "The html color code would be: #%06X" i 
    putStrLn $ printf "The value of %d in binary is: %b" i i 

Sortie:

La valeur de 65535 en hexadécimal est: 0x0000FFFF
Le code couleur html serait: # 00FFFF
La valeur de 65535 en binaire est: 1111111111111111

+0

'printf" La valeur de% d en hexadécimal est: 0x% 08x "i i' est correct car printf peut être à la fois 'IO()' et 'String' – Nybble

+0

Oui, en effet. Je voulais juste le rendre évident, qu'il peut être utilisé comme une fonction pure qui renvoie une chaîne. –

4

Hex peut être écrit avec 0x et binaire avec 0b préfixe .: par exemple

> 0xff 
255 
>:set -XBinaryLiterals 
> 0b11 
3 

Notez que binaire nécessite l'extension BinaryLiterals.

2

Vous pouvez définir vos propres fonctions récursives comme:

import Data.Char (digitToInt) 
import Data.Char (intToDigit) 

-- generic function from base to decimal 
toNum :: [Char] -> Int -> (Char -> Int) -> Int 
toNum [] base map = 0 
toNum s base map = base * toNum (init(s)) base map + map(last(s)) 

-- generic function from decimal to base k 
toKBaseNum :: Int -> Int -> (Int -> Char) -> [Char] 
toKBaseNum x base map | x < base = [map x] 
         | otherwise = toKBaseNum (x `div` base) base map ++ [map(x `mod` base)] 


-- mapping function for hex to decimal 
mapHexToDec :: Char -> Int 
mapHexToDec x | x == 'A' = 10 
       | x == 'B' = 11 
       | x == 'C' = 12 
       | x == 'D' = 13 
       | x == 'E' = 14 
       | x == 'F' = 15 
       | otherwise = digitToInt(x) :: Int 

-- map decimal to hex 
mapDecToHex :: Int -> Char 
mapDecToHex x | x < 10 = intToDigit(x) 
       | x == 10 = 'A' 
       | x == 11 = 'B' 
       | x == 12 = 'C' 
       | x == 13 = 'D' 
       | x == 14 = 'E' 
       | x == 15 = 'F' 

-- hex to decimal 
hexToDec :: String -> Int 
hexToDec [] = 0 
hexToDec s = toNum s 16 mapHexToDec 

-- binary to decimal 
binToDec :: String -> Int 
binToDec [] = 0 
binToDec s = toNum s 2 (\x -> if x == '0' then 0 else 1) 

-- decimal to binary 
decToBin :: Int -> String 
decToBin x = toKBaseNum x 2 (\x -> if x == 1 then '1' else '0') 

-- decimal to hex 
decToHex :: Int -> String 
decToHex x = toKBaseNum x 16 mapDecToHex 

Explication: Comme vous pouvez le voir, la fonction tonum convertit une valeur basée k à la décimale, en utilisant la base donnée et une fonction de mappage . La fonction de mappage mappera les caractères spéciaux à une valeur décimale (par exemple A = 10, B = 11, ... en hexadécimal). Pour le mappage binaire, vous pouvez également utiliser une expression lambda comme vous le voyez dans binToDec.

Considérant que la fonction toKBaseVal est l'inverse, en convertissant une décimale en une valeur basée sur k.Encore une fois nous avons besoin d'une fonction de mappage qui fait le contraire: d'une décimale au caractère spécial correspondant de la valeur basée sur k.

En tant que test, vous pouvez taper:

binToDec(decToBin 7) = 7 

Supposons que vous voulez convertir des décimales en octal:

-- decimal to octal 
decToOct :: Int -> String 
decToOct x = toKBaseNum x 8 (\x -> intToDigit(x)) 

Encore une fois, je viens d'utiliser une expression lambda, parce que la cartographie est simple: Juste pour digit.

Espérons que ça aide! Bonne programmation!

Questions connexes