Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
处理Haskell中的样板文件_Haskell_Boilerplate_Duplication - Fatal编程技术网

处理Haskell中的样板文件

处理Haskell中的样板文件,haskell,boilerplate,duplication,Haskell,Boilerplate,Duplication,这是一个软问题,但在下面的代码中,标记为“caesar ciphers”的部分有很多重复。处理这个问题的“Haskell”方法是什么?我应该做一个高阶函数吗?我想过了,但我不知道有什么意义。是否有一种“密码”类型,我可以定义用于制作密码 此外,我知道它可能看起来有点过于工程化,因为我在两个地方进行相同的错误检查,但我认为从每个函数“含义”的角度来看,它是有意义的。建议 import Data.Char import Control.Applicative import Control.Monad

这是一个软问题,但在下面的代码中,标记为“caesar ciphers”的部分有很多重复。处理这个问题的“Haskell”方法是什么?我应该做一个高阶函数吗?我想过了,但我不知道有什么意义。是否有一种“密码”类型,我可以定义用于制作密码

此外,我知道它可能看起来有点过于工程化,因为我在两个地方进行相同的错误检查,但我认为从每个函数“含义”的角度来看,它是有意义的。建议

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)) 

--Caesar Ciphers

caesarEncipher::Integer->Integer->Integer->Maybe Integer
caesarEncipher r s p | relativelyPrime r 26 = Just $ mod (r * p + s) 26
                     | otherwise = Nothing

caesarDecipher::Integer->Integer->Integer->Maybe Integer
caesarDecipher r s c | relativelyPrime r 26 = mod <$> ((*) <$> q <*> pure (c - s)) <*> pure 26
                     | otherwise = Nothing
    where
        q = modularInverse r 26

caesarEncipherString::Integer->Integer->String->Maybe String
caesarEncipherString r s p | relativelyPrime r 26 = fmap digitsToText $ mapM (caesarEncipher r s) plaintext
                           | otherwise = Nothing
    where
        plaintext = textToDigits p

caesarDecipherString::Integer->Integer->String->Maybe String
caesarDecipherString r s c | relativelyPrime r 26 = fmap digitsToText $ mapM (caesarDecipher r s) ciphertext
                           | otherwise = Nothing
    where
        ciphertext = textToDigits c

bruteForceCaesarDecipher::String->[Maybe String]
bruteForceCaesarDecipher c = caesarDecipherString <$> [0..25] <*> [0..25] <*> pure c
导入数据.Char
导入控制
进口管制
导入Math.NumberTheory.Powers
--助手
extendedGcd::Integer->Integer->(整数,整数)
扩展gcd a b | r==0=(0,1)
|否则=(y,x-(y*d))
哪里
(d,r)=a`divMod`b
(x,y)=扩展GCD b r
modularInverse::Integer->Integer->Maybe Integer
modularInverse n b |相对素n b=正义。fst$extGcd n b
|否则=没有
哪里
extGcd=扩展gcd
相对素数::整数->整数->布尔
相对素数mn=gcd mn==1
textToDigits::字符串->[Integer]
textToDigits=map(\x->toInteger(ord x-97))
DigitToText::[Integer]->字符串
digitsToText=map(\x->chr(从整数x+97))
--凯撒密码
凯撒加密:整数->整数->整数->可能整数
caesarEncipher r s p |相对优先权r 26=仅$mod(r*p+s)26
|否则=没有
凯撒解密::整数->整数->整数->可能整数
凯撒解密r s c |相对素r 26=模(*)q纯(c-s))纯26
|否则=没有
哪里
q=模数r 26
凯撒加密字符串::整数->整数->字符串->可能字符串
caesarEncipherString r s p | relativelyPrime r 26=fmap digitsToText$mapM(CaesarencipherR s)明文
|否则=没有
哪里
明文=文本到数字p
凯撒解密字符串::整数->整数->字符串->可能字符串
caesarDecipherString r s c | relativelyPrime r 26=fmap digitsToText$mapM(caesarDecipher r s)密文
|否则=没有
哪里
密文=文本到数字c
BRUTEFORCEASEADECRIPHER::String->[可能是String]
bruteForceCaesarDecipher c=caesarDecipherString[0..25][0..25]纯c

请注意,加密和解密使用完全相同的算法,因此您应该有一个函数来执行该操作

transform :: Integer -> Integer -> Integer -> Integer
transform mult trans n = (mult * n + trans) `mod` 26
然后,检查共三性并计算每个字符的模逆是浪费的,因此我建议

caesarEncipherString r s p
    | gcd r 26 == 1 = Just $ digitsToText $ map (transform r s) $ textToDigits p
    | otherwise     = Nothing

caesarDecipherString r s c = do
    mi <- modularInverse r 26
    caesarEncipherString mi (mi*(26-s)) c
caesarEncipherString r s p
|gcd r 26==1=仅$digitsToText$map(转换RS)$textToDigits p
|否则=没有
解码字符串r s c=do
mi制作一个
类型,并使用智能构造函数 样板文件的主要来源似乎是反复检查
r
是可逆的,并计算其逆。将操作(如
加密
)分为两个步骤是有意义的:首先检查,然后实际加密。这样,您可以只编写一次检查部分

实现这一点的一种方法是定义一个新类型
CaesarKey
,该类型保证只包含有效密钥。我们可以使用以下方法保证该不变量:

{-# LANGUAGE RecordWildCards #-} -- for the Key{..} syntax below

-- invariant: r and q are inverses mod 26. 
-- To ensure this invariant, we only export the 'caesarKey' smart constructor,
-- and not the underlying 'Key' constructor
data CaesarKey = Key { r :: Integer, s :: Integer, q :: Integer }

caesarKey :: Integer -> Integer -> Maybe CaesarKey
caesarKey r s = Key r s <$> invertMod r 26

-- ciphers
encipher :: CaesarKey -> Integer -> Integer
encipher Key{..} p = mod (r * p + s) 26

decipher :: CaesarKey -> Integer -> Integer
decipher Key{..} c = mod (q * (c - s)) 26

encipherString :: CaesarKey -> String -> String
encipherString key = digitsToText . map (encipher key) . textToDigits

decipherString :: CaesarKey -> String -> String
decipherString key = digitsToText . map (decipher key) . textToDigits
现在我们可以扔掉
解密
解密字符串
函数,因为它们是不必要的(也就是说,最好使用
反转

创建
allKeys
功能 从概念上讲,我们可以将
破译分解为两个任务:首先,生成所有可能的密钥;第二,用每个键解码文本。让我们用代码实现这一点:

allKeys :: [CaesarKey]
allKeys = catMaybes $ caesarKey <$> [1,3..25] <*> [1,3..25]

bruteForceCaesar :: String -> [String]
bruteForceCaesar str = [encipherString key str | key <- allKeys]
allkey::[CaesarKey]
allKeys=catMaybes$caesarKey[1,3..25][1,3..25]
bruteForceCaesar::String->[String]

布鲁特福斯凯撒街=[EncrypherString key str | key Side note:caesar的东西可能应该放在它自己的模块中。另一个Side note:
Math.NumberTheory.GCD.extendedGCD
Math.NumberTheory.Moduli.invertMod
。另一个Side note:这个数论库在一个名为hackage的包中。另外,你可以从c中创建这个类的实例rypto api。我仔细阅读并遵循了您的建议。这产生了不可思议的简洁易读的代码,并避免了我正在做的许多练习,fmap和家庭。很快,在allKeys声明中,第二个列表实际上应该是[1..25],因为s不需要与26互质。非常感谢,这真的很有帮助!
-- turns a key suitable for encoding into one suitable for decoding, and vice versa.
--   @invert (invert key) = key@
invert :: CaesarKey -> CaesarKey
invert (Key r s q) = Key q ((26-q)*s) r
allKeys :: [CaesarKey]
allKeys = catMaybes $ caesarKey <$> [1,3..25] <*> [1,3..25]

bruteForceCaesar :: String -> [String]
bruteForceCaesar str = [encipherString key str | key <- allKeys]