IT Share you

Haskell에서 정수 리터럴을 바이너리 또는 16 진수로 인쇄하는 방법은 무엇입니까?

shareyou 2020. 11. 29. 12:38
반응형

Haskell에서 정수 리터럴을 바이너리 또는 16 진수로 인쇄하는 방법은 무엇입니까?


Haskell에서 정수 리터럴을 바이너리 또는 16 진수로 인쇄하는 방법은 무엇입니까?

printBinary 5 => "0101"

printHex 5 => "05"

이를 허용하는 라이브러리 / 함수는 무엇입니까?

Numeric 모듈과 showIntAtBase 함수를 발견했지만 올바르게 사용할 수 없습니다.

> :t showIntAtBase 

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

숫자 모듈은 여러 포함 일체형를 나타내는 함수 , 각종 기지 포함한 showIntAtBase. 다음은 몇 가지 사용 예입니다.

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

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

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

산출:

16 진수로 65535의 값은 다음과 같습니다. 0x0000ffff
html 색상 코드는 다음과 같습니다. # 00FFFF
바이너리로 된 65535의 값은 다음과 같습니다. 1111111111111111


NumericData.Char모듈 을 가져 오면 다음을 수행 할 수 있습니다.

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

이것이 작동하는 모든 것이기 때문에 16까지의 모든 염기에서 intToDigit작동합니다. 위의 예에서 빈 문자열 인수가 추가 된 이유 는 표시 표현을 기존 문자열에 연결하는 showIntAtBase유형의 함수 반환하기 때문 ShowS입니다.


다음과 같이 정수를 이진수로 변환 할 수 있습니다.

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]

16 진수는 다음 0x과 같이 0b접두사를 사용 하여 바이너리 로 작성할 수 있습니다 .

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

바이너리에는 BinaryLiterals확장자 가 필요합니다 .


다음과 같이 고유 한 재귀 함수를 정의 할 수 있습니다.

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

Explanation: As you can see, the toNum function converts a k-based value to decimal, using the given base and a mapping function. The mapping function will map special characters to a decimal value (for ex. A=10, B=11, ... in hex). For binary mapping you could also use a lambda expression like you see in binToDec.

Whereas the toKBaseVal function is the opposite, converting a decimal to a k-based value. Again we need a mapping function which does the opposite: from a decimal to the corresponding special character of the k-based value.

As a test you can type:

binToDec(decToBin 7) = 7

Suppose you want to convert from decimal to octal:

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

Again, I use just a lambda expression, because the mapping is simple: just int to digit.

Hope that helps! Good programming!


Silly solution for one-liner fans:

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

The nucleus of the one-liner is:

quotRem 247 16

For the sake of clarity, you can, alternatively, put the following in a file:

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

And execute the script with:

stack runghc -- DecToHex.hs 247

I used fixed-point operator just so it is an example with fixed-point operator; also because it allowed me to construct the one-liner strictly bottom-up. (Note: bottom-up development is to be discouraged.)

References: stack script syntax, Command line arguments, fix operator definition.


Here is a simple, efficient, base-agnostic, Unlicenced implementation:

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

You have to import Data.Word to use Word8 (which limits the values as much as reasonably possible), and you will often need fromIntegral (if only automatic type conversions were a thing...).

참고URL : https://stackoverflow.com/questions/1959715/how-to-print-integer-literals-in-binary-or-hex-in-haskell

반응형