Как напечатать целочисленные литералы в двоичном или шестнадцатеричном в haskell? - PullRequest
61 голосов
/ 24 декабря 2009

Как вывести целочисленные литералы в двоичном или шестнадцатеричном формате в haskell?

printBinary 5 => "0101"

printHex 5 => "05"

Какие библиотеки / функции позволяют это?

Я наткнулся на модуль Numeric и его функцию showIntAtBase, но не смог его правильно использовать.

> :t showIntAtBase 

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

Ответы [ 8 ]

81 голосов
/ 25 декабря 2009

Числовой модуль включает в себя несколько функций для отображения типа Integral на различных базах, включая showIntAtBase. Вот несколько примеров использования:

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

putStrLn $ showHex 12 "" -- prints "c"
putStrLn $ showIntAtBase 2 intToDigit 12 "" -- prints "1100"
31 голосов
/ 21 июня 2011

Вы также можете использовать printf пакета printf для форматирования вывода с помощью дескрипторов формата 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

Выход:

Значение 65535 в шестнадцатеричном формате: 0x0000ffff
Код цвета html будет: # 00FFFF
Значение 65535 в двоичном виде: 1111111111111111

28 голосов
/ 25 декабря 2009

Если вы импортируете модули Numeric и Data.Char, вы можете сделать это:

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

Это будет работать для любых баз до 16, так как это все, для чего intToDigit работает. Причина использования дополнительного пустого строкового аргумента в приведенных выше примерах заключается в том, что showIntAtBase возвращает функцию типа ShowS, которая объединяет отображаемое представление с существующей строкой.

6 голосов
/ 24 декабря 2009

Вы можете преобразовать целое число в двоичное что-то вроде следующего:

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

использование в GHCi:

Prelude> decToBin 10
[1,0,1,0]
4 голосов
/ 27 февраля 2017

Hex может быть записан с 0x и двоичным с префиксом 0b, например ::100100

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

Обратите внимание, что для двоичного файла требуется расширение BinaryLiterals.

3 голосов
/ 11 октября 2018

Глупое решение для однострочных вентиляторов:

(\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

Ядро однострочника:

quotRem 247 16

Для ясности вы можете, в качестве альтернативы, поместить в файл следующее:

#!/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

И выполнить скрипт с:

stack runghc -- DecToHex.hs 247

Я использовал оператор с фиксированной точкой просто так, это пример с оператором с фиксированной точкой; также потому, что это позволило мне построить однострочно строго снизу вверх. (Примечание: развитие снизу вверх не рекомендуется.)

Ссылки: синтаксис стекового скрипта , Аргументы командной строки , fix определение оператора .

3 голосов
/ 05 марта 2017

Вы можете определить свои собственные рекурсивные функции, такие как:

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

Пояснение: Как видите, функция toNum преобразует основанное на k значение в десятичное, используя заданную базу и функцию отображения. Функция отображения преобразует специальные символы в десятичное значение (например, A = 10, B = 11, ... в шестнадцатеричном формате). Для бинарного отображения вы также можете использовать лямбда-выражение, как вы видите в binToDec.

Принимая во внимание, что функция toKBaseVal является противоположной, преобразовывая десятичное число в основанное на k значение. Опять же, нам нужна функция отображения, которая делает противоположное: от десятичного до соответствующего специального символа значения на основе k.

В качестве теста вы можете набрать:

binToDec(decToBin 7) = 7

Предположим, вы хотите перевести из десятичного в восьмеричное значение:

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

Опять же, я использую только лямбда-выражение, потому что отображение простое: просто int в цифру.

Надеюсь, это поможет! Хорошее программирование!

0 голосов
/ 11 апреля 2019

Вот простая, эффективная, независимая от базы Unlicence 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

Вам необходимо import Data.Word, чтобы использовать Word8 (что ограничивает значения настолько, насколько это возможно), и вам часто потребуется fromIntegral (если бы были только автоматические преобразования типов ...).

...