Как избавиться от лишнего - PullRequest
1 голос
/ 02 апреля 2012

У меня есть функция, которая может завершиться ошибкой, поэтому возвращаемое ею значение необходимо обернуть в Maybe.Он использует другую функцию, которая также может завершиться ошибкой, и она также обернута в Maybe.Проблема в том, чтобы заставить типы работать в промежуточном вычислении, мне нужно «преждевременно» поднять функцию для работы в контексте Maybe.В результате я получаю тип Maybe [Maybe Integer], когда я хочу получить Maybe [Integer].Рассматриваемая функция - это функция exptDecipherString, функция, которая вызывает «преждевременный» подъем, - это функция modularInverse.

import Data.Char
import Control.Applicative
import Control.Monad
import Math.NumberTheory.Powers

--Helpers

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 

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

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

--Exponentiation Ciphers

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

exptDecipher::Integer->Integer->Integer->Maybe Integer
exptDecipher m q c | relativelyPrime q (m - 1) = Just $ powerMod c q m
                   | otherwise = Nothing

exptEncipherString::Integer->Integer->String->Maybe [Integer]
exptEncipherString m k p | relativelyPrime k (m - 1) = mapM (exptEncipher m k) plaintext
                         | otherwise = Nothing
    where
        plaintext = textToDigits p

exptDecipherString::Integer->Integer->[Integer]->Maybe String
exptDecipherString m k c | relativelyPrime k (m - 1) = fmap digitsToText plaintext
                         | otherwise = Nothing
    where
        q = modularInverse k (m - 1)
        plaintext = mapM (exptDecipher m <$> q <*>) (map pure c)

1 Ответ

6 голосов
/ 02 апреля 2012

Первое, что вы обычно должны попробовать для "Как X может стать Y", это hoogle .В этом случае рекомендуется использовать join, что приведет к свертыванию двух Maybe s в один.

При некоторой реструктуризации кода монаду Maybe также можно использовать здесь.

Когда все остальное не удается, прокрутите свое собственное решение, которое использует функции или оператор case с сопоставлением с образцом.

...