请更正我对Maybe Monad的使用

时间:2012-03-31 22:22:32

标签: haskell monads

我正在实施一个执行取幂密码的小程序。某些计算可能会失败,例如,计算模块化逆。我用Maybe来处理这些类型的失败。但是现在我被困了,因为我需要注射"也许是另一个部分应用函数里面的值。我知道,如果我有一个带有一个参数的函数,我会使用bind来执行此操作。

import Data.Char
import Math.NumberTheory.Powers

extendedGcd::Integer->Integer->(Integer, Integer)
extendedGcd a b | r == 0 = (0, 1)
                | otherwise = (y, x - (y * d))
                where
                    (d, r) = a `divMod` b
                    (x, y) = extendedGcd b r

modularInverse::Integer->Integer->Maybe Integer
modularInverse n b | relativelyPrime n b = Just . fst $ extGcd n b
                   | otherwise = Nothing
                   where
                        extGcd = extendedGcd

relativelyPrime::Integer->Integer->Bool
relativelyPrime m n | gcd m n == 1 = True
                    | otherwise = False

textToDigits::String->[Integer]
textToDigits p = map (\x->toInteger (ord x - 97)) p

digitsToText::[Integer]->String
digitsToText d = map (\x->chr ((fromIntegral x) + 97)) d

exptEncipher::Integer->Integer->Integer->Maybe Integer
exptEncipher m k p | relativelyPrime k (p - 1) = Just $ powerMod p k m 
                   | otherwise = Nothing

exptDecipher::Integer->Integer->Integer->Integer
exptDecipher m q c = powerMod c q m

exptEncipherString::Integer->Integer->String->[Maybe Integer]
exptEncipherString m k p = map (exptEncipher m k) plaintext
    where
        plaintext = textToDigits p

exptDecipherString::Integer->Integer->[Maybe Integer]->Maybe String
exptDecipherString m k c = (fmap digitsToText) plaintext
    where
        q = modularInverse k (m - 1)
        plaintext = map (fmap $ exptDecipher m q) c

具体来说,我的问题出在函数exptDecipherString中,我需要将q中monad封装的值注入到函数exptDecipher中,然后我将解除它对c的作用。这样做的正确方法是什么?另外,我担心我最终会得到一个[Maybe Char]列表,而不是我想要的Maybe String。我在解决所有这些方面遇到了问题。有人可以开导我吗?

1 个答案:

答案 0 :(得分:3)

您可以使用sequenceap来确定类型。首先是他们的签名:

ap :: Monad m => m (a -> b) -> m a -> m b
sequence :: Monad m => [m a] -> m [a]

请注意sequence直接解决了您对[Maybe Char]而不是Maybe String的担忧。两者都在Control.Monad中(请注意,您必须导入ap)。我们可以按如下方式使用它们:

exptDecipherString :: Integer -> Integer -> [Maybe Integer] -> Maybe String
exptDecipherString m k c = fmap digitsToText plaintext
  where
    q = modularInverse k (m - 1)
    plaintext = sequence $ map (ap $ fmap (exptDecipher m) q) c

我们可以通过完成这些类型来达到这一点。首先,我们将exptDecipher应用于m,它为我们提供了Integer -> Integer -> Integer类型的函数。我们希望将其应用于q,但它是Maybe Integer,因此我们必须使用fmap (exptDecipher m) q,然后其类型为Maybe (Integer -> Integer)。然后我们可以在前面弹出ap并获得Maybe Integer -> Maybe Integer类型的内容。然后,我们会在c上进行映射,这会为我们提供[Maybe Integer],我们可以使用sequence将其翻出来。

这可能不起作用 - 如果逻辑中有错误等等 - 但至少它会编译。

一些附注:您可以分别使用来自<$>的中缀运算符<*>Control.Applicative代替fmapap语法稍微好一点,您的relativelyPrime可以更简单地编写为relativelyPrime m n = gcd m n == 1