有条件地绑定或忽略 do 表示法中的值
Conditionally binding or ignoring a value in do notation
作为一个学习项目,我目前正在尝试为 Haskell 中的 ELF 文件格式构建一个解析器。 Elf 二进制文件有一个称为程序头的部分,这个头包含一个专用于标志的词。程序头包含两个标志字段:一个用于 64 位标志,一个用于 32 位标志,顺序不连续。
像这样:
data ProgramHeader =
ProgramHeader {
getSegmentType :: SegmentType
, get64SegmentFlags :: SegmentFlag
, getOffset :: Offset
, getVirtualAddress :: Address
, getPhysicalAddress :: Address
, getFileSize :: Size
, getMemorySize :: Size
, get32SegmentFlags :: SegmentFlag
, getAlignment :: Either Word32 Word64
}
解析如下:
parseProgramHeader :: WordSize -> Endianness -> Get Program
parseProgramHeader wordsize en = do
st <- parseSegmentType
flags64 <- parseSegmentFlag
offset <- parseVariant wordsize en
virtualAddress <- parseVariant wordsize en
physicalAddress <- parseVariant wordsize en
fileSize <- parseVariant wordsize en
memorySize <- parseVariant wordsize en
flags32 <- parseSegmentFlag
a <- parseVariableWord wordsize
return $ ProgramHeader st flags64 offset virtualAddress physicalAddress fileSize memorySize flags32 a
现在我希望能够将这两个 SegmentFlag
字段合并为一个字段:
data ProgramHeader =
ProgramHeader {
getSegmentType :: SegmentType
, getSegmentFlags :: SegmentFlag
, getOffset :: Offset
, getVirtualAddress :: Address
, getPhysicalAddress :: Address
, getFileSize :: Size
, getMemorySize :: Size
, getAlignment :: Either Word32 Word64
}
但是,我仍然需要解析标志词两次(在每个相应的位置),只保留相关的一个。
我的直觉是,这意味着我只需要为相应的wordsize绑定parseSegmentFlag值;如果不是这种情况,仍然会调用 parseSegmentFlag,但会丢弃该值。
在伪代码中:
parseProgramHeader :: WordSize -> Endianness -> Get Program
parseProgramHeader wordsize en = do
st <- parseSegmentType
if wordsize == Bit64
then
do flags <- parseSegmentFlag
else
discard $ parseSegmentFlag
[...]
if wordsize == Bit32
then do flags <- parseSegmentFlag
else do discard $ parseSegmentFlag
现在我完全不知道如何在 Haskell 中优雅地执行此操作。有任何想法吗?指针?
谢谢!
另一种选择,只保留代码,然后在最后选择相关的版本,即
... -- as before
let flags = case ws of
Bit32 -> flags32
Bit64 -> flags64
return $ ProgramHeader st flags ...
(我使用 case
而不是 if
因为如果你碰巧添加了另一个案例,你会在这里收到警告,而不是进入你选择的默认分支)
作为一个学习项目,我目前正在尝试为 Haskell 中的 ELF 文件格式构建一个解析器。 Elf 二进制文件有一个称为程序头的部分,这个头包含一个专用于标志的词。程序头包含两个标志字段:一个用于 64 位标志,一个用于 32 位标志,顺序不连续。
像这样:
data ProgramHeader =
ProgramHeader {
getSegmentType :: SegmentType
, get64SegmentFlags :: SegmentFlag
, getOffset :: Offset
, getVirtualAddress :: Address
, getPhysicalAddress :: Address
, getFileSize :: Size
, getMemorySize :: Size
, get32SegmentFlags :: SegmentFlag
, getAlignment :: Either Word32 Word64
}
解析如下:
parseProgramHeader :: WordSize -> Endianness -> Get Program
parseProgramHeader wordsize en = do
st <- parseSegmentType
flags64 <- parseSegmentFlag
offset <- parseVariant wordsize en
virtualAddress <- parseVariant wordsize en
physicalAddress <- parseVariant wordsize en
fileSize <- parseVariant wordsize en
memorySize <- parseVariant wordsize en
flags32 <- parseSegmentFlag
a <- parseVariableWord wordsize
return $ ProgramHeader st flags64 offset virtualAddress physicalAddress fileSize memorySize flags32 a
现在我希望能够将这两个 SegmentFlag
字段合并为一个字段:
data ProgramHeader =
ProgramHeader {
getSegmentType :: SegmentType
, getSegmentFlags :: SegmentFlag
, getOffset :: Offset
, getVirtualAddress :: Address
, getPhysicalAddress :: Address
, getFileSize :: Size
, getMemorySize :: Size
, getAlignment :: Either Word32 Word64
}
但是,我仍然需要解析标志词两次(在每个相应的位置),只保留相关的一个。
我的直觉是,这意味着我只需要为相应的wordsize绑定parseSegmentFlag值;如果不是这种情况,仍然会调用 parseSegmentFlag,但会丢弃该值。 在伪代码中:
parseProgramHeader :: WordSize -> Endianness -> Get Program
parseProgramHeader wordsize en = do
st <- parseSegmentType
if wordsize == Bit64
then
do flags <- parseSegmentFlag
else
discard $ parseSegmentFlag
[...]
if wordsize == Bit32
then do flags <- parseSegmentFlag
else do discard $ parseSegmentFlag
现在我完全不知道如何在 Haskell 中优雅地执行此操作。有任何想法吗?指针?
谢谢!
另一种选择,只保留代码,然后在最后选择相关的版本,即
... -- as before
let flags = case ws of
Bit32 -> flags32
Bit64 -> flags64
return $ ProgramHeader st flags ...
(我使用 case
而不是 if
因为如果你碰巧添加了另一个案例,你会在这里收到警告,而不是进入你选择的默认分支)