检查可能值

时间:2016-10-22 16:54:22

标签: haskell maybe

我只是Haskell的初学者,我尝试在Haskell中进行cryptopals加密挑战。

我写了以下代码:

import Data.Char

nibbleToInt :: Char -> Int
nibbleToInt c
    | ('0' <= c && c <= '9') = (ord c) - 48
    | ('a' <= c && c <= 'f') = (ord c) - 87
    | ('A' <= c && c <= 'F') = (ord c) - 55
    | otherwise = error "Boom"


hexToInt :: String -> Int
hexToInt (x:y:[]) = nibbleToInt x * 16 + nibbleToInt y
hexToInt _ = error "Boom"

hexToInts :: String -> [Int]
hexToInts [] = []
hexToInts (n1:n2:ns) = [hexToInt $ [n1] ++ [n2]] ++ (hexToInts ns)
hexToInts _ = error "Boom"

main = do
    print $ hexToInts "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d"

此代码似乎有效,但我确信它可以改进!

我重构了nibbleToInt函数以使用Maybe

nibbleToInt :: Char -> Maybe Int
nibbleToInt c
    | ('0' <= c && c <= '9') = Just ((ord c) - 48)
    | ('a' <= c && c <= 'f') = Just ((ord c) - 87)
    | ('A' <= c && c <= 'F') = Just ((ord c) - 55)
    | otherwise = Nothing

然而,我不知道如何重写函数hexToInt,因为它调用nibbleToInt函数两次,返回Maybe Int。我不知道如何测试这两个值。

欢迎任何帮助,因为我不知道我周围的任何Haskell程序员......

1 个答案:

答案 0 :(得分:3)

您可以使用Maybe monad将一系列操作链接在一起,这些操作只有在值为Just时才会发生。如果值为Nothing,则链将停止:

hexToInt :: String -> Maybe Int
hexToInt (x:y:[]) = do
    a <- nibbleToInt x  -- chain will end if nibbleToInt evaluates to Nothing
    b <- nibbleToInt y  -- chain will end if nibbleToInt evaluates to Nothing
    return (a * 16 + b) -- chain completed successfully
hexToInt _ = Nothing

hexToInts :: String -> Maybe [Int]
hexToInts [] = return []
hexToInts (n1:n2:ns) = do
    i <- hexToInt (n1:[n2]) -- chain will end if hexToInt evaluates to Nothing
    is <- hexToInts ns      -- chain will end if hexToInts evaluates to Nothing
    return (i:is)           -- chain completed successfully
hexToInts _ = Nothing

您还可以使用Applicative样式,它在更实用的样式中执行相同的操作:

hexToInt :: String -> Maybe Int
hexToInt (x:y:[]) = f <$> nibbleToInt x <*> nibbleToInt y where
    f a b = a * 16 + b
hexToInt _ = Nothing

hexToInts :: String -> Maybe [Int]
hexToInts [] = return []
hexToInts (n1:n2:ns) = (:) <$> hexToInt (n1:[n2]) <*> hexToInts ns
hexToInts _ = Nothing

请注意,hexToInts可以使用splitAt来破坏列表:

hexToInts :: String -> Maybe [Int]
hexToInts [] = return []
hexToInts ns = (:) <$> hexToInt a <*> hexToInts as where
    (a, as) = splitAt 2 ns

或编写辅助函数来配对列表的元素,并使用mapM将monadic函数应用于对:

toPairs :: [a] -> [[a]]
toPairs [] = []
toPairs xs = a : toPairs as where
    (a, as) = splitAt 2 xs

hexToInts :: String -> Maybe [Int]
hexToInts = mapM hexToInt . toPairs