【问题标题】:How to print integer literals in binary or hex in haskell?如何在haskell中以二进制或十六进制打印整数文字?
【发布时间】:2010-12-29 21:42:34
【问题描述】:

如何在haskell中以二进制或十六进制打印整数文字?

printBinary 5 => "0101"

printHex 5 => "05"

哪些库/函数允许这样做?

我遇到了 Numeric 模块及其 showIntAtBase 函数,但一直无法正确使用。

> :t showIntAtBase 

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

【问题讨论】:

    标签: haskell binary io hex


    【解决方案1】:

    Numeric 模块包括多个 functions for showing an Integral type 在不同的基础上,包括 showIntAtBase。以下是一些使用示例:

    import Numeric (showHex, showIntAtBase)
    import Data.Char (intToDigit)
    
    putStrLn $ showHex 12 "" -- prints "c"
    putStrLn $ showIntAtBase 2 intToDigit 12 "" -- prints "1100"
    

    【讨论】:

    • 对于像我这样没有向下滚动的懒惰的人来说,printf 示例更加简洁灵活,并且可以做其他有用的事情,例如给出一个恒定长度的字符串和所有其他 printf 特征。而不是上面,只是:printf "%032b" 5
    • @mozboz, printf 在 Haskell 中更像是一个魔术,而不是用于严肃代码的函数。格式字符串在运行时解析(可能会产生运行时错误),整个机制有点慢。
    • 遗憾的是,没有showIntAtBase 的变体可以用零填充输出。毕竟,如果您将格式设置为二进制或十六进制,则与您想要删除所有前导零不同。
    【解决方案2】:

    您还可以使用 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

    【讨论】:

    • ` printf "The value of %d in hex is: 0x%08x" i i ` 可以,因为 printf 既可以是IO () 也可以是String
    • 是的,确实如此。我只是想明确一点,它可以用作返回字符串的纯函数。
    【解决方案3】:

    如果您导入 NumericData.Char 模块,您可以这样做:

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

    这将适用于最多 16 个碱基,因为这就是 intToDigit 的全部用途。上面示例中额外空字符串参数的原因是showIntAtBase 返回一个ShowS 类型的函数,它将显示表示连接到现有字符串。

    【讨论】:

      【解决方案4】:

      您可以使用以下方式将整数转换为二进制:

      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]
      

      【讨论】:

      • 在最后一行的末尾加上 b:(decToBIn a) 不是更好吗?
      • 不应该0还给0吗? decToBin' 0 = [0] 也许?
      【解决方案5】:

      十六进制可以写成0x,二进制可以写成0b前缀,例如:

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

      请注意,二进制文件需要BinaryLiterals 扩展名。

      【讨论】:

        【解决方案6】:

        您可以定义自己的递归函数,例如:

        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 中看到的 lambda 表达式。

        toKBaseVal 函数则相反,将小数转换为基于 k 的值。同样,我们需要一个相反的映射函数:从小数到基于 k 的值的相应特殊字符。

        作为测试,您可以键入:

        binToDec(decToBin 7) = 7
        

        假设你想从十进制转换为八进制:

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

        同样,我只使用 lambda 表达式,因为映射很简单:只是 int 到 digit。

        希望对您有所帮助!好编程!

        【讨论】:

          【解决方案7】:

          单线粉丝的愚蠢解决方案:

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

          我使用了定点运算符,所以这是一个定点运算符的示例;也因为它允许我严格自下而上地构建单线。 (注意:不鼓励自下而上的开发。)

          参考:stack script syntaxCommand line argumentsfix operator definition

          【讨论】:

          • 我觉得你可以用("0123456789abcdef" !!)代替(\n -&gt; "0123456789abcdef" !! n)
          【解决方案8】:

          这是一个简单、高效、与基础无关的Unlicenced 实现:

          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(如果只是自动类型转换的话……)。

          【讨论】:

          • 所有其他答案都忽略Data.Word。让我们在这里变得真实 - 一旦你做“有点锤击”,例如使用Data.Bits,您更有可能希望看到十六进制或二进制数字,并且您更可能不使用Int,而是使用Word8, Word16, ..。 +1
          • 虽然他在使用 Word8 作为基数而不是数字时做出了一个奇怪的选择...可以改进...如果它适用于所有Bits a,那就更好了。
          【解决方案9】:

          使用FiniteBits 类:

          import Data.Bits (FiniteBits, finiteBitSize, testBit, shiftR)
          
          showBits :: FiniteBits a => a -> String
          showBits bits =
            go (finiteBitSize bits - 1) where
              go shift =
                if shift >= 0
                  then
                    let bit = if testBit (shiftR bits shift) 0 then '1' else '0'
                    in bit : go (pred shift)
                  else
                    ""
          

          例子:

          showBits (4 :: Word8) => "00000100"

          showBits (50 :: Int16) => "0000000000110010"

          showBits (-127 :: Int32) => "11111111111111111111111110000001"

          【讨论】:

            猜你喜欢
            • 2013-03-31
            • 2020-05-09
            • 2022-11-29
            • 1970-01-01
            • 1970-01-01
            • 2010-11-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多