Haskell:预期的惰性,为什么要评估它?

标签 haskell error-handling lazy-evaluation option-type

我有一个函数 sideH,它存在 Prelude.head [] 的风险。因此,我使用 Maybe 编写它,以避免这种情况:

sideH :: Residue -> Maybe (Atom, Atom)
sideH res
    -- Make sure the elements exist
    | nits /= [] && cars /= [] && oxys /= [] = Just (newH1, newH2)
    | otherwise = Nothing where
    ...

上面的工作完全符合预期,没有错误。现在,在调用 sideH 的函数(不是 do 构造)中,我必须处理 sideH 返回 Nothing 的情况:

callerFunc :: [Residue] -> Aromatic -> [(Double, Double)]
callerFunc [] _ = []
callerFunc (r:rs) aro
    -- Evaluate only if there is something to evaluate
    | newHs /= Nothing = (newH1Pos, newH2Pos)
    | otherwise = callerFunc rs aro where
    newHs = sideH r
    newH1Pos = atomPos $ fst $ fromJust newHs
    newH2Pos = atomPos $ snd $ fromJust newHs

如果我尝试在 newH = Nothing 时评估 newH1PosnewH2Pos,它将失败,因为 fromJust Nothing是一个错误。但是,我希望这种情况永远不会发生。我希望 callerFunc 评估 newHs,它要么是 Just Something 要么是 Nothing。如果为 Nothing,则 callerFunc 将进入下一步,而不会评估 newH1PosnewH2Pos。事实似乎并非如此。我在我期望 newHs 返回 Nothing 的地方收到 *** Exception: Maybe.fromJust: Nothing 错误。

有人要求我提供更多代码。我试图找出重现错误的最小情况,但与此同时,这是完整的有问题的 callerFunc 代码。

-- Given a list of residues and an aromatic, find instances where there
--  is a Hydrogen bond between the aromatic and the Hydrogens on Gln or Asn
callerFunc :: [Residue] -> Aromatic -> [(Double, Double)]
callerFunc [] _ = []
callerFunc (r:rs) aro
    -- GLN or ASN case
    | fst delR <= 7.0 && (resName r == gln || resName r == asn) &&
        newHs /= Nothing && snd delR <= 6.0 = 
        [(snd delR, fst delR)] ++ hBondSFinder rs aro
    | otherwise = hBondSFinder rs aro where
    -- Sidechain identifying strings
    gln = B.pack [71, 76, 78]
    asn = B.pack [65, 83, 78]
    -- Get the location of the Hydrogens on the residue's sidechain
    newHs = sideH r
    newH1Pos = atomPos $ fst $ fromJust newHs
    newH2Pos = atomPos $ snd $ fromJust newHs
    -- Get the location of the Nitrogen on the mainchain of the Residue
    ats = resAtoms r
    backboneNPos = atomPos $ head $ getAtomName ats "N"
    hNVect1 = Line2P {lp1 = newH1Pos, lp2 = backboneNPos}
    hNVect2 = Line2P {lp1 = newH2Pos, lp2 = backboneNPos}
    interPoint1 = linePlaneInter (aroPlane aro) hNVect1
    interPoint2 = linePlaneInter (aroPlane aro) hNVect2
    delR = minimum [(interPoint1 `dist` newH1Pos, delr1), 
        (interPoint2 `dist` newH2Pos, delr2)]
    delr1 = interPoint1 `dist` (aroCenter aro)
    delr2 = interPoint2 `dist` (aroCenter aro)

我知道这是一个痛苦的代码转储。我正在努力减少它。

最佳答案

这个问题的答案(在评论中提出)不适合评论:“我不确定如何使用模式匹配,在这里删除这些 if 语句。”。

例如,像这样,尽管仍然存在一些代码味道,可以通过一些额外的重构来改进:

sideH :: Residue -> Maybe (Atom, Atom)
sideH res = case (nits, cars, oxys) of
    (_:_, _:_, _:_) -> Just (newH1, newH2)
    _ -> Nothing
    where
    ...

如果你有灵活的道德,你可以尝试这样的事情:

sideH :: Residue -> Maybe (Atom, Atom)
sideH res = do
    _:_ <- return nits
    _:_ <- return cars
    _:_ <- return oxys
    return (newH1, newH2)
    where
    ...

同样,如果有更多的上下文和代码可供推荐,那么这两个代码示例都可能会改进大约十倍。

关于Haskell:预期的惰性,为什么要评估它?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9425437/

相关文章:

haskell - 应该将列表转换为字符串的函数中的解析错误 - haskell

performance - 蛮力旅行商: Why is Haskell so much slower than C?

linux - 动态处理带有二进制返回码的命令的错误报告

vb.net - Lazy(Of T) 使用/初始化

lazy-evaluation - Perl 6 中嵌套和非嵌套映射的上下文有什么区别?

haskell - Data.ByteString.Lazy 中 block 的大小

linux - 有没有办法告诉 GHC 将所有警告转储到文件中?

c# - 远程服务器返回错误: (500) Internal Server Error on website

silverlight - AppDomain.GetData 方法不可访问?

haskell - 使用 Haskell 从麦克风捕获音频输入?