【问题标题】:How to make it better? "Maybe Bool" doesn't look great如何让它变得更好? “Maybe Bool”看起来不太好
【发布时间】:2019-10-07 14:10:29
【问题描述】:

首先,我是 Haskell 的新手,所以如果我问的问题有点愚蠢,请告诉我如何才能做得更好。谢谢:)

我的任务是获取符合特定条件的字符串列表。如果没有字符串符合,我想评估为Nothing。所以我写了两个函数:

isSpecialLine :: String -> String -> Maybe Bool
isSpecialLine t s = Just $ (("[" ++ t ++ ":") `isPrefixOf` s) && ("]::" `isSuffixOf` s)

getLinesWith :: String -> String -> Maybe [String]
getLinesWith t = filterM (isSpecialLine t) . lines

这段代码有效,但我发现Maybe Bool 看起来有点奇怪。嘿,它是二进制的!它始终为TrueFalse,因此isSpecialLine 的值将始终为Just TrueJust False。就我而言,它永远不可能是Nothing

但如果我将isSpecialLine 输出类型更改为Bool,则会出现以下问题:filterM 期望Maybe Bool 而不是Bool

好的,我这样做:

getLinesWith :: String -> String -> Maybe [String]
getLinesWith t = filterM (Just $ isSpecialLine t) . lines

现在编译器抱怨类型不匹配:Maybe (String -> Bool) 与预期的String -> Maybe Bool 不匹配。好吧,很合理。所以我:

getLinesWith :: String -> String -> Maybe [String]
getLinesWith t = Just $ filter (isSpecialLine t) . lines

再次输入不匹配,这次Maybe (String -> [String]) 不是String -> Maybe [String]。将[String] 包装成Maybe monad 的正确语法是什么?

【问题讨论】:

  • 我认为你首先不需要MaybeMaybe 通常用于非全部功能(例如,一些无意义的输入)。

标签: haskell monads maybe


【解决方案1】:

isSpecialLine 永远不会产生 Nothing,所以这是一个很好的暗示,Maybe 也可能没有意义。

isSpecialLine :: String -> String -> Bool
isSpecialLine t s = (("[" ++ t ++ ":") `isPrefixOf` s) && ("]::" `isSuffixOf` s)

getLinesWith :: String -> String -> [String]
getLinesWith t = filter (isSpecialLine t) . lines

【讨论】:

    【解决方案2】:

    在我看来,您在这里不需要filterM。您可以改用filter :: (a -> Bool) -> [a] -> [a]

    isSpecialLine :: String -> String -> Bool
    isSpecialLine t s = isPrefixOf ("[" ++ t ++ ":") s && isSuffixOf "]::" s
    
    getLinesWith :: String -> String -> [String]
    getLinesWith t = filter (isSpecialLine t) . lines

    确实,这将返回Strings 的列表,其中将保留与isSpecialLine 过滤器匹配的每个字符串。

    【讨论】:

    • 当然,这是getLinesWith 的第一个版本。但我想得到Maybe [String],而不是[String]。遇到这种情况该怎么办?
    • @Pnatone887:在什么条件下getLinesWith 会返回Nothing
    • 如果没有符合条件的行,它应该返回Nothing
    • @Pantone877 你觉得NothingJust [] 有什么区别?
    • @Pantone877:通常Maybe 可以被视为类型级别的1+:我们构造了一个额外的值。但通常这样做才有意义,好吧,如果我们需要一个额外的值来表示无法用 n 个其他值表示的东西。我记得 Tom Schrijvers 曾就此发表过一次演讲,但不知何故我无法立即找到它。
    【解决方案3】:

    不要像这样使用Maybe 并从getLinesWith 解包数据,

    getLinesWith :: String -> String -> Maybe [String]
    ...
    
    getDate :: String -> String -> UTCTime
    getDate t s =
        timeFromString
        . filter (/= ' ')
        . getBetweenColons
        . maybe ":01-01-1971:" head $ getLinesWith t s
    

    没有Maybe,我完成了更好看(我希望如此)的解决方案:

    getLinesWith :: String -> String -> [String]
    ...
    
    getDate :: String -> String -> UTCTime
    getDate t s
      | null date = timeFromString "01-01-1971"
      | otherwise = timeFromString
                  . filter (/= ' ')
                  . getBetweenColons
                  . head $ date
      where date = getLinesWith t s    
    

    实际上,所有围绕Maybe 的舞蹈都是从GHC 抱怨head [] 开始的。 谢谢大家的意见!

    【讨论】:

    • 如果你使用date = getLinesWith t s ++ ["01-01-1971"](或者任何会被处理成"01-01-1971"的字符串),你可以保证date是一个非空列表,并且head可以安全使用.
    • 太棒了,@chepner!那更优雅。非常感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-20
    相关资源
    最近更新 更多