在Haskell中转换Word8和Word16

5
我正在尝试在Haskell中进行小端转换,这样我就可以将Word16转换为两个Word8(例如258 = 1 * 256 + 2,则结果应为[2, 1])。然后我将结果打包成一个ByteString。
我为此创建了以下代码:
import Data.Word
import Data.Bits

getByte b num = shift (relevantBits b num) (shiftNum b)
    where bitMask b = sum $ map (2^) [8*b-8 .. 8*b-1]
          relevantBits b num = num .&. bitMask b
          shiftNum b = 8-8*b

encodeWord16 x = [getByte 1 x, getByte 2 x]

input :: Word16
input = 355

output :: [Word8]
output = encodeWord16 input

函数 getByte 从数字num中获取第b个字节。函数encodeWord16使用此帮助程序函数执行小端转换。
但是,这段代码无法编译,我收到了如下错误信息:
Couldn't match expected type `Word8' with actual type `Word16'
In the first argument of `encodeWord16', namely `input'
In the expression: encodeWord16 input
In an equation for `output': output = encodeWord16 input

我(非常不系统地)尝试通过随机分布fromIntegral表达式来实现所需的结果,但显然我的Haskell类型系统理解不足以解决这个问题。有没有一种系统化的方法来处理这个问题?基本上,我希望函数encodeWord16具有类型签名Word16 -> [Word8]

4个回答

7

fromIntegral可用于不同整数类型之间的转换。

fromIntegral :: (Num b, Integral a) => a -> b

encodeWord16 :: Word16 -> [Word8]
encodeWord16 x = map fromIntegral [getByte 1 x, getByte 2 x]

尽管使用 getByte 返回 Word8 会更好:

getByte :: Int -> Word16 -> Word8
getByte b num = fromIntegral $ shift (relevantBits b num) (shiftNum b)
    -- where ...

4

不需要手动编写转换代码,你可以使用预定义函数来完成。

import Data.Word
import Data.ByteString.Builder
import Data.ByteString.Lazy (unpack)

encodeWord16 :: Word16 -> [Word8]
encodeWord16 = unpack . toLazyByteString . word16LE

3
直接提取这些字节怎么样?像这样:
encodeWord16 x = [ x .&. 0xFF, (x .&. 0xFF00) `shiftR` 8 ]

如果你想让encodeWord16的签名为Word16 -> [Word8],那么在它之前添加map fromIntegral,就像这样:
encodeWord16 :: Word16 -> [Word8]
encodeWord16 x = map fromIntegral [ x .&. 0xFF, (x .&. 0xFF00) `shiftR` 8 ]

是的,那看起来是更有效率的方法。我仍然需要应用 map fromIntegral(请参见András Kovács的答案)。 - Julian
@Julian 是的,因为如果 x 的类型是 Word16,那么 x .&. 0xFF 的类型也将是 Word16 - Lee Duhem
有没有一种方法可以将Word8转换为[Word8]?即将它们变成位列表。 - astiefel
@astiefel 是的。然而,我认为位向量,例如 Data.BitVector,是一个更好的选择 :-) - Lee Duhem

1
"

binary 包含以下代码:

"
-- Words16s are written as 2 bytes in big-endian (network) order
instance Binary Word16 where
    put     = putWord16be

(http://hackage.haskell.org/package/binary-0.7.1.0/docs/Data-Binary.html#g:1)

-- | Write a Word16 in big endian format
putWord16be :: Word16 -> Builder
putWord16be w = writeN 2 $ \p -> do
    poke p               (fromIntegral (shiftr_w16 w 8) :: Word8)
    poke (p `plusPtr` 1) (fromIntegral (w)              :: Word8)

(http://hackage.haskell.org/package/binary-0.7.1.0/docs/Data-Binary-Builder.html#g:5)

所以您可以像这样使用它:
> encode (355 :: Word16)
"\SOHc"
> toLazyByteString $ putWord16be 355
"\SOHc"
> index (encode (355 :: Word16)) 0
1
> index (toLazyByteString $ putWord16be 355) 0
1
> index (encode (355 :: Word16)) 1
99
> index (toLazyByteString $ putWord16be 355) 1
99

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接