Haskellで整数リテラルをバイナリまたは16進数で印刷する方法は?
printBinary 5 => "0101"
printHex 5 => "05"
どのライブラリ/関数がこれを許可していますか?
NumericモジュールとそのshowIntAtBase関数に出会いましたが、正しく使用できませんでした。
> :t showIntAtBase
showIntAtBase :: (Integral a) => a -> (Int -> Char) -> a -> String -> String
Numericモジュールには、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
Numeric
およびData.Char
モジュールをインポートする場合、これを実行できます。
showIntAtBase 2 intToDigit 10 "" => "1010"
showIntAtBase 16 intToDigit 1023 "" => "3ff"
intToDigit
が機能するのはこれだけであるため、これは16までのベースで機能します。上記の例で余分な空の文字列引数を使用する理由は、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
拡張子が必要であることに注意してください。
ワンライナーファン向けの愚かなソリューション:
(\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
演算子定義 。
次のような独自の再帰関数を定義できます。
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ベースの値を10進数に変換します。マッピング関数は、特殊文字を10進数値にマップします(例:A = 10、B = 111、16進数)。バイナリマッピングの場合、binToDecで表示されるようなラムダ式を使用することもできます。
一方、toKBaseVal関数は逆で、小数をkベースの値に変換します。繰り返しますが、逆を行うマッピング関数が必要です。10進数からkベースの値の対応する特殊文字までです。
テストとして、次のように入力できます。
binToDec(decToBin 7) = 7
10進数から8進数に変換するとします。
-- decimal to octal
decToOct :: Int -> String
decToOct x = toKBaseNum x 8 (\x -> intToDigit(x))
繰り返しますが、マッピングは単純であるため、ラムダ式のみを使用します:整数から整数へ。
お役に立てば幸いです!良いプログラミング!
以下は、シンプルで効率的な、基本に依存しない、 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
必ず import Data.Word
を使用するにはWord8
(合理的に可能な限り値を制限します)、そして多くの場合fromIntegral
が必要になります(自動型変換のみが重要な場合...)。