我只是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程序员......
答案 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