web-dev-qa-db-fra.com

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 le permettent?

Je suis tombé sur le module numérique et sa fonction showIntAtBase mais je n'ai pas pu l'utiliser correctement.

> :t showIntAtBase 

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

Le module numérique comprend plusieurs fonctions pour afficher un type intégral à différentes 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"
81
Chuck

Vous pouvez également utiliser printf du package 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

Production:

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

31
Martin Lütke

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

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

Cela fonctionnera pour toutes les bases jusqu'à 16, car c'est tout ce pour quoi intToDigit fonctionne. La raison de l'argument de chaîne vide supplémentaire 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.

28
Ian Ross

Vous pouvez convertir un entier en binaire avec quelque chose comme ceci:

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]
6
Sajad Bahmani

L'hex peut être écrit avec 0x et binaire avec 0b préfixe par exemple:

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

Notez que le binaire nécessite l'extension BinaryLiterals.

4
Mikkel

Solution idiote pour les fans à une ligne:

(\d -> let fix f = let {x = f x} in x in fmap (\n -> "0123456789abcdef" !! n) (fix (\f l n -> if n == 0 then l :: [Int] else let (q, r) = quotRem n 16 in f (r:l) q) [] d)) 247

Le noyau du monocylindre est:

quotRem 247 16

Pour plus de clarté, vous pouvez également mettre les éléments suivants dans un fichier:

#!/usr/bin/env stack
{- stack script --resolver lts-12.1 -}
-- file: DecToHex.hs

module Main where

import System.Environment

fix :: (a -> a) -> a
fix f = let {x = f x} in x

ff :: ([Int] -> Int -> [Int]) -> [Int] -> Int -> [Int]
ff = \f l n ->
  if n == 0
  then l
  else
    let (q, r) = quotRem n 16
    in f (r:l) q

decToHex :: Int -> String
decToHex d =
  fmap (\n -> "0123456789abcdef" !! n)
  (fix ff [] d)

main :: IO ()
main =
  getArgs >>=
  putStrLn . show . decToHex . read . head

Et exécutez le script avec:

stack runghc -- DecToHex.hs 247

J'ai utilisé un opérateur à virgule fixe juste pour que ce soit un exemple avec un opérateur à virgule fixe; aussi parce que cela m'a permis de construire le one-liner strictement bottom-up. (Remarque: le développement ascendant doit être découragé.)

Références: syntaxe du script de pile , arguments de la ligne de commande , définition de l'opérateur fix .

récursivitéhaskellhexhaskell-stack

3
inkbottle

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 sur k en décimal, 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.

Alors que la fonction toKBaseVal est l'inverse, 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.

Comme test, vous pouvez taper:

binToDec(decToBin 7) = 7

Supposons que vous souhaitiez convertir de décimal en octal:

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

Encore une fois, j'utilise juste une expression lambda, car le mappage est simple: juste int à digit.

J'espère que cela pourra aider! Bonne programmation!

3
Alessandro Giusa

Voici une implémentation simple, efficace, indépendante de la base, nlicence d:

convertToBase :: Word8 -> Integer -> String
convertToBase b n
    | n < 0              = '-' : convertToBase b (-n)
    | n < fromIntegral b = [(['0'..'9'] ++ ['A' .. 'Z']) !! fromIntegral n]
    | otherwise          = let (d, m) = n `divMod` fromIntegral b in convertToBase b d ++ convertToBase b m

Vous devez import Data.Word utiliser Word8 (ce qui limite les valeurs autant que possible), et vous aurez souvent besoin de fromIntegral (si seules les conversions de type automatiques étaient chose ...).

0
Solomon Ucko