我正在实施一个执行取幂密码的小程序。某些计算可能会失败,例如,计算模块化逆。我用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。我在解决所有这些方面遇到了问题。有人可以开导我吗?
答案 0 :(得分:3)
您可以使用sequence
和ap
来确定类型。首先是他们的签名:
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
代替fmap
和ap
语法稍微好一点,您的relativelyPrime
可以更简单地编写为relativelyPrime m n = gcd m n == 1
。